前言

在 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 问题相关推荐

  1. 【Android Gradle 插件】Extension 扩展类型 ( Module 引入插件类型 | application 插件 | library 插件 | Variants 变体列表 )

    文章目录 一.Module 引入插件类型 1.com.android.application 插件 2.com.android.library 插件 二.Extension 扩展类型 三.applic ...

  2. 【Android Gradle 插件】Module 目录下 build.gradle 配置文件 ( android 闭包块配置 | AppExtension 扩展类型参考文档 )

    文章目录 一.Module 目录下 build.gradle 配置文件 1.android 闭包块配置 2.AppExtension 扩展类型参考文档 Android Plugin DSL Refer ...

  3. linux根目录cdef,关于linux:Cython:从python调用的cdef函数中调用扩展类型cdef方法...

    我正在尝试编写一个Cython模块,该模块可计算成对距离,这是较大类的位置敏感哈希的一部分.我尝试不创建每种类型和每种距离度量的代码,而是尝试创建一个cdef函数,该函数采用从Metric继承的各种扩 ...

  4. MIME (多用途互联网邮件扩展类型)

    百度百科: MIME(Multipurpose Internet Mail Extensions) 多用途互联网邮件扩展类型.是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问 ...

  5. 杂项:MIME(多用途互联网邮件扩展类型)百科

    ylbtech-杂项:MIME(多用途互联网邮件扩展类型)百科 MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型.是设定某种扩展名的文件用 ...

  6. 如何在IIS添加MIME扩展类型

    在iis7中默认的MIME类型并不包含所有的后缀名文件,像现在比较热门的apk,ipa文件都是需要手动添加的. 那如何在IIS添加MIME类型?步骤如下: 1.打开iis7,选择你要设置网站,打开mi ...

  7. ios 输入法扩展_App Extension编程指南(iOS8/OS X v10.10):扩展类型--自定义键盘

    通过使用第三方输入法替换系统原生输入法,用户可以实现一些特殊的功能.比如一个特别新颖的输入方式,或输入iOS原生输入法并不支持的语言.第三方输入法的基本功能很简单:通过点击.手势,或者其他输入事件,然 ...

  8. 换肤实例二,可扩展类型换肤库

    榨干剩余价值,这是第二个实例 步骤与第一个示例类似,如果不清楚请参考:QQ换肤源码实战,一步一步教你操作!! Demo下载 PPT下载 About 网易换肤第一篇:换肤技术解密! 网易换肤第二篇:本地 ...

  9. C#拾遗系列(9):继承、接口、扩展方法、分部类、类操作、Ref and Out、可空类型...

    本文内容: 继承 Equal示例 结构和类 属性 Ref and Out 类操作 扩展方法 接口 可空类型 分部类 1. 继承 using System; using System.Collectio ...

最新文章

  1. 详解zabbix中文版安装部署
  2. 这篇长达165页的论文,用一个里程碑式的证明同时解决了量子物理学和理论数学的难题...
  3. Oracle索引失效问题
  4. redirect和forward
  5. 源代码管理的新15条建议
  6. php7的稳定性,探索PHP7(一)--性能
  7. 文本右上角右下角添加文本
  8. Hangfire使用ApplicationInsigts监控
  9. [.NET跨平台]Jexus独立版本的便利与过程中的一些坑
  10. php cdi_本机CDI限定词:@Any和@Default
  11. python 句子中没有中文_人生感悟经典句子,生活中可以没有诗歌,但不能没有诗意...
  12. vs 之bug集(不断更新中)
  13. python将文字转换为语音_python实现将文本转换成语音
  14. 实现100以内的素数输出(Python与C++对比)
  15. ubuntu下mysql无法启动_升级Ubuntu到10.04后MySQL无法启动
  16. Redis3集群安装
  17. 在Vista系统中,Flash渲染功能无法使用,咋办?
  18. android 隐藏鼠标光标,Android7.1下显示/隐藏鼠标
  19. 红警2 csf文件解析 简体化
  20. docker 自定义网络

热门文章

  1. 第27课:使用时间测量工具挖掘可利用的业余时间(图文篇)
  2. php动态网页学生作品,基于php动态网页技术的办公网站的设计
  3. 均值估计标准差(Standard Deviation) 和 标准误差(Standard Error)
  4. Hexo 主题配置 - Icarus
  5. severlet技术概念 基础
  6. layui里的倒计时
  7. 第二次作业:熟悉使用工具
  8. SqlLite数据库
  9. mysql 转换文本_mysql replace将纯文本数据转换成HTML格式的方法
  10. 1.1 第一课:如何打开Excel 2016软件 [原创Excel教程]