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
部分)。
我知道在 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
部分)。