方法和ELF OS ABI中的静态局部变量

Static locals in methods and ELF OS ABI

本文关键字:静态 局部变量 ABI ELF OS 方法      更新时间:2023-10-16

给定程序

$ cat main.cpp 
#ifndef WITH_LOCAL_STATIC
static int y = 0;
#endif
class X {
  public:
    void foo() {
#ifdef WITH_LOCAL_STATIC
        static int y = 0;
#endif
        ++y;
    }
};
int main() {
    X().foo();
    return 0;
}

以两种不同的方式编译:

$ g++ main.cpp -o global
$ g++ main.cpp -DWITH_LOCAL_STATIC -o local

我得到两种不同的二进制格式:

$ file local
local: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x8d8e998601e44115b5fa6c0e71f3ed97cb13a0bd, not stripped
$ file global
global: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x3481ba7c6969ed9d1bd1c8ce0f052d574023d488, not stripped

有人能解释一下为什么我在一种情况下得到ELFOSABI_LINUX,而在另一种情况中得到ELFOSABI_NONE吗?编译器是gcc 4.7.2

背景是,在我的环境中,加载程序拒绝非ELFOSABI_NONE的可执行文件。

GNU/Linux格式取代了System V对象文件格式,您的gcc认为可以运行可执行文件的最低OS/ABI是GNU/Linu克斯。

当程序中的符号类型为STT_GNU_IFUNC(表示间接函数的GNU扩展)时,通常会发生这种情况,而这些符号通常来自glibc。当您引入本地静态变量gcc时,它向翻译单元添加了(更多)代码,以处理其初始化和销毁(按照_ZZZ__static_initialization_and_destruction_iii的行),这可能是glibc的相关部分发挥作用的地方。

首先,你最好遵循这个问题中的建议:如何避免二进制文件中的STT_GNU_IFUNC符号?

第二,我不得不说,在我的盒子上,旧的gcc 4.4和新的clang 3.4都将全局本地二进制文件生成为SYSV标准ELF,所以要么您的测试用例缺少更多相关的部分,要么您可能使用了自定义配置和构建的gcc、自定义链接器或非标准glibc。

你可以寻求更多的途径来调查你是如何在二进制文件中使用这些GNU间接函数的:

  • 在二进制文件上运行nm并识别间接符号,它们应该具有i类型。(见下文。)
  • 生成链接映射和/或程序集输出,并将这些间接符号跟踪到代码的细节
  • 此外,请检查使用ldd -v $(type -p gcc)定位glibc是否指向非标准libc

i-对于PE格式文件,这表示符号位于特定于DLL实现的部分中。对于ELF格式的文件,这表示符号是一个间接函数。这是对ELF符号类型标准集的GNU扩展。它指示一个符号>,如果重定位引用了该符号,则该符号不会计算为其地址,而是必须在运行时调用。然后,运行时执行将返回要在重新定位中使用的值

https://sourceware.org/binutils/docs/binutils/nm.html