glibc 中的 atexit()?
atexit() from within glibc?
我正在 glibc 中编写代码,在程序退出之前我需要一个钩子。
#include <stdlib.h>
#include <stdio.h>
void dump_statistics(void){
...
}
void init(void){
...
atexit(dump_statistics);
...
}
但是当我编译它时,它在 link 时间失败了
source.c:10: undefined reference to `atexit'
collect2: error: ld returned 1 exit status
这让我相信从 glibc 中调用这个函数是无效的。虽然我可以从包含的 header 中看到声明,但我无法在内部获得此函数的定义。这个函数在 glibc 中是否有不同的名称,或者在内部使用了不同的语义?
新增信息:
我可以确认 atexit.oS 出现在 libc_nonshared.a 中:
$ nm -an libc_nonshared.a
...
atexit.oS:
U __cxa_atexit
w __dso_handle
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T atexit
0000000000000000 a atexit.c
0000000000000000 b .bss
0000000000000000 n .comment
0000000000000000 d .data
0000000000000000 N .debug_abbrev
0000000000000000 N .debug_aranges
0000000000000000 N .debug_info
0000000000000000 N .debug_line
0000000000000000 N .debug_loc
0000000000000000 N .debug_str
0000000000000000 r .eh_frame
0000000000000000 n .note.GNU-stack
0000000000000000 t .text
0000000000000000 t .text.unlikely
...
libc.so 包含:
$ nm -an libc.so | grep atexit
0000000000000000 a cxa_atexit.c
0000000000000000 a cxa_thread_atexit_impl.c
0000000000000000 a old_atexit.c
0000000000035ec0 t __internal_atexit
0000000000035f10 T __cxa_atexit
0000000000035f10 t __GI___cxa_atexit
00000000000360e0 T __cxa_thread_atexit_impl
00000000003988d8 d __elf_set___libc_atexit_element__IO_cleanup__
00000000003988d8 d __libc_atexit
00000000003988d8 d __start___libc_atexit
00000000003988e0 d __stop___libc_atexit
000000000039e8d0 b added_atexit_handler.9386
我没有 ld-linux.so 神器。
失败的链接命令:
make[4]: Entering directory '/root_dir/glibc-2.23/time'
make[4]: Leaving directory '/root_dir/glibc-2.23/time'
make[3]: Leaving directory '/root_dir/glibc-2.23/elf'
gcc -nostdlib -nostartfiles -r -o /root_dir/glibc-2.23_build/libc_pic.os \
-Wl,-d -Wl,--whole-archive /root_dir/glibc-2.23_build/libc_pic.a -o /root_dir/glibc-2.23_build/libc_pic.os
gcc -shared -static-libgcc -Wl,-O1 -Wl,-z,defs -Wl,-dynamic-linker=/root_dir/glibc-2.23_install/lib/ld-linux-x86-64.so.2 -B/root_dir/glibc-2.23_build/csu/ -Wl,--version-script=/root_dir/glibc-2.23_build/libc.map -Wl,-soname=libc.so.6 -Wl,-z,combreloc -Wl,-z,relro -Wl,--hash-style=both -nostdlib -nostartfiles -e __libc_main -L/root_dir/glibc-2.23_build -L/root_dir/glibc-2.23_build/math -L/root_dir/glibc-2.23_build/elf -L/root_dir/glibc-2.23_build/dlfcn -L/root_dir/glibc-2.23_build/nss -L/root_dir/glibc-2.23_build/nis -L/root_dir/glibc-2.23_build/rt -L/root_dir/glibc-2.23_build/resolv -L/root_dir/glibc-2.23_build/crypt -L/root_dir/glibc-2.23_build/mathvec -L/root_dir/glibc-2.23_build/nptl -Wl,-rpath-link=/root_dir/glibc-2.23_build:/root_dir/glibc-2.23_build/math:/root_dir/glibc-2.23_build/elf:/root_dir/glibc-2.23_build/dlfcn:/root_dir/glibc-2.23_build/nss:/root_dir/glibc-2.23_build/nis:/root_dir/glibc-2.23_build/rt:/root_dir/glibc-2.23_build/resolv:/root_dir/glibc-2.23_build/crypt:/root_dir/glibc-2.23_build/mathvec:/root_dir/glibc-2.23_build/nptl -o /root_dir/glibc-2.23_build/libc.so -T /root_dir/glibc-2.23_build/shlib.lds /root_dir/glibc-2.23_build/csu/abi-note.o /root_dir/glibc-2.23_build/elf/soinit.os /root_dir/glibc-2.23_build/libc_pic.os /root_dir/glibc-2.23_build/elf/sofini.os /root_dir/glibc-2.23_build/elf/interp.os /root_dir/glibc-2.23_build/elf/ld.so -lgcc
/root_dir/glibc-2.23_build/libc_pic.os: In function `soa_arena_init':
/root_dir/glibc-2.23/malloc/small-object-arena.c:103: undefined reference to `atexit'
collect2: error: ld returned 1 exit status
../Makerules:681: recipe for target '/root_dir/glibc-2.23_build/libc.so' failed
make[2]: *** [/root_dir/glibc-2.23_build/libc.so] Error 1
make[2]: Leaving directory '/root_dir/glibc-2.23/elf'
Makefile:214: recipe for target 'elf/subdir_lib' failed
make[1]: *** [elf/subdir_lib] Error 2
make[1]: Leaving directory '/root_dir/glibc-2.23'
Makefile:9: recipe for target 'all' failed
make: *** [all] Error 2
small-object-arena.c 是我添加的文件,它包含我尝试从中调用 atexit 的 init 函数;该文件被合并到 glibc 的 malloc 子系统中,并且从第一次使用顶级内存管理 API(例如 malloc、realloc、calloc 等)时调用 init。
This leads me to believe that it is not valid to call this function from within glibc.a
你的结论是错误的。事实上,GLIBC 本身已经从例如调用 atexit
__gmon_start__
在 csu/gmon-start.c
.
问题可能是 libc.a
的一小部分链接到 ld-linux.so
,并且 那个子集 不包含 atexit
.您修改为调用 atexit
的代码很可能是 包含在 ld-linux
.
中
由于ld-linux
必须完全self-contained(不能有任何未解决的依赖关系),你必须小心不要向它添加任何依赖关系。
atexit
是在 libc_nonshared.a
中定义的,而不是 libc.so.6
。要找到它,link 编辑器需要一个 linker 脚本,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 (
/lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a
AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 )
)
(此示例来自 Debian,因此是 multi-arch 路径,为了便于阅读,已将其换行。)
如果libc.so
是符号link改为libc.so.6
,说明您的系统安装已经损坏,您应该考虑重新安装。
我正在 glibc 中编写代码,在程序退出之前我需要一个钩子。
#include <stdlib.h>
#include <stdio.h>
void dump_statistics(void){
...
}
void init(void){
...
atexit(dump_statistics);
...
}
但是当我编译它时,它在 link 时间失败了
source.c:10: undefined reference to `atexit'
collect2: error: ld returned 1 exit status
这让我相信从 glibc 中调用这个函数是无效的。虽然我可以从包含的 header 中看到声明,但我无法在内部获得此函数的定义。这个函数在 glibc 中是否有不同的名称,或者在内部使用了不同的语义?
新增信息:
我可以确认 atexit.oS 出现在 libc_nonshared.a 中:
$ nm -an libc_nonshared.a
...
atexit.oS:
U __cxa_atexit
w __dso_handle
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T atexit
0000000000000000 a atexit.c
0000000000000000 b .bss
0000000000000000 n .comment
0000000000000000 d .data
0000000000000000 N .debug_abbrev
0000000000000000 N .debug_aranges
0000000000000000 N .debug_info
0000000000000000 N .debug_line
0000000000000000 N .debug_loc
0000000000000000 N .debug_str
0000000000000000 r .eh_frame
0000000000000000 n .note.GNU-stack
0000000000000000 t .text
0000000000000000 t .text.unlikely
...
libc.so 包含:
$ nm -an libc.so | grep atexit
0000000000000000 a cxa_atexit.c
0000000000000000 a cxa_thread_atexit_impl.c
0000000000000000 a old_atexit.c
0000000000035ec0 t __internal_atexit
0000000000035f10 T __cxa_atexit
0000000000035f10 t __GI___cxa_atexit
00000000000360e0 T __cxa_thread_atexit_impl
00000000003988d8 d __elf_set___libc_atexit_element__IO_cleanup__
00000000003988d8 d __libc_atexit
00000000003988d8 d __start___libc_atexit
00000000003988e0 d __stop___libc_atexit
000000000039e8d0 b added_atexit_handler.9386
我没有 ld-linux.so 神器。
失败的链接命令:
make[4]: Entering directory '/root_dir/glibc-2.23/time'
make[4]: Leaving directory '/root_dir/glibc-2.23/time'
make[3]: Leaving directory '/root_dir/glibc-2.23/elf'
gcc -nostdlib -nostartfiles -r -o /root_dir/glibc-2.23_build/libc_pic.os \
-Wl,-d -Wl,--whole-archive /root_dir/glibc-2.23_build/libc_pic.a -o /root_dir/glibc-2.23_build/libc_pic.os
gcc -shared -static-libgcc -Wl,-O1 -Wl,-z,defs -Wl,-dynamic-linker=/root_dir/glibc-2.23_install/lib/ld-linux-x86-64.so.2 -B/root_dir/glibc-2.23_build/csu/ -Wl,--version-script=/root_dir/glibc-2.23_build/libc.map -Wl,-soname=libc.so.6 -Wl,-z,combreloc -Wl,-z,relro -Wl,--hash-style=both -nostdlib -nostartfiles -e __libc_main -L/root_dir/glibc-2.23_build -L/root_dir/glibc-2.23_build/math -L/root_dir/glibc-2.23_build/elf -L/root_dir/glibc-2.23_build/dlfcn -L/root_dir/glibc-2.23_build/nss -L/root_dir/glibc-2.23_build/nis -L/root_dir/glibc-2.23_build/rt -L/root_dir/glibc-2.23_build/resolv -L/root_dir/glibc-2.23_build/crypt -L/root_dir/glibc-2.23_build/mathvec -L/root_dir/glibc-2.23_build/nptl -Wl,-rpath-link=/root_dir/glibc-2.23_build:/root_dir/glibc-2.23_build/math:/root_dir/glibc-2.23_build/elf:/root_dir/glibc-2.23_build/dlfcn:/root_dir/glibc-2.23_build/nss:/root_dir/glibc-2.23_build/nis:/root_dir/glibc-2.23_build/rt:/root_dir/glibc-2.23_build/resolv:/root_dir/glibc-2.23_build/crypt:/root_dir/glibc-2.23_build/mathvec:/root_dir/glibc-2.23_build/nptl -o /root_dir/glibc-2.23_build/libc.so -T /root_dir/glibc-2.23_build/shlib.lds /root_dir/glibc-2.23_build/csu/abi-note.o /root_dir/glibc-2.23_build/elf/soinit.os /root_dir/glibc-2.23_build/libc_pic.os /root_dir/glibc-2.23_build/elf/sofini.os /root_dir/glibc-2.23_build/elf/interp.os /root_dir/glibc-2.23_build/elf/ld.so -lgcc
/root_dir/glibc-2.23_build/libc_pic.os: In function `soa_arena_init':
/root_dir/glibc-2.23/malloc/small-object-arena.c:103: undefined reference to `atexit'
collect2: error: ld returned 1 exit status
../Makerules:681: recipe for target '/root_dir/glibc-2.23_build/libc.so' failed
make[2]: *** [/root_dir/glibc-2.23_build/libc.so] Error 1
make[2]: Leaving directory '/root_dir/glibc-2.23/elf'
Makefile:214: recipe for target 'elf/subdir_lib' failed
make[1]: *** [elf/subdir_lib] Error 2
make[1]: Leaving directory '/root_dir/glibc-2.23'
Makefile:9: recipe for target 'all' failed
make: *** [all] Error 2
small-object-arena.c 是我添加的文件,它包含我尝试从中调用 atexit 的 init 函数;该文件被合并到 glibc 的 malloc 子系统中,并且从第一次使用顶级内存管理 API(例如 malloc、realloc、calloc 等)时调用 init。
This leads me to believe that it is not valid to call this function from within glibc.a
你的结论是错误的。事实上,GLIBC 本身已经从例如调用 atexit
__gmon_start__
在 csu/gmon-start.c
.
问题可能是 libc.a
的一小部分链接到 ld-linux.so
,并且 那个子集 不包含 atexit
.您修改为调用 atexit
的代码很可能是 包含在 ld-linux
.
由于ld-linux
必须完全self-contained(不能有任何未解决的依赖关系),你必须小心不要向它添加任何依赖关系。
atexit
是在 libc_nonshared.a
中定义的,而不是 libc.so.6
。要找到它,link 编辑器需要一个 linker 脚本,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 (
/lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a
AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 )
)
(此示例来自 Debian,因此是 multi-arch 路径,为了便于阅读,已将其换行。)
如果libc.so
是符号link改为libc.so.6
,说明您的系统安装已经损坏,您应该考虑重新安装。