STT_GNU_IFUNC 与 libc.so 的 GNU 扩展类型 ABI 问题
前言
在 ELF file OS ABI invalid 与 chroot 大法 这篇文章中,我描述了 libc.so ABI 的特别之处,它的 ABI 为 GNU 扩展格式而非 System V 格式。在本文中研究下它如此特别的原因。
elf.h 中的相关定义
系统头文件路径中,与这个 ABI 相关的宏在 /usr/include/elf.h 文件中定义,
相关代码摘录如下:
138 #define EI_OSABI 7 /* OS ABI identification */139 #define ELFOSABI_NONE 0 /* UNIX System V ABI */140 #define ELFOSABI_SYSV 0 /* Alias. */141 #define ELFOSABI_HPUX 1 /* HP-UX */142 #define ELFOSABI_NETBSD 2 /* NetBSD. */143 #define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */144 #define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */......................
System V ABI 值为 0,UNIX ABI 值为 3,这与 elf 头中的 magic 字段一致。网上搜索了下,搜了半天,终于找到了一些相关的内容,看描述应该是 libc 库中使用了一种名为 STT_GNU_IFUNC 的扩展功能带来的副作用。
STT_GNU_IFUNC 功能
这个 STT_GNU_IFUNC 中的关键词是 IFUNC,这里的 “I” 表示的是间接的意思,IFUNC 表示的是间接的函数调用。
下面是一个简单的代码示例(内容来自互联网并进行了修改):
/* Dispatching via IFUNC ELF Extension */
#include <stddef.h>
#include <stdio.h>extern void foo(unsigned *data, size_t len);void foo_sse42(unsigned *data, size_t len) { printf("%s\n", __FUNCTION__); }
void foo_avx2(unsigned *data, size_t len) { printf("%s\n", __FUNCTION__); }int cpu_has_sse42(void)
{return 0;
}int cpu_has_avx2(void)
{return 1;
}void foo(unsigned *data, size_t len) __attribute__((ifunc ("resolve_foo")));static void *resolve_foo(void)
{if (cpu_has_avx2())return foo_avx2;else if (cpu_has_sse42())return foo_sse42;elsereturn foo_c;
}int main(void)
{foo(NULL, 0);return 0;
}
这个程序模拟了 foo 函数根据不同的指令集分发的过程,实际上 IFUNC 主要的应用就是屏蔽底层不同架构的区别,统一的接口就能够支持不同的架构。
它并不是直接调用每个架构提供的函数,它首先通过一个 resolver 函数来在运行时或第一次被调用的时候获取当前架构提供的 func 函数的指针,并修改 got、plt 表(存疑),然后调用之,由于已经修改了 got、plt 表中的相关表项,后续的调用会直接完成,不会再使用 resolver 函数。
上述代码中对 foo 函数的 attribute 修饰中将 resolve_foo resolver 函数绑定到 foo 函数上。
编译此程序后查看 elf 头,有如下记录:
[longyu@debian-10:22:52:50] program-problem $ gcc ./ifunc_test.c
[longyu@debian-10:22:52:54] program-problem $ readelf -h ./a.out
ELF 头:Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 类别: ELF64数据: 2 补码,小端序 (little endian)版本: 1 (current)OS/ABI: UNIX - GNUABI 版本: 0类型: DYN (共享目标文件)系统架构: Advanced Micro Devices X86-64版本: 0x1入口点地址: 0x1060程序头起点: 64 (bytes into file)Start of section headers: 15056 (bytes into file)标志: 0x0本头的大小: 64 (字节)程序头大小: 56 (字节)Number of program headers: 11节头大小: 64 (字节)节头数量: 30字符串表索引节头: 29
可以看到编译出的 a.out 可执行文件的 elf 头中的 ABI 也是 GNU 格式,与 libc.so 中的相同。
glibc 中对 IFUNC 扩展功能的使用
不同的架构,其 memcpy、memset、strcmp、strcpy 等函数针对不同的架构有不同的优化实现,glibc 中就使用了 IFUNC 来在运行时确定当前架构的这些函数。
使用 readelf -s 查看 libc.so 中的符号信息,能够搜索到如下与 IFUNC 相关的内容:
[longyu@debian-10:22:01:01] program-problem $ readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep 'IFUNC'31: 00000000000b5c30 183 IFUNC GLOBAL DEFAULT 13 __gettimeofday@@GLIBC_2.2.571: 00000000000a32e0 71 IFUNC WEAK DEFAULT 13 wmemset@@GLIBC_2.2.598: 0000000000087c40 46 IFUNC GLOBAL DEFAULT 13 strcpy@@GLIBC_2.2.5109: 000000000008a890 42 IFUNC GLOBAL DEFAULT 13 __rawmemchr@@GLIBC_2.2.5122: 00000000000a3260 93 IFUNC GLOBAL DEFAULT 13 wmemcmp@@GLIBC_2.2.5186: 0000000000088100 81 IFUNC GLOBAL DEFAULT 13 strncmp@@GLIBC_2.2.5233: 0000000000088190 42 IFUNC GLOBAL DEFAULT 13 strrchr@@GLIBC_2.2.5327: 0000000000089220 46 IFUNC WEAK DEFAULT 13 stpncpy@@GLIBC_2.2.5391: 0000000000107af0 194 IFUNC GLOBAL DEFAULT 13 __mempcpy_chk@@GLIBC_2.3.4421: 0000000000088160 46 IFUNC GLOBAL DEFAULT 13 strncpy@@GLIBC_2.2.5463: 00000000000b5b40 183 IFUNC GLOBAL DEFAULT 13 time@@GLIBC_2.2.5558: 0000000000088ee0 42 IFUNC GLOBAL DEFAULT 13 memchr@@GLIBC_2.2.5............
可以看到这里的函数除了上面提到的 memcpy 等等,还多了一些函数,正是因为这些函数使用了 IFUNC 功能,libc.so 的 ABI 成为了 GNU 格式,而非标准的 System V 格式。
如何确定当前编译环境是否支持 IFUNC?
glibc 中的 configure 文件使用如下代码确定系统是否支持 IFUNC。
cat > conftest.c <<EOF
extern int func (int);
int used_func (int a)
{return a;
}
static void *resolver ()
{return &used_func;
}
extern __typeof (func) func __attribute__ ((ifunc ("resolver")));
EOF
libc_cv_gcc_indirect_function=no
if ${CC-cc} -c conftest.c -o conftest.o 1>&5 \2>&5 ; thenif $READELF -s conftest.o | grep IFUNC >/dev/null 2>&5; thenlibc_cv_gcc_indirect_function=yesfi
fi
rm -f conftest*
在支持 IFUNC 的系统,readelf 查看编译生成的 conftest.o 的 symbols 将能够看到与 IFUNC 相关的内容,这就是上述过程依赖的原理。
IFUNC 从什么版本开始支持?
从参考链接的某篇文章中找到了与这个问题相关的信息,摘录如下:
The “GNU indirect function” feature requires support in the assembler, linker and loader, which is found in Binutils version 2.20 and later.
这里说明在 Binutils 2.20 之后的版本才支持这个扩展功能,由于我在网上没有找到 gnu 的官方说明,暂且就认为就是这个版本吧!
参考链接
https://jasoncc.github.io/gnu_gcc_glibc/gnu-ifunc.html
https://www.evanjones.ca/portable-linux-binaries.html
https://stackoverflow.com/questions/2764533/how-do-i-compile-on-linux-to-share-with-all-distributions
http://www.sco.com/developers/gabi/latest/contents.html
https://sites.google.com/site/x32abi/documents
STT_GNU_IFUNC 与 libc.so 的 GNU 扩展类型 ABI 问题相关推荐
- 【Android Gradle 插件】Extension 扩展类型 ( Module 引入插件类型 | application 插件 | library 插件 | Variants 变体列表 )
文章目录 一.Module 引入插件类型 1.com.android.application 插件 2.com.android.library 插件 二.Extension 扩展类型 三.applic ...
- 【Android Gradle 插件】Module 目录下 build.gradle 配置文件 ( android 闭包块配置 | AppExtension 扩展类型参考文档 )
文章目录 一.Module 目录下 build.gradle 配置文件 1.android 闭包块配置 2.AppExtension 扩展类型参考文档 Android Plugin DSL Refer ...
- linux根目录cdef,关于linux:Cython:从python调用的cdef函数中调用扩展类型cdef方法...
我正在尝试编写一个Cython模块,该模块可计算成对距离,这是较大类的位置敏感哈希的一部分.我尝试不创建每种类型和每种距离度量的代码,而是尝试创建一个cdef函数,该函数采用从Metric继承的各种扩 ...
- MIME (多用途互联网邮件扩展类型)
百度百科: MIME(Multipurpose Internet Mail Extensions) 多用途互联网邮件扩展类型.是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问 ...
- 杂项:MIME(多用途互联网邮件扩展类型)百科
ylbtech-杂项:MIME(多用途互联网邮件扩展类型)百科 MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型.是设定某种扩展名的文件用 ...
- 如何在IIS添加MIME扩展类型
在iis7中默认的MIME类型并不包含所有的后缀名文件,像现在比较热门的apk,ipa文件都是需要手动添加的. 那如何在IIS添加MIME类型?步骤如下: 1.打开iis7,选择你要设置网站,打开mi ...
- ios 输入法扩展_App Extension编程指南(iOS8/OS X v10.10):扩展类型--自定义键盘
通过使用第三方输入法替换系统原生输入法,用户可以实现一些特殊的功能.比如一个特别新颖的输入方式,或输入iOS原生输入法并不支持的语言.第三方输入法的基本功能很简单:通过点击.手势,或者其他输入事件,然 ...
- 换肤实例二,可扩展类型换肤库
榨干剩余价值,这是第二个实例 步骤与第一个示例类似,如果不清楚请参考:QQ换肤源码实战,一步一步教你操作!! Demo下载 PPT下载 About 网易换肤第一篇:换肤技术解密! 网易换肤第二篇:本地 ...
- C#拾遗系列(9):继承、接口、扩展方法、分部类、类操作、Ref and Out、可空类型...
本文内容: 继承 Equal示例 结构和类 属性 Ref and Out 类操作 扩展方法 接口 可空类型 分部类 1. 继承 using System; using System.Collectio ...
最新文章
- 详解zabbix中文版安装部署
- 这篇长达165页的论文,用一个里程碑式的证明同时解决了量子物理学和理论数学的难题...
- Oracle索引失效问题
- redirect和forward
- 源代码管理的新15条建议
- php7的稳定性,探索PHP7(一)--性能
- 文本右上角右下角添加文本
- Hangfire使用ApplicationInsigts监控
- [.NET跨平台]Jexus独立版本的便利与过程中的一些坑
- php cdi_本机CDI限定词:@Any和@Default
- python 句子中没有中文_人生感悟经典句子,生活中可以没有诗歌,但不能没有诗意...
- vs 之bug集(不断更新中)
- python将文字转换为语音_python实现将文本转换成语音
- 实现100以内的素数输出(Python与C++对比)
- ubuntu下mysql无法启动_升级Ubuntu到10.04后MySQL无法启动
- Redis3集群安装
- 在Vista系统中,Flash渲染功能无法使用,咋办?
- android 隐藏鼠标光标,Android7.1下显示/隐藏鼠标
- 红警2 csf文件解析 简体化
- docker 自定义网络
热门文章
- 第27课:使用时间测量工具挖掘可利用的业余时间(图文篇)
- php动态网页学生作品,基于php动态网页技术的办公网站的设计
- 均值估计标准差(Standard Deviation) 和 标准误差(Standard Error)
- Hexo 主题配置 - Icarus
- severlet技术概念 基础
- layui里的倒计时
- 第二次作业:熟悉使用工具
- SqlLite数据库
- mysql 转换文本_mysql replace将纯文本数据转换成HTML格式的方法
- 1.1 第一课:如何打开Excel 2016软件 [原创Excel教程]