Chinaunix首页 | 论坛 | 博客
  • 博客访问: 59271
  • 博文数量: 27
  • 博客积分: 2000
  • 博客等级: 大尉
  • 技术积分: 300
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-24 17:31
文章分类
文章存档

2011年(1)

2010年(8)

2009年(18)

我的朋友

分类: LINUX

2009-06-18 14:55:23

 
找到动态连接器的入口代码。
先参看《Before main() 分析》。redhat上测试,其他linux的gdb结果不一样或有问题.
=======================================================================

[test@redhat]# more test.c
#include
int main(int argc, char *argv[])
{
    printf("Hello, world\n");
    return 0;
}

[test@redhat]# gcc -g -o test test.c
[test@redhat]# readelf -h ./test
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x80482b0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          2644 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         7
  Size of section headers:           40 (bytes)
  Number of section headers:         36
  Section header string table index: 33

# test程序的入口地址是:0x80482b0

[test@redhat]# ldd ./test
        linux-gate.so.1 =>  (0x00f28000)
        libc.so.6 => /lib/libc.so.6 (0x4f93e000)
        /lib/ld-linux.so.2 (0x4f921000)
[test@redhat]# readelf -h  /lib/ld-linux.so.2
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x4f921810
  Start of program headers:          52 (bytes into file)
  Start of section headers:          120604 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         27
  Section header string table index: 26

# 动态连接器程序/lib/ld-linux.so.2的入口地址是:0x4f921810

[test@redhat]# gdb -q ./test
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) disass _start     # _start是test程序的入口地址。
Dump of assembler code for function _start:
0x080482b0 <_start+0>:  xor    %ebp,%ebp
0x080482b2 <_start+2>:  pop    %esi
0x080482b3 <_start+3>:  mov    %esp,%ecx
0x080482b5 <_start+5>:  and    $0xfffffff0,%esp
0x080482b8 <_start+8>:  push   %eax
0x080482b9 <_start+9>:  push   %esp
0x080482ba <_start+10>: push   %edx
0x080482bb <_start+11>: push   $0x8048380
0x080482c0 <_start+16>: push   $0x8048390
0x080482c5 <_start+21>: push   %ecx
0x080482c6 <_start+22>: push   %esi
0x080482c7 <_start+23>: push   $0x8048354
0x080482cc <_start+28>: call   0x8048284 <>
0x080482d1 <_start+33>: hlt   
0x080482d2 <_start+34>: nop   
0x080482d3 <_start+35>: nop   
End of assembler dump.

# 现在test还没执行,但test内部定义的一些符号(比如 _start)已经可见。但是来自其他共享库的函数是看不见的。
 
(gdb) x/20 0x4f921810  # test程序没有执行,此时动态连接器还没映射到test的地址空间里。
0x4f921810:     Cannot access memory at address 0x4f921810  
(gdb) b * 0x4f921810   # 在动态连接器的起始地址下断点。
Breakpoint 1 at 0x4f921810
(gdb) r   # 执行test程序
Starting program: /lzzz/test
Breakpoint 1, 0x4f921810 in ?? ()
(gdb) disass
No function contains program counter for selected frame.
(gdb) disass 0x4f921810
No function contains specified address.

(gdb) x/20i 0x4f921810   # 动态连接器的起始地址已经可以访问了,说明动态连接器已经映射到test的地址空间里了,可查看汇编指令,但看不出相关函数信息。
0x4f921810:     mov    %esp,%eax
0x4f921812:     call   0x4f921f60
0x4f921817:     mov    %eax,%edi
0x4f921819:     call   0x4f921800
0x4f92181e:     add    $0x197a2,%ebx
0x4f921824:     mov    0xffffff04(%ebx),%eax
0x4f92182a:     pop    %edx
0x4f92182b:     lea    (%esp,%eax,4),%esp
0x4f92182e:     sub    %eax,%edx
0x4f921830:     push   %edx
0x4f921831:     mov    0x40(%ebx),%eax
0x4f921837:     lea    0x8(%esp,%edx,4),%esi
0x4f92183b:     lea    0x4(%esp),%ecx
0x4f92183f:     mov    %esp,%ebp
0x4f921841:     and    $0xfffffff0,%esp
0x4f921844:     push   %eax
0x4f921845:     push   %eax
0x4f921846:     push   %ebp
0x4f921847:     push   %esi
0x4f921848:     xor    %ebp,%ebp

(gdb) c   # 让程序完全执行1遍。
Continuing.
Hello, world
Program exited normally.
(gdb) disass 0x4f921810   # 再看动态连接器的代码,主要调用 _dl_start函数
Dump of assembler code for function _start:
0x4f921810 <_start+0>:  mov    %esp,%eax
0x4f921812 <_start+2>:  call   0x4f921f60 <_dl_start>
End of assembler dump.
 
# 注意动态连接器代码也以_start开头,_start符号是在动态连接器rtld.c里定义的全局符号,start.s里也定义了一个 _start全局符号,那个才是test程序真正的入口0x080482b0
 
(gdb) disass 0x4f921f60 # 动态连接器里的 _dl_start代码.
Dump of assembler code for function _dl_start:
0x4f921f60 <_dl_start+0>:       push   %ebp
0x4f921f61 <_dl_start+1>:       mov    %esp,%ebp
0x4f921f63 <_dl_start+3>:       push   %edi
0x4f921f64 <_dl_start+4>:       push   %esi
0x4f921f65 <_dl_start+5>:       push   %ebx
0x4f921f66 <_dl_start+6>:       sub    $0x3c,%esp
0x4f921f69 <_dl_start+9>:       call   0x4f93672b <__i686.get_pc_thunk.bx>
0x4f921f6e <_dl_start+14>:      add    $0x19052,%ebx
0x4f921f74 <_dl_start+20>:      mov    %eax,0xffffffc8(%ebp)
0x4f921f77 <_dl_start+23>:      rdtsc 
0x4f921f79 <_dl_start+25>:      mov    %edx,0xfffffeec(%ebx)
0x4f921f7f <_dl_start+31>:      mov    0x0(%ebx),%edx
0x4f921f85 <_dl_start+37>:      mov    %eax,0xfffffee8(%ebx)
0x4f921f8b <_dl_start+43>:      lea    0xffffff38(%ebx),%eax
---Type to continue, or q to quit---
 
(gdb) disass _dl_start   # 这个不是我们想要的_dl_start函数,估计是因为其他代码里也定义了 _dl_start变量
Dump of assembler code for function _dl_start:
0x4f953c50 <_dl_start+0>:       push   %ebp
0x4f953c51 <_dl_start+1>:       mov    %esp,%ebp
0x4f953c53 <_dl_start+3>:       sub    $0x8,%esp
0x4f953c56 <_dl_start+6>:       call   0x4f968350
0x4f953c5b <_dl_start+11>:      nop   
0x4f953c5c <_dl_start+12>:      lea    0x0(%esi),%esi
End of assembler dump.
 
(gdb) x/20 0x4f921810
0x4f921810 <_start>:    0x49e8e089      0x89000007      0xffe2e8c7      0xc381ffff
0x4f921820 <_dl_start_user+9>:  0x000197a2      0xff04838b      0x8d5affff      0xc2298424
0x4f921830 <_dl_start_user+25>: 0x40838b52      0x8d000000      0x8d089474      0x8904244c
0x4f921840 <_dl_start_user+41>: 0xf0e483e5      0x56555050      0x51e8ed31      0x8d0000d7
0x4f921850 <_dl_start_user+57>: 0xff431093      0x24248bff      0xb68de7ff      0x00000000
 
# _dl_start_user 也是在动态连接器rtld.c里定义的全局符号,紧接在_dl_start之后执行

(gdb) disass _dl_start_user
Dump of assembler code for function _dl_start_user:
0x4f921817 <_dl_start_user+0>:  mov    %eax,%edi   # %eax里存放前面_dl_start()获取的_start的地址.
0x4f921819 <_dl_start_user+2>:  call   0x4f921800 <_dl_sysinfo_int80+16>
0x4f92181e <_dl_start_user+7>:  add    $0x197a2,%ebx
0x4f921824 <_dl_start_user+13>: mov    0xffffff04(%ebx),%eax
0x4f92182a <_dl_start_user+19>: pop    %edx
0x4f92182b <_dl_start_user+20>: lea    (%esp,%eax,4),%esp
0x4f92182e <_dl_start_user+23>: sub    %eax,%edx
0x4f921830 <_dl_start_user+25>: push   %edx
0x4f921831 <_dl_start_user+26>: mov    0x40(%ebx),%eax
0x4f921837 <_dl_start_user+32>: lea    0x8(%esp,%edx,4),%esi
0x4f92183b <_dl_start_user+36>: lea    0x4(%esp),%ecx
0x4f92183f <_dl_start_user+40>: mov    %esp,%ebp
0x4f921841 <_dl_start_user+42>: and    $0xfffffff0,%esp
0x4f921844 <_dl_start_user+45>: push   %eax
0x4f921845 <_dl_start_user+46>: push   %eax
0x4f921846 <_dl_start_user+47>: push   %ebp
0x4f921847 <_dl_start_user+48>: push   %esi
0x4f921848 <_dl_start_user+49>: xor    %ebp,%ebp
0x4f92184a <_dl_start_user+51>: call   0x4f92efa0 <_dl_init_internal>
0x4f92184f <_dl_start_user+56>: lea    0xffff4310(%ebx),%edx
0x4f921855 <_dl_start_user+62>: mov    (%esp),%esp
0x4f921858 <_dl_start_user+65>: jmp    *%edi    # 跳转到test程序入口 _start开始执行
0x4f92185a <_dl_start_user+67>: lea    0x0(%esi),%esi
End of assembler dump.
(gdb) q
[test@redhat]#

===========================================================================
[test@redhat]# readelf -a ./test
# 可以看到 print函数在 .sybtab表 里对应的是puts函数()
[test@redhat]# gdb -q ./test
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) disass _init  # 即使程序还没执行,test程序内部定义的很多符号(.symtab符号表)已经可以看到。但是来自其他共享库的函数是看不见的。随便找几个看看.
Dump of assembler code for function _init:
0x0804824c <_init+0>:   push   %ebp
0x0804824d <_init+1>:   mov    %esp,%ebp
0x0804824f <_init+3>:   sub    $0x8,%esp
0x08048252 <_init+6>:   call   0x80482d4
0x08048257 <_init+11>:  call   0x8048330
0x0804825c <_init+16>:  call   0x8048400 <__do_global_ctors_aux>
0x08048261 <_init+21>:  leave 
0x08048262 <_init+22>:  ret   
End of assembler dump.
(gdb) disass __bss_start
No function contains specified address.
(gdb) disass __libc_start_main
No symbol "__libc_start_main" in current context.
(gdb) disass __i686.get_pc_thunk.bx
No symbol "__i686" in current context.
(gdb) disass __libc_csu_fini
Dump of assembler code for function __libc_csu_fini:
0x08048380 <__libc_csu_fini+0>: push   %ebp
0x08048381 <__libc_csu_fini+1>: mov    %esp,%ebp
0x08048383 <__libc_csu_fini+3>: pop    %ebp
0x08048384 <__libc_csu_fini+4>: ret   
0x08048385 <__libc_csu_fini+5>: lea    0x0(%esi),%esi
0x08048389 <__libc_csu_fini+9>: lea    0x0(%edi),%edi
End of assembler dump.
(gdb) disass puts   # 此时print函数属于其他共享库,肯定也是看不见的。
No symbol "puts" in current context.
(gdb) b * 0x4f921810  # 在 动态连接器开始地址下断点。
Breakpoint 1 at 0x4f921810
(gdb) b * 0x4f921817  # 在 _dl_start_user处下断点。此时_dl_start刚刚执行完毕。
Breakpoint 2 at 0x4f921817
(gdb) r
Starting program: /lzzz/test
Breakpoint 1, 0x4f921810 in ?? ()
(gdb) disass puts     # 在 _dl_start执行之前,puts函数解析不出来。
No symbol "puts" in current context.
(gdb) c
Continuing.
Breakpoint 2, 0x4f921817 in _dl_start_user () from /lib/ld-linux.so.2
(gdb) disass puts     # _dl_start执行完毕。puts函数可以解析出来了。
Dump of assembler code for function puts:
0x4f9953f0 :    push   %ebp
0x4f9953f1 :    mov    %esp,%ebp
0x4f9953f3 :    sub    $0x1c,%esp
0x4f9953f6 :    mov    %ebx,0xfffffff4(%ebp)
0x4f9953f9 :    mov    0x8(%ebp),%eax
0x4f9953fc :   call   0x4f953c30 <__i686.get_pc_thunk.bx>
0x4f995401 :   add    $0xe1bf3,%ebx
0x4f995407 :   mov    %esi,0xfffffff8(%ebp)
0x4f99540a :   mov    %edi,0xfffffffc(%ebp)
0x4f99540d :   mov    %eax,(%esp)
0x4f995410 :   call   0x4f9aa320
---Type to continue, or q to quit---q
Quit
(gdb) c
Continuing.
Hello, world
Program exited normally.
(gdb) r   # 多执行几次test,可看到,在gdb里,test在第1次执行时会从动态连接器开头执行,以后总是从_dl_start_user处执行(第1断点拦截不下来了)。
Starting program: /lzzz/test
Breakpoint 2, 0x4f921817 in _dl_start_user () from /lib/ld-linux.so.2
(gdb) c
Continuing.
Hello, world
Program exited normally.
(gdb) r
Starting program: /lzzz/test
Breakpoint 2, 0x4f921817 in _dl_start_user () from /lib/ld-linux.so.2
(gdb) c
Continuing.
Hello, world
Program exited normally.
(gdb)

=============================================================================
# 执行 readelf -a ./test  在.symtab 符号表里多选择几个函数,每个都加上断点,看看他们的执行顺序
[test@redhat]# gdb -q ./test
(gdb) b * 0x4f921810  # 动态连接器开头地址
(gdb) b * _init
(gdb) b * _start
(gdb) b * __register_frame_info
(gdb) b * __deregister_frame_info
(gdb) b * __libc_start_main
(gdb) b * _main
(gdb) b * call_gmon_start
(gdb) b * __do_global_ctors_aux
(gdb) r   # 开始执行程序
(gdb) c   # 继续执行
 
# 可以看到,从动态连接器到main() 大致的执行顺序为:
_dl_start --> __register_frame_info --> _start() --> __libc_start_main -->
_init() --> call_gmon_start -->  __register_frame_info --> __do_global_ctors_aux
--> main ()

 
 
 
 
 
阅读(1307) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~