在 Linux 上动态链接到 libc 时调用 atexit
Call `atexit` when linking to libc dynamically on Linux
如果我有以下用 C 编写的程序(在 Debian 8.7 上用 GCC 编译),我可以像您期望的那样调用 atexit()
:
#include <stdlib.h>
void exit_handler(void) {
return;
}
int main () {
atexit(exit_handler);
return 0;
}
当我编译并 运行 它时:
$ gcc test.c
$ ./a.out
如您所料,不输出任何内容。事实上,当我 运行 ldd
,我得到:
$ ldd a.out
linux-vdso.so.1 (0x00007fffbe592000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe07d3a8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe07d753000)
但是libc
好像没有atexit
的符号,amd只有__cxa_atexit
和__cxa_threaded_atexit_impl
:
$ nm --dynamic /lib/x86_64-linux-gnu/libc.so.6 | grep 'atexit'
0000000000037d90 T __cxa_atexit
0000000000037fa0 T __cxa_thread_atexit_impl
正如你所期望的那样,如果我尝试动态地从 link 到 libc
,我实际上不能调用 atexit()
,例如在下面的 Racket 程序中 links 到 libc
并尝试查找 atexit
:
#lang racket
(require ffi/unsafe)
(get-ffi-obj 'atexit (ffi-lib "libc" '("6")) (_fun (_fun -> _void) -> _int))
给出输出:
$ racket findatexit.rkt
ffi-obj: couldn't get "atexit" from "libc.so.6" (/lib/x86_64-linux-gnu/libc.so.6: undefined symbol: atexit)
这里我想知道的是:
- 如果
libc
在 Linux 上没有 atexit
的任何符号,为什么我仍然可以从 C 程序中调用它?
- 有什么方法可以在 Linux 上动态调用
atexit
或类似函数吗?
(我应该注意到 atexit
确实是 OS X 上的一个符号,所以它只是 Linux 在这里看起来不寻常。)
编辑:
在@Jonathan的建议下,我也运行:
$ gcc -c test.c
$ nm test.o
U atexit
0000000000000000 T exit_handler
0000000000000007 T main
这似乎表明 atexit
符号在某处,但它没有出现在 ldd
显示的任何库中。
我在 Centos 7 虚拟机上进行了一些探索,我想我找到了它 — 但它并不明显!
找到了!
在/usr/lib64/libc_nonshared.a
中:
$ nm /usr/lib64/libc_nonshared.a | grep -i atexit
atexit.oS:
0000000000000000 T atexit
U __cxa_atexit
$
为什么要看那个图书馆?好问题——说来话长。你坐得舒服吗?那我就开始了……
到达那里所采取的步骤
- 使用问题中的
test.c
代码。
用gcc -v test.c
编译:
$ gcc -v test.c
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v test.c -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase test -version -o /tmp/ccPHTer7.s
GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-11) (x86_64-redhat-linux)
compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-11), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=96 --param ggc-min-heapsize=124992
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include
/usr/local/include
/usr/include
End of search list.
GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-11) (x86_64-redhat-linux)
compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-11), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=96 --param ggc-min-heapsize=124992
Compiler executable checksum: 356f86e67978d665416e07d560c8ba0d
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
as -v --64 -o /tmp/cc5WHEA4.o /tmp/ccPHTer7.s
GNU assembler version 2.25.1 (x86_64-redhat-linux) using BFD version version 2.25.1-22.base.el7
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/cc5WHEA4.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o
$
有趣的部分是末尾的collect2
命令行。每行写一个参数,即:
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2
--build-id
--no-add-needed
--eh-frame-hdr
--hash-style=gnu
-m
elf_x86_64
-dynamic-linker
/lib64/ld-linux-x86-64.so.2
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o
-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5
-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64
-L/lib/../lib64
-L/usr/lib/../lib64
-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../..
/tmp/cc5WHEA4.o
-lgcc
--as-needed
-lgcc_s
--no-as-needed
-lc
-lgcc
--as-needed
-lgcc_s
--no-as-needed
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o
因此,有一堆 cr*.o
文件,外加三个库:-lc
、-lgcc
和 -lgcc_s
要查找,以及要查看的一堆目录:
-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5
、-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64
、-L/lib/../lib64
、-L/usr/lib/../lib64
、-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../..
。 /tmp/cc5WHEA4.o
是从 test.c
.
创建的目标文件
对路径名应用一些清理代码,然后使用 ls
帮助查找库生成一个文件列表以进一步检查:
/lib64/ld-linux-x86-64.so.2
/usr/lib64/crt1.o
/usr/lib64/crti.o
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o
/usr/lib64/crtn.o
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/libgcc.a
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/libgcc_s.so
/usr/lib64/libgcc_s.so.1
/lib64/libgcc_s.so.1
/usr/lib64/libgcc_s.so.1
/usr/lib64/libc.so
/usr/lib64/libc.so.6
/lib64/libc.so
/lib64/libc.so.6
/usr/lib64/libc.so
/usr/lib64/libc.so.6
该文件列表保存在文件 yy
(缺乏想象力的名称)中,然后用于:
$ nm -o $(<yy) | tee nm.log | grep -i atexit
nm: _trampoline.o: no symbols
nm: __main.o: no symbols
nm: _ctors.o: no symbols
nm: /usr/lib/gcc/x86_64-redhat-linux/4.8.5/libgcc_s.so: no symbols
nm: /usr/lib64/libgcc_s.so.1: no symbols
nm: /lib64/libgcc_s.so.1: no symbols
nm: /usr/lib64/libgcc_s.so.1: no symbols
nm: /usr/lib64/libc.so: File format not recognized
/usr/lib64/libc.so.6:00000000003bcc00 b added_atexit_handler.9157
/usr/lib64/libc.so.6:0000000000038c90 T __cxa_atexit
/usr/lib64/libc.so.6:0000000000038c90 t __cxa_atexit_internal
/usr/lib64/libc.so.6:00000000003b6838 d __elf_set___libc_atexit_element__IO_cleanup__
/usr/lib64/libc.so.6:0000000000038c40 t __internal_atexit
/usr/lib64/libc.so.6:00000000003b6838 d __start___libc_atexit
/usr/lib64/libc.so.6:00000000003b6840 d __stop___libc_atexit
nm: /lib64/libc.so: File format not recognized
/lib64/libc.so.6:00000000003bcc00 b added_atexit_handler.9157
/lib64/libc.so.6:0000000000038c90 T __cxa_atexit
/lib64/libc.so.6:0000000000038c90 t __cxa_atexit_internal
/lib64/libc.so.6:00000000003b6838 d __elf_set___libc_atexit_element__IO_cleanup__
/lib64/libc.so.6:0000000000038c40 t __internal_atexit
nm: /usr/lib64/libc.so: File format not recognized
/lib64/libc.so.6:00000000003b6838 d __start___libc_atexit
/lib64/libc.so.6:00000000003b6840 d __stop___libc_atexit
/usr/lib64/libc.so.6:00000000003bcc00 b added_atexit_handler.9157
/usr/lib64/libc.so.6:0000000000038c90 T __cxa_atexit
/usr/lib64/libc.so.6:0000000000038c90 t __cxa_atexit_internal
/usr/lib64/libc.so.6:00000000003b6838 d __elf_set___libc_atexit_element__IO_cleanup__
/usr/lib64/libc.so.6:0000000000038c40 t __internal_atexit
/usr/lib64/libc.so.6:00000000003b6838 d __start___libc_atexit
/usr/lib64/libc.so.6:00000000003b6840 d __stop___libc_atexit
$
没有证据表明那里有一个普通的 atexit
函数。它藏在哪里,那些 'File format not recognized' 消息是什么?
$ file /usr/lib64/libc.so
/usr/lib64/libc.so: ASCII text
$
ASCII 文本?什么?
$ cat /usr/lib64/libc.so
/* GNU ld script
Use the shared library, but some functions are only in
the static library, so try that secondarily. */
OUTPUT_FORMAT(elf64-x86-64)
GROUP ( /lib64/libc.so.6 /usr/lib64/libc_nonshared.a AS_NEEDED ( /lib64/ld-linux-x86-64.so.2 ) )
$
确定; /usr/lib64/libc_nonshared.a
里有什么?
$ nm /usr/lib64/libc_nonshared.a | grep -i atexit
atexit.oS:
0000000000000000 T atexit
U __cxa_atexit
$
宾果游戏!找到了!
因此,GCC 使用的 collect2
链接器似乎能够加载未在命令行中列出的文件,其中一个文件是 /usr/lib64/libc_nonshared.a
,并且该库具有atexit()
在里面。因此,您应该能够调用 atexit()
,因为它是静态链接到可执行文件中的……除非这里隐藏着更多我没有发现的黑魔法。
如果我有以下用 C 编写的程序(在 Debian 8.7 上用 GCC 编译),我可以像您期望的那样调用 atexit()
:
#include <stdlib.h>
void exit_handler(void) {
return;
}
int main () {
atexit(exit_handler);
return 0;
}
当我编译并 运行 它时:
$ gcc test.c
$ ./a.out
如您所料,不输出任何内容。事实上,当我 运行 ldd
,我得到:
$ ldd a.out
linux-vdso.so.1 (0x00007fffbe592000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe07d3a8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe07d753000)
但是libc
好像没有atexit
的符号,amd只有__cxa_atexit
和__cxa_threaded_atexit_impl
:
$ nm --dynamic /lib/x86_64-linux-gnu/libc.so.6 | grep 'atexit'
0000000000037d90 T __cxa_atexit
0000000000037fa0 T __cxa_thread_atexit_impl
正如你所期望的那样,如果我尝试动态地从 link 到 libc
,我实际上不能调用 atexit()
,例如在下面的 Racket 程序中 links 到 libc
并尝试查找 atexit
:
#lang racket
(require ffi/unsafe)
(get-ffi-obj 'atexit (ffi-lib "libc" '("6")) (_fun (_fun -> _void) -> _int))
给出输出:
$ racket findatexit.rkt
ffi-obj: couldn't get "atexit" from "libc.so.6" (/lib/x86_64-linux-gnu/libc.so.6: undefined symbol: atexit)
这里我想知道的是:
- 如果
libc
在 Linux 上没有atexit
的任何符号,为什么我仍然可以从 C 程序中调用它? - 有什么方法可以在 Linux 上动态调用
atexit
或类似函数吗?
(我应该注意到 atexit
确实是 OS X 上的一个符号,所以它只是 Linux 在这里看起来不寻常。)
编辑:
在@Jonathan的建议下,我也运行:
$ gcc -c test.c
$ nm test.o
U atexit
0000000000000000 T exit_handler
0000000000000007 T main
这似乎表明 atexit
符号在某处,但它没有出现在 ldd
显示的任何库中。
我在 Centos 7 虚拟机上进行了一些探索,我想我找到了它 — 但它并不明显!
找到了!
在/usr/lib64/libc_nonshared.a
中:
$ nm /usr/lib64/libc_nonshared.a | grep -i atexit
atexit.oS:
0000000000000000 T atexit
U __cxa_atexit
$
为什么要看那个图书馆?好问题——说来话长。你坐得舒服吗?那我就开始了……
到达那里所采取的步骤
- 使用问题中的
test.c
代码。 用
gcc -v test.c
编译:$ gcc -v test.c Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux Thread model: posix gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64' /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v test.c -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase test -version -o /tmp/ccPHTer7.s GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-11) (x86_64-redhat-linux) compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-11), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1 GGC heuristics: --param ggc-min-expand=96 --param ggc-min-heapsize=124992 ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed" ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include" #include "..." search starts here: #include <...> search starts here: /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include /usr/local/include /usr/include End of search list. GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-11) (x86_64-redhat-linux) compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-11), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1 GGC heuristics: --param ggc-min-expand=96 --param ggc-min-heapsize=124992 Compiler executable checksum: 356f86e67978d665416e07d560c8ba0d COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64' as -v --64 -o /tmp/cc5WHEA4.o /tmp/ccPHTer7.s GNU assembler version 2.25.1 (x86_64-redhat-linux) using BFD version version 2.25.1-22.base.el7 COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/ LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64' /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/cc5WHEA4.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o $
有趣的部分是末尾的
collect2
命令行。每行写一个参数,即:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/cc5WHEA4.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o
因此,有一堆
cr*.o
文件,外加三个库:-lc
、-lgcc
和-lgcc_s
要查找,以及要查看的一堆目录:-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5
、-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64
、-L/lib/../lib64
、-L/usr/lib/../lib64
、-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../..
。/tmp/cc5WHEA4.o
是从test.c
. 创建的目标文件
对路径名应用一些清理代码,然后使用
ls
帮助查找库生成一个文件列表以进一步检查:/lib64/ld-linux-x86-64.so.2 /usr/lib64/crt1.o /usr/lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib64/crtn.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/libgcc.a /usr/lib/gcc/x86_64-redhat-linux/4.8.5/libgcc_s.so /usr/lib64/libgcc_s.so.1 /lib64/libgcc_s.so.1 /usr/lib64/libgcc_s.so.1 /usr/lib64/libc.so /usr/lib64/libc.so.6 /lib64/libc.so /lib64/libc.so.6 /usr/lib64/libc.so /usr/lib64/libc.so.6
该文件列表保存在文件
yy
(缺乏想象力的名称)中,然后用于:$ nm -o $(<yy) | tee nm.log | grep -i atexit nm: _trampoline.o: no symbols nm: __main.o: no symbols nm: _ctors.o: no symbols nm: /usr/lib/gcc/x86_64-redhat-linux/4.8.5/libgcc_s.so: no symbols nm: /usr/lib64/libgcc_s.so.1: no symbols nm: /lib64/libgcc_s.so.1: no symbols nm: /usr/lib64/libgcc_s.so.1: no symbols nm: /usr/lib64/libc.so: File format not recognized /usr/lib64/libc.so.6:00000000003bcc00 b added_atexit_handler.9157 /usr/lib64/libc.so.6:0000000000038c90 T __cxa_atexit /usr/lib64/libc.so.6:0000000000038c90 t __cxa_atexit_internal /usr/lib64/libc.so.6:00000000003b6838 d __elf_set___libc_atexit_element__IO_cleanup__ /usr/lib64/libc.so.6:0000000000038c40 t __internal_atexit /usr/lib64/libc.so.6:00000000003b6838 d __start___libc_atexit /usr/lib64/libc.so.6:00000000003b6840 d __stop___libc_atexit nm: /lib64/libc.so: File format not recognized /lib64/libc.so.6:00000000003bcc00 b added_atexit_handler.9157 /lib64/libc.so.6:0000000000038c90 T __cxa_atexit /lib64/libc.so.6:0000000000038c90 t __cxa_atexit_internal /lib64/libc.so.6:00000000003b6838 d __elf_set___libc_atexit_element__IO_cleanup__ /lib64/libc.so.6:0000000000038c40 t __internal_atexit nm: /usr/lib64/libc.so: File format not recognized /lib64/libc.so.6:00000000003b6838 d __start___libc_atexit /lib64/libc.so.6:00000000003b6840 d __stop___libc_atexit /usr/lib64/libc.so.6:00000000003bcc00 b added_atexit_handler.9157 /usr/lib64/libc.so.6:0000000000038c90 T __cxa_atexit /usr/lib64/libc.so.6:0000000000038c90 t __cxa_atexit_internal /usr/lib64/libc.so.6:00000000003b6838 d __elf_set___libc_atexit_element__IO_cleanup__ /usr/lib64/libc.so.6:0000000000038c40 t __internal_atexit /usr/lib64/libc.so.6:00000000003b6838 d __start___libc_atexit /usr/lib64/libc.so.6:00000000003b6840 d __stop___libc_atexit $
没有证据表明那里有一个普通的
atexit
函数。它藏在哪里,那些 'File format not recognized' 消息是什么?$ file /usr/lib64/libc.so /usr/lib64/libc.so: ASCII text $
ASCII 文本?什么?
$ cat /usr/lib64/libc.so /* GNU ld script Use the shared library, but some functions are only in the static library, so try that secondarily. */ OUTPUT_FORMAT(elf64-x86-64) GROUP ( /lib64/libc.so.6 /usr/lib64/libc_nonshared.a AS_NEEDED ( /lib64/ld-linux-x86-64.so.2 ) ) $
确定;
/usr/lib64/libc_nonshared.a
里有什么?$ nm /usr/lib64/libc_nonshared.a | grep -i atexit atexit.oS: 0000000000000000 T atexit U __cxa_atexit $
宾果游戏!找到了!
因此,GCC 使用的 collect2
链接器似乎能够加载未在命令行中列出的文件,其中一个文件是 /usr/lib64/libc_nonshared.a
,并且该库具有atexit()
在里面。因此,您应该能够调用 atexit()
,因为它是静态链接到可执行文件中的……除非这里隐藏着更多我没有发现的黑魔法。