GCC 如何在 MinGW 上实现 __attribute__((constructor))?

How does GCC implement __attribute__((constructor)) on MinGW?

我知道在 ELF 平台上,__attribute__((constructor)) 使用 .ctors ELF 部分。现在我意识到函数属性也适用于 MinGW 上的 GCC,我想知道它是如何实现的。

对于 MinGW 目标(以及其他 COFF 目标,如 Cygwin),编译器仅在 .ctors COFF 部分发出每个构造函数地址:

$ cat c1.c
void c1() {
}
$ x86_64-w64-mingw32-gcc -c c1.c
$ objdump -x c1.o | grep ctors
# nothing
$ cat c1.c
__attribute__((constructor)) void c1() {
}
$ x86_64-w64-mingw32-gcc -c c1.c
$  objdump -x c1.o | grep ctors
  5 .ctors        00000008  0000000000000000  0000000000000000  00000150  2**3

然后配置 GNU ld 链接器(用于 MinGW 目标)(通过其默认链接器脚本)将这些部分组合成常规 .text 部分,其中 __CTOR_LIST__ 符号指向第一项,并且具有最后一项以零结尾。 (可能 .rdata 部分会更清楚,因为这些只是函数的地址,而不是 CPU 指令,但出于某种原因,使用了 .text。事实上,LLVM LLD 链接器针对 MinGW places them in .rdata.)

LD 链接器:

$ x86_64-w64-mingw32-ld --verbose
...
.text ... {
  ...
  __CTOR_LIST__ = .;   
  LONG (-1); LONG (-1);
  KEEP (*(.ctors));
  KEEP (*(.ctor));
  KEEP (*(SORT_BY_NAME(.ctors.*)));
  LONG (0); LONG (0);
  ...
...
}

然后由 C 运行time 库在初始化期间通过使用此 __CTOR_LIST__ 符号来 运行 这些构造函数。

mingw-w64 C 运行时间:

extern func_ptr __CTOR_LIST__[];

void __do_global_ctors (void)
{
  // finds the last (zero terminated) item
  ...    
  // then runs from last to first:
  for (i = nptrs; i >= 1; i--)
  {
    __CTOR_LIST__[i] ();
  }
  ...
}

(另外,在Cygwin runtime中也很相似)

这也可以在调试器中看到:

$ echo $MSYSTEM
MINGW64

$ cat c11.c
#include <stdio.h>

__attribute__((constructor))
void i1() {
  puts("i 1");
}

int main() {
  puts("main");
  return 0;
}
    
$ gcc c11.c -o c11
    
$ gdb ./c11.exe    
(gdb) b i1    
(gdb) r    
(gdb) bt
#0  0x00007ff603591548 in i1 ()
#1  0x00007ff6035915f2 in __do_global_ctors () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:44
#2  0x00007ff60359164f in __main () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:58
#3  0x00007ff60359139b in __tmainCRTStartup () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:313
#4  0x00007ff6035914f6 in mainCRTStartup () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:202
(gdb)

请注意,在某些环境中(不是 MinGW,也不是 Linux),它是 GCC 的责任(它的编译器 运行time libgcc,更具体地说,它的静态部分称为 crtbegin.o和 crtend.o) 而不是 C 运行 时间到 运行 这些构造函数。

此外,为了比较,在 ELF 目标(如 Linux)上,GCC 编译器使用了与上述 MinGW 类似的机制(它使用 ELF .ctors 部分,尽管其余部分有点不同),但自从 GCC 4.7(2012 年发布)以来,它使用的机制略有不同(ELF .init_array 部分)。