libc_hidden_def、libc_hidden_weak、libc_hidden_proto

2023-05-16

libc_hidden_def、libc_hidden_weak、libc_hidden_proto

在阅读glibc源码的时候,遇见了几个没见过的宏,几乎所有的函数都会使用这几个宏:libc_hidden_def、libc_hidden_weak、libc_hidden_proto

因为我比较好奇,所以特地去找了一下有关这些宏的定义(主要也是想多学一点)

Linux下学习源码,最方便的地方就是它的各种命令
使用find ./ | grep -r "define libc_hidden_def",发现在include/libc-symbols.h中定义了

进入该文件
一进去就是一行注释:

/* This file is included implicitly in the compilation of every source file,
   using -include.  It includes config.h.  */

这个文件被隐含地包括在了所有源文件地编译中

下面紧跟着这几个宏:

/* Use `#if IS_IN (module)` to detect what component is being compiled.  */
#define PASTE_NAME1(a,b) a##b
#define PASTE_NAME(a,b)  PASTE_NAME1 (a,b)
#define IN_MODULE        PASTE_NAME (MODULE_, MODULE_NAME)
#define IS_IN(lib)       (IN_MODULE == MODULE_##lib)

主要是为了定义IS_IN(lib)这个宏
看名字就可以看出来,这个宏的意思是判断当前的模块

上面这些倒不是重点,往下面再翻翻,就看到了一段有一点意思的东西

#ifndef __ASSEMBLER__
/* GCC understands weak symbols and aliases; use its interface where
   possible, instead of embedded assembly language.  */

这部分的描述挺重要的(自己觉得是这样子的)

456行开始有一个长注释,介绍了这几个宏:

/* The following macros are used for PLT bypassing within libc.so
 457    (and if needed other libraries similarly).*/

主要内容如下:

首先,你在头文件foo.h中有一个函数原性,例如

int foo(int __bar);

如果对foo函数的调用应当始终指向libc.so(即foo这个函数只能是libc.so里面的函数),则应当在头文件foo.h中使用该宏:

libc_hidden_proto (foo)

然后在这个函数的定义,例如foo.c,后面加入

libc_hidden_def (foo)

libc_hidden_weak (foo)

如果foo只是其他函数的一个别名,则应当在函数的定义后,先使用strong_alias或者weak_alias,然后再使用libc_hidden_deflibc_hidden_weak

如果该函数在多个静态库中出现,则应当这么使用:

# if IS_IN(libc) || IS_IN(xxxx)
hidden_proto(foo)
#endif

下面的宏定义分出了两条分支:汇编和非汇编

一般来说,使用GCC进行编译,能利用GCC的特性做一些事情,因此,这里的注释推荐使用非汇编的路径

#ifndef __ASSEMBLER__

首先看libc_hidden_proto
它在622行:

#define libc_hidden_proto(name, attrs...) hidden_proto (name, ##attrs)

它是定义在hidden_proto这个宏上的,我们继续看这个宏

#define hidden_proto(name, attrs...) \
  __hidden_proto (name, , __GI_##name, ##attrs)

它有两个参数,参数一是name,参数二是attr,定义在__hodden_proto这个宏上,而这个宏的定义是:

#define __hidden_proto(name, thread, internal, attrs...)             \
  extern thread __typeof (name) name __asm__ (__hidden_asmname (#internal)) \
  __hidden_proto_hiddenattr (attrs);

它的参数有这几个:name、thread、internal、attrs

首先,它声明了一个全局变量或函数,然后,它涉及了这两个东西:

  1. thread
  2. __asm__(__hidden_asmname)
  3. internal

https://blog.csdn.net/xj178926426/article/details/54345449

__thread变量每一个线程有一份独立实体,各个线程的值互不干扰。
即线程的“私有变量”

显然,hidden_proto的这一个变量放空了,而下面还有一个hidden_tls_proto

#define __hidden_proto(name, thread, internal, attrs...)             \
  extern thread __typeof (name) name __asm__ (__hidden_asmname (#internal)) \
  __hidden_proto_hiddenattr (attrs);

两个的区别就是这个字段是否放空,即是否是线程私有变量


接下来要深挖的是__hidden_asmname
它看起来不像是GCC自己的东西

果然,在下面马上就有定义:

#define __hidden_asmname(name) \
  __hidden_asmname1 (__USER_LABEL_PREFIX__, name)

继续追查__hidden_asmname1

#define __hidden_asmname1(prefix, name) __hidden_asmname2(prefix, name)

它指向了__hidden_asmname2

#define __hidden_asmname1(prefix, name) __hidden_asmname2(prefix, name)

这个宏只是简单地把prefix粘贴到name前面

__hidden_asmname则是将__USER_LABEL_PREFIX__粘贴到name

这个宏是GNU标准下的默认宏

https://blog.csdn.net/qq_22237829/article/details/73872781

不知道为什么,这个宏的定义是空的,就是什么都没有,我没有找到其他的解释和资料

展开hidden_proto,它的前半部分大概就是

extern __typeof(name) name \
__asm__(__hidden_asmname(__GI_name))

extern __typeof(name) name \
__asm__(__GI_name)

因为__USER_LABEL_PREFIX__是空的,所以__hidden_asmname这个宏其实没有起作用

把这一段复制到一个新的文件里面,进行预处理后

hidden_proto(foo)

这一段变成了

extern __typeof (foo) foo __asm__ ("" "__GI_foo") __attribute__ ((visibility ("hidden")));

而后面的__attribute__((visibility("hidden")))则在strong_alias、weak_alias中说明了:对于链接该库的程序来说,该函数就是不可见的,除非强制声明
即,该接口是不对外暴露的,只是内部使用的

这一段话里面,对我来说,最困惑难懂的就是__asm__那一段
我不知道內联汇和在编里面放一个名字的意义何在

在下面有一段注释,里面有一句话说:

/*
hidden_proto doesn't make sense for assembly but the equivalent
   is to call via the HIDDEN_JUMPTARGET macro instead of JUMPTARGET.
*/

其中,HIDDEN_JUMPTAEGET的定义是:

#define HIDDEN_JUMPTARGET(name) __GI_##name

就是简单地在名字前面加了一个前缀

因此,大致可以猜测一下__asm__在这里的用处:起一个别名

在文件中这么写:

int foo() __asm__("__PREFIX_foo");

则使用nm查看编译出来的ELF文件,发现foo被替换为了__PREFIX_foo

0000000000001129 T __PREFIX_foo

结合下面的注释,则可以明确该宏的作用:给函数加一个__GI_的前缀。通过这个重命名的把戏,即使别的模块定义了和GLIB中函数重名的函数,GLIB也能保证不访问别人的函数,而是只访问自己的函数

在汇编GCC中,函数由本名进行定义,因此,我们需要给它加一个别名;
在C中,函数使用__GI_前缀,我们需要将别名添加到实名中


同样,查看libc_hidden_def

#define hidden_def(name)                __hidden_ver1(__GI_##name, name, name);

继续追踪

#define __hidden_ver1(local, internal, name) \
  __hidden_ver2 (, local, internal, name)

同样,它有三个参数:local、internal、name

#define __hidden_ver2(thread, local, internal, name)                    \
  extern thread __typeof (name) __EI_##name \
    __asm__(__hidden_asmname (#internal));  \
  extern thread __typeof (name) __EI_##name \
    __attribute__((alias (__hidden_asmname (#local))))  \
    __attribute_copy__ (name)

因为比较复杂,不好懂,我们像刚刚一样,复制到C文件里,预处理后一窥究竟:

extern __typeof(foo) __EI_foo __asm__("foo");
extern __typeof(foo) __EI_foo __attribute__((alias("__GI_foo"))) __attribute_copy__(foo);

第一句,声明了一个全局的函数__EI_foo,它的别名是foo

第二句,__EI_foo变成了__GI_foo的别名

那么,访问foo,其实就是在访问__GI_foo


libc_hidden_protolibc_hidden_def合作,到底做了什么事情呢?

如果只使用了libc_hidden_proto,则

int foo();
extern __typeof (foo) foo __asm__ ("" "__GI_foo") __attribute__ ((visibility ("hidden")));

int foo()
{}

只是声明了一个名为__GI_foo的全局函数,它的真身是foo,但是在符号表上却找不到foo,只有__GI_foo,还是隐藏的:

$ readelf -s a.out | grep foo
    57: 0000000000001129    11 FUNC    GLOBAL HIDDEN    14 __GI_foo

同时使用了libc_hidden_def之后,

int foo();
extern __typeof (foo) foo __asm__ ("" "__GI_foo") __attribute__ ((visibility ("hidden")));
extern __typeof (foo) __EI_foo __asm__("" "foo"); extern __typeof (foo) __EI_foo __attribute__((alias ("" "__GI_foo"))) ;
int foo()
{}

libc_hidden_proto的基础上,它声明了一个名字是foo的全局变量,这个foo的真身是__EI_foo
下一句马上就说了,__EI_foo只不过是__GI_foo的一个别名

那么,所有对foo的访问,都是在访问__EI_foo,而对__EI_foo的访问,都是在访问__GI_foo,而__GI_foo就是foo本身

绕这么大弯子,最终的目的就是,保证所有对foo的访问最终跑到这个库里

lib_hidden_weak,只是在lib_hidden_def的基础上,增加了一个__attribute__((weak))
即将其做为弱符号处理

弱符号,即用户可以自己定义一个同名的函数

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

libc_hidden_def、libc_hidden_weak、libc_hidden_proto 的相关文章

  • send_keys 到隐藏元素

    我想将文本发送到页面的文本框 这是页面上的隐藏元素
  • 如何防止 getopt 与缺少参数的选项混淆?

    说 我有代码 while c getopt argc argv n p 1 switch c case n syslog LOG NOTICE n s optarg break case p some code break case han
  • GCC 对弱 const 变量的优化错误

    我有一种奇怪的感觉gcc处理事情时的行为弱常量变量在不同的优化级别 即 O0 or O1 这是代码 def h 声明 const int var int copy int do copy void weak c weak var定义 do
  • 从套接字 fd 获取 IP 地址、端口和连接类型

    我有一个跟踪网络应用程序的跟踪程序 给定一个属于被跟踪者的套接字文件描述符 跟踪器是否有可能找到它对应的IP 对于服务器来说是它绑定到的接口的IP 对于客户端来说是被绑定接口的地址 用于建立连接的接口 端口号和连接类型 是否有 libc A
  • 带有负秒的奇怪 mktime 逻辑

    我一直在使用 mktime localtime 进行时间管理 包括一些关于日期 时间的繁重算术 当向 mktime 提供包含负值的 struct tm 时 我注意到一些非常奇怪的事情 采取下面的代码 2013 年 11 月 3 日 洛杉矶的
  • 隐藏用户输入

    我正在尝试获取一个既可以在本机 Windows shell 中也可以在 cygwin shell 通过 ssh 中运行的脚本 该脚本会提示并读取用户输入的密码 到目前为止 我已经尝试过以下方法 using Term ReadKey and
  • 存在隐藏输入类型时不返回 POST 数据

    我认为我的代码中有错误 或者我的 PHP 或 Apache 设置不正确 当我提交包含隐藏字段的表单时 我的 POST 数组中没有收到任何数据 当我注释掉代码中的隐藏字段时 POST 数据会正确返回 HTML表格
  • C 中 ptrdiff_t 在哪里定义?

    哪里ptrdiff t在C中定义 它定义在stddef h 该标头定义了整数类型size t ptrdiff t and wchar t 函数宏offsetof 以及常数宏NULL
  • 是否有链接器标志强制它在启动时加载所有共享库?

    是否有一个标志或任何其他指令可以用来强制使用 Linux 动态链接器ld so在程序启动时立即加载所有共享库 而不是惰性绑定 https unix stackexchange com questions 55846 elf shared l
  • 在 proto 中从父文件夹导入到子文件夹

    我正在尝试将原型 defn 从父原型导入到具有以下文件夹结构的子原型中 parent proto sub child proto 父级 proto message Attribute 子原型 import parent proto mess
  • 关于Android NDK libc++ libc++_shared、libstdc++的困惑

    我在尝试使用 Android NDK 23 23 1 7779620 构建一个简单的 C 库时感到非常困惑 我正在使用 CMake 这是一个非常简单的程序 CMakeLists txt cmake minimum required VERS
  • 使用隐藏的div缓存数据

    我试图通过执行以下操作来加快我的 ajax Web 应用程序的响应时间 假设用户请求一个内容不会更改的页面 例如网络表单 当用户发出不同的请求时 我通过将表单放入隐藏的 div 中来 缓存 表单 在显示新信息之前 因此 表单基本上仍然加载在
  • 为什么从管道读取时 libc++ getline 会阻塞,而 libstdc++ getline 不会?

    TL DR 一个程序使用libc 的版本getline当函数从管道读取输入时 它将阻塞 直到管道的缓冲区已满 相同的是NOT为真libstdc 的版本getline函数 这里 一旦输入行可用 该函数就会立即读取并返回一行输入 我是否应该预料
  • C++ 错误:“内联”只能出现在函数上

    我正在尝试编译VCMI http forum vcmi eu portal php在 OS X 10 7 下使用 clang 我配置了项目CXX clang 因为苹果的 gcc 似乎不识别 required std c 0x flag 我已
  • xlocale 在 OS X 上损坏?

    我有一个简单的程序 它使用命令行上传递给它的一系列语言环境来测试 wchar t 和 char 之间的转换 它通过打印区域设置名称和转换失败的字符串来输出失败的转换列表 我正在使用 clang 和 libc 构建它 我的理解是 libc 的
  • rand() 不返回随机值

    它是一个生成随机埃尔米特矩阵的小代码埃尔米特矩阵 http en wikipedia org wiki Hermitian matrix 我在每次调用 rand 之前都调用了 srand 但输出仍然没有随机性 我使用 c99 的复杂数据类型
  • OSX 10.9.5 上 libc 中的 wordexp 是否存在泄漏?

    Is wordexp https developer apple com library prerelease mac documentation Darwin Reference ManPages man3 wordexp 3 html
  • 如何将 Google proto 时间戳转换为 Java LocalDate?

    我们需要将 Google Protobuf 时间戳转换为正常日期 在这种情况下 有没有办法将 Google Proto 缓冲区时间戳转换为 JavaLocalDate直接地 tl dr 作为 UTC 时刻 转换为java time Inst
  • 值类型不完整的映射

    我收到以下错误 class Test std map
  • 为什么 (jQuery) Waypoints“视图底部”不适用于隐藏元素?

    我试图使用 jQuery 和 Waypoints 以前称为 jQuery Waypoints 在用户向下滚动时动态显示隐藏的图像 我发现我可以很容易地向元素添加一个路径点 并在元素 在视图中 时触发处理程序 偏移属性设置在视图底部 但是 尝

随机推荐

  • 如何实现广告精准投放?一文获得新思路

    随着互联网人口红利的持续衰减 xff0c 互联网用户数量的增长速度越来越慢 市场进入存量 xff0c 用户们对产品质量要求越来越高 面对这样的市场阶段 xff0c APP开发者们做好广告精准投放是很有必要的 精准地广告投放在减少广告预算浪费
  • 激励视频广告——移动应用的财富密钥

    如何良好地平衡用户体验和用户增长 是广告行业的持久命题 xff0c 上网搜索 激励视频广告 你会发现类似的问题层出不穷 xff1a 请问什么是激励视频广告 xff1f 谁能麻烦介绍一下吗 xff1f 激励视频广告哪家做的好 xff1f 跪求
  • 设计模式详解:模式汇总与索引清单

    从本篇开始 xff0c 和您一起进入设计模式的世界 之前用C 做微信微信公众号开发系列文章 xff0c 更多的是原生模式 xff0c 帮助猿友们理解业务流程和基本实现方法 xff0c 但是那些类的实现仍然是用面向过程的思维方式 xff0c
  • 如何稳步实现互联网流量变现?

    我突然想起了自己刚做开发的时候 xff0c 那会还是菜鸟的我为了快速获取流量 最大程度的变现 xff0c 基本上不会考虑所谓的用户体验 xff0c 满脑子都是怎么引流 怎么变现 xff0c 所以常常引起用户反感 xff0c 严重折损了用户体
  • 一文获悉互动广告的投放攻略

    一直以来 xff0c 顶着 第四代互联网广告 头衔的 互动广告 通过互动 交流等方式进行双向传播 xff0c 一改以往传统广告的单向线性传播 xff0c 与此同时 xff0c 用户也从被动的观看者转变为主动的参与者 xff0c 直观地体验产
  • 穿山甲成长中心——人能尽其才则百事兴

    对于一众APP开发者来说 xff0c 要想在激烈的市场竞争中突出重围 xff0c 得到用户的青睐 xff0c 往往要面临重重困难 缺乏广告资源 xff0c 流量变现困难等着问题都使得开发者在刚刚进入市场时没有机会展现自己从而停滞不前 每一个
  • 广告精准投放的新出路为何?

    众所周知 xff0c 当前广告行业呈现普遍性的跨媒体投放 xff0c 测试成本 管理成本较高 xff0c 存在信息孤岛等情况 xff0c 一众广告主们通过数据分析优化投放的难度较大 在各类引擎中搜索 广告精准投放 xff0c 诸如 如何以更
  • 互联网流量变现出路为何?一文浅析

    国庆小长假在各大广告主和开发者们眼里就是一座巨大的流量富矿 xff0c 以国庆为主题推的广告和软件层出不穷 我突然想起了我自己刚做开发的时候 xff0c 那会还是菜鸟的我为了快速获取流量 最大程度的变现 xff0c 基本上不会考虑所谓的用户
  • MyBatisPlus 入门学习笔记(版本:3.4.3)

    文章目录 学习辅助资料 MyBatisPlus概述1 MyBatisPlus是什么2 特性 快速开始1 创建数据库 96 mybatis plus 96 2 导入相关依赖3 数据源配置3 快速开始3 1 User实体类编写3 2 mappe
  • Shell自动化脚本学习

    目录 xff08 1 6 xff09 Linux Shell脚本的自动化编程之shell xff1a 命令排序 xff08 1 7 xff09 Linux Shell脚本的自动化编程之shell xff1a 通配符 xff08 2 1 xf
  • ROS创建工作空间及功能包流程总结整理(python)

    ROS创建工作空间及功能包流程总结整理 xff08 python xff09 参考资料 xff1a B站赵虚左 xff1a https www bilibili com video BV1Ci4y1L7ZZ p 61 19 amp vd s
  • 中序计算式的计算器模型C语言实现

    span class token macro property span class token directive keyword include span span class token string lt stdio h gt sp
  • 设计模式详解:面向对象设计的七大原则

    单一职责原则 xff1a 一个对象应该只包含单一的职责 xff0c 并且该职责被完整地封装在一个类中 Single Responsibility Principle SRP Every object should have a single
  • 排序算法——猴子排序

    猴子排序 让一群猴子在打印机前昼夜不停地敲打键盘 xff0c 最终有可能能输入一部莎士比亚作品集 尽管概论微乎其微 同理 xff0c 把一堆扑克牌扔到天上 xff0c 等它们落下来的时候有概率会刚刚好从小到大排成一列 现在有一个无序的数组
  • ubuntu下firefox使用HTML 5播放器看B站

    ubuntu下FireFox使用HTML 5播放器看B站 firefox使用flash是真的难顶 xff0c 一直闪白 发现bilibili其实可以使用html 5播放器 使用 span class token function sudo
  • Ubuntu下将rm命令替换为trash命令

    Ubuntu下将rm命令替换为trash命令 rm命令是一个很可怕的命令 xff0c 因为它不会给你后悔的机会 xff0c 删了就是删了 xff0c 再也找不回来了 xff08 据说能在lost 43 found里面恢复 xff0c 但是操
  • 正则表达式_排除特定字符/字符串

    正则表达式 排除特定字符 字符串 使用场景 xff1a 使用git add A指令提交一个文件夹中所有的代码文件 xff0c 忽略所有的可执行文件 抽象化 匹配一些字符串 xff0c 找出其中不含后缀 xff0c 即 的字符串 理解 排除特
  • 什么是操作系统?操作系统的定义、功能、特性

    什么是操作系统 xff1f 操作系统的定义 功能 特性 什么是操作系统 xff1f 首先 xff0c 计算机的资源可以分为硬件资源和软件资源 CPU 存储设备 各种类型的输入输出设备与外设等 xff0c 共同构成计算机的硬件资源 各种程序
  • 计算机网络第六课

    计算机网络第六课 奈式准则未考虑噪声 噪声 xff1a 模拟信号 gt 数字信号转换 信道复用技术 xff08 续 xff09 在单一物理通信线路上 xff0c 传输若干个独立的信号 三种信道复用技术 xff1a FDMTDMWDM TDM
  • libc_hidden_def、libc_hidden_weak、libc_hidden_proto

    libc hidden def libc hidden weak libc hidden proto 在阅读glibc源码的时候 xff0c 遇见了几个没见过的宏 xff0c 几乎所有的函数都会使用这几个宏 xff1a libc hidde