前两天,CU的gongping11写了一个博文atexit可以注册退出函数,在main之后,执行注册退出函数。讲的非常的好,我们在gongping11的基础上再进一步。
我写北极之北那篇博文之后,我发现了一个很好的blog,国外的这个大侠分享了很多ELF方面的宝贝,我也给别人推荐过这个blog, 如果英文实力比较高的筒子可以去看那篇博客。但是那篇博客稍微有点老了,有些过时的内容。但是我还是强力推荐。~charngda/elf.html。
- #include <stdio.h>
- #include <stdlib.h>
- void preinit(int argc, char **argv, char **envp) {
- printf("%s\n", __FUNCTION__);
- }
- void init(int argc, char **argv, char **envp) {
- printf("%s\n", __FUNCTION__);
- }
- void fini() {
- printf("%s\n", __FUNCTION__);
- }
- __attribute__((section(".init_array"))) typeof(init) *__init = init;
- __attribute__((section(".preinit_array"))) typeof(preinit) *__preinit = preinit;
- __attribute__((section(".fini_array"))) typeof(fini) *__fini = fini;
- void __attribute__ ((constructor)) constructor() {
- printf("%s\n", __FUNCTION__);
- }
- void __attribute__ ((constructor)) constructor_2() {
- printf("%s\n", __FUNCTION__);
- }
- void __attribute__ ((destructor)) destructor() {
- printf("%s\n", __FUNCTION__);
- }
- void __attribute__ ((destructor)) destructor_2() {
- printf("%s\n", __FUNCTION__);
- }
- void my_atexit() {
- printf("%s\n", __FUNCTION__);
- }
- void my_atexit2() {
- printf("%s\n", __FUNCTION__);
- }
- int main() {
- atexit(my_atexit);
- atexit(my_atexit2);
- printf("%s\n",__FUNCTION__);
- }
- root@manu:~/code/c/self/initfini# ./test
- preinit
- init
- constructor_2
- constructor
- main
- my_atexit2
- my_atexit
- destructor
- destructor_2
- fini
我的glibc的版本号是:
- root@manu:~/code/c/self/initfini# ldd test
- linux-gate.so.1 => (0xb773c000)
- libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7578000)
- /lib/ld-linux.so.2 (0xb773d000)
- root@manu:~/code/c/self/initfini# ll /lib/i386-linux-gnu/libc.so.6
- lrwxrwxrwx 1 root root 12 10月 6 04:39 /lib/i386-linux-gnu/libc.so.6 -> libc-2.15.so*
- (gdb) b preinit
- Breakpoint 1 at 0x804843a: file test.c, line 5.
- (gdb) r
- Starting program: /home/manu/code/c/self/initfini/test
- Breakpoint 1, preinit (argc=1, argv=0xbffff774, envp=0xbffff77c) at test.c:5
- 5 printf("%s\n", __FUNCTION__);
- (gdb) bt
- #0 preinit (argc=1, argv=0xbffff774, envp=0xbffff77c) at test.c:5
- #1 0xb7fecfd2 in _dl_init (main_map=0xb7fff918, argc=1, argv=0xbffff774, env=0xbffff77c) at dl-init.c:119
- #2 0xb7fdf20f in _dl_start_user () from /lib/ld-linux.so.2
- void
- internal_function
- _dl_init (struct link_map *main_map, int argc, char **argv, char **env)
- {
- ElfW(Dyn) *preinit_array = main_map->l_info[DT_PREINIT_ARRAY];
- ElfW(Dyn) *preinit_array_size = main_map->l_info[DT_PREINIT_ARRAYSZ];
- unsigned int i;
- if (__builtin_expect (GL(dl_initfirst) != NULL, 0))
- {
- call_init (GL(dl_initfirst), argc, argv, env);
- GL(dl_initfirst) = NULL;
- }
- /* Don't do anything if there is no preinit array. */
- if (__builtin_expect (preinit_array != NULL, 0)
- && preinit_array_size != NULL
- && (i = preinit_array_size->d_un.d_val / sizeof (ElfW(Addr))) > 0)
- {
- ElfW(Addr) *addrs;
- unsigned int cnt;
- if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
- _dl_debug_printf ("\ncalling preinit: %s\n\n",
- main_map->l_name[0]
- ? main_map->l_name : rtld_progname);
- addrs = (ElfW(Addr) *) (preinit_array->d_un.d_ptr + main_map->l_addr);
- for (cnt = 0; cnt < i; ++cnt)
- ((init_t) addrs[cnt]) (argc, argv, env);
- }
- /* Stupid users forced the ELF specification to be changed. It now
- says that the dynamic loader is responsible for determining the
- order in which the constructors have to run. The constructors
- for all dependencies of an object must run before the constructor
- for the object itself. Circular dependencies are left unspecified.
- This is highly questionable since it puts the burden on the dynamic
- loader which has to find the dependencies at runtime instead of
- letting the user do it right. Stupidity */
- i = main_map->l_searchlist.r_nlist;
- while (i-- > 0)
- call_init (main_map->l_initfini[i], argc, argv, env);
- #ifndef HAVE_INLINED_SYSCALLS
- /* Finished starting up. */
- INTUSE(_dl_starting_up) = 0;
- #endif
- }
下面我调试了下,
注意DT_PREINIT_ARRAY = 32 , DT_PREINIT_ARRAYSZ=33,这是定义在头文件的宏。
- (gdb) p main_map->l_info[32]
- $5 = (Elf32_Dyn *) 0x8049f0c
- $7 = {d_tag = 0x20, d_un = {d_val = 0x8049ec4, d_ptr = 0x8049ec4}}
- (gdb) p/x *(main_map->l_info[33])
- $8 = {d_tag = 0x21, d_un = {d_val = 0x4, d_ptr = 0x4}}
- (gdb) x/4x 0x08049ec4
- 0x8049ec4 <__preinit>: 0x08048434 0x08048448 0x08048484 0x08048470
- addrs = (ElfW(Addr) *) (preinit_array->d_un.d_ptr + main_map->l_addr);
- for (cnt = 0; cnt < i; ++cnt)
- ((init_t) addrs[cnt]) (argc, argv, env);
- 08048434
:: - 8048434: 55 push %ebp
- 8048435: 89 e5 mov %esp,%ebp
- 8048437: 83 ec 18 sub $0x18,%esp
- 804843a: c7 04 24 86 86 04 08 movl $0x8048686,(%esp)
- 8048441: e8 0a ff ff ff call 8048350
- 8048446: c9 leave
- 8048447: c3 ret
OK ,我们preinit段对应的代码就分析结束了,值得一提的是,此时,我们的入口点_start还没有执行,我可以证明下:
- (gdb) b _start
- Breakpoint 2 at 0x8048380
- (gdb) b init
- Breakpoint 3 at 0x804844e: init. (5 locations)
- (gdb) b __libc_start_main
- Breakpoint 4 at 0xb7e323e0: file libc-start.c, line 96.
- (gdb) c
- Continuing.
- preinit
- Breakpoint 2, 0x08048380 in _start ()
- (gdb) c
Continuing.
Breakpoint 4, __libc_start_main (main=0x80484e8, argc=1, ubp_av=0xbffff774, init=0x8048520 <__libc_csu_init>,
fini=0x8048590 <__libc_csu_fini>, rtld_fini=0xb7fed270 <_dl_fini>, stack_end=0xbffff76c) at libc-start.c:96
96 libc-start.c: 没有那个文件或目录.
(gdb) c
Continuing.
Breakpoint 3, init (argc=1, argv=0xbffff774, envp=0xbffff77c) at test.c:9
9 printf("%s\n", __FUNCTION__); - (gdb) bt
#0 init (argc=1, argv=0xbffff774, envp=0xbffff77c) at test.c:9
#1 0x08048572 in __libc_csu_init ()
#2 0xb7e3246a in __libc_start_main (main=0x80484e8, argc=1, ubp_av=0xbffff774, init=0x8048520 <__libc_csu_init>,
fini=0x8048590 <__libc_csu_fini>, rtld_fini=0xb7fed270 <_dl_fini>, stack_end=0xbffff76c) at libc-start.c:185
#3 0x080483a1 in _start ()
- void
- __libc_csu_init (int argc, char **argv, char **envp)
- {
- /* For dynamically linked executables the preinit array is executed by
- the dynamic linker (before initializing any shared object). */
- #ifndef LIBC_NONSHARED
- /* For static executables, preinit happens right before init. */
- {
- const size_t size = __preinit_array_end - __preinit_array_start;
- size_t i;
- for (i = 0; i < size; i++)
- (*__preinit_array_start [i]) (argc, argv, envp);
- }
- #endif
- _init ();
- const size_t size = __init_array_end - __init_array_start;
- for (size_t i = 0; i < size; i++)
- (*__init_array_start [i]) (argc, argv, envp);
- }
- Function pointers in .preinit_array section ,before _start
- Functions marked as __attribute__ ((constructor)), via _init
- Function pointers in .init_array section
OK ,main函数之前的事情都了了,本来把退出一起写了,但是文章太长了,而且太晚了就写这些吧。
还是有很多东西不懂,没办法,一口吃不成胖子,心急吃不了热豆腐,还是得慢慢来。
参考文献
1 ~charngda/elf.html/
2 程序员的自我修养