歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

查找動態庫函數的過程分析:用GDB跟蹤匯編代碼

在一個最簡單的hello world程序中,printf是一個屬於libc庫的動態庫函數。在編譯時是不知道printf所在地址的,那麼,在程序運行的時候是怎麼找到printf所在地址並且跳到那個地址去執行的呢?本文將以printf為例,用GDB跟蹤動態庫函數的查找過程。匯編以MIPS為例, 以後有時間再分析ARM版本的。
首先給出程序代碼:

  1. #include <stdio.h>

  2. int main(int argc, char* argv[])
  3. {
  4.     printf("Hello world %d.\n", argc);
  5.     return 0;
  6. }
用以下命令編譯得到可執行程序,並DUMP其匯編指令:
  1. mipsel-linux-gnu-gcc main.c -o main
  2. mipsel-linux-gnu-objdump -xD main > main.dump
一. 匯編代碼靜態分析
在得到的main.dump文件中, 找到main函數所在的地方如下:
  1. 004005b0 <main>:
  2. 4005b0: 27bdffe8 addiu sp,sp,-24
  3. 4005b4: afbf0014 sw ra,20(sp)
  4. 4005b8: afbe0010 sw s8,16(sp)
  5. 4005bc: 03a0f021 move s8,sp
  6. 4005c0: afc40018 sw a0,24(s8)
  7. 4005c4: afc5001c sw a1,28(s8)
  8. 4005c8: 3c020040 lui v0,0x40
  9. 4005cc: 24440760 addiu a0,v0,1888
  10. 4005d0: 8fc50018 lw a1,24(s8)
  11. 4005d4: 3c020040 lui v0,0x40
  12. 4005d8: 24590470 addiu t9,v0,1136
  13. 4005dc: 0320f809 jalr t9
  14. 4005e0: 00000000 nop
  15. 4005e4: 00001021 move v0,zero
  16. 4005e8: 03c0e821 move sp,s8
  17. 4005ec: 8fbf0014 lw ra,20(sp)
  18. 4005f0: 8fbe0010 lw s8,16(sp)
  19. 4005f4: 27bd0018 addiu sp,sp,24
  20. 4005f8: 03e00008 jr ra
  21. 4005fc: 00000000 nop
藍色的三行是調用printf的匯編。注意這裡的lui指令,並不是吧0x40放在v0寄存器裡,而是把0x40左移16位再放進v0寄存器,也就是說v0=0x400000。 1136換成十六進制是0x470,那麼t9的值就是0x400470,那麼jalr t9就是跳轉到0x400470的位置。這個位置所在處就是printf@plt.
  1. 00400470 <printf@plt>:
  2. 400470: 3c0f0041 lui t7,0x41
  3. 400474: 8df907c0 lw t9,1984(t7)
  4. 400478: 25f807c0 addiu t8,t7,1984
  5. 40047c: 03200008 jr t9
  6. 400480: 00000000 nop
這裡的流程是:t7=0x410000, t9=*(0x4107c0), t8=0x4107c0, jr t9 也就是寄存器t8的值是0x4107c0, 而t9的值是內存地址0x4107c0處存放的值。然後跳轉到t9指示的地址處。所以,這裡的關鍵就是內存地址0x4107c0處存放的值到底是什麼。
  1. Disassembly of section .got.plt:
  2. 004107b4 <.got.plt>:
  3. ...
  4. 4107bc: 00400440 0x400440
  5. 4107c0: 00400440 0x400440
可見,地址0x4107c0位於.got.plt段,存放的值為0x400440. 也就是說剛才程序的執行流程會跳轉到0x400440地址處,這正是.plt段的位置。
  1. Disassembly of section .plt:
  2. 00400440 <_PROCEDURE_LINKAGE_TABLE_>:
  3. 400440: 3c1c0041 lui gp,0x41
  4. 400444: 8f9907b4 lw t9,1972(gp)
  5. 400448: 279c07b4 addiu gp,gp,1972
  6. 40044c: 031cc023 subu t8,t8,gp
  7. 400450: 03e07821 move t7,ra
  8. 400454: 0018c082 srl t8,t8,0x2
  9. 400458: 0320f809 jalr t9
  10. 40045c: 2718fffe addiu t8,t8,-2
  11. 00400460 <__libc_start_main@plt>:
  12. 400460: 3c0f0041 lui t7,0x41
  13. 400464: 8df907bc lw t9,1980(t7)
  14. 400468: 25f807bc addiu t8,t7,1980
  15. 40046c: 03200008 jr t9
先看藍色部分,t9=*(0x4107b4),其實就是以0x4107b4處存放的值做目的地址去跳轉。而從上面.got.plt段的匯編看,0x4107b4處是省略號,內容未知,怎麼回事? 用mipsel-linux-gnu-objdump –xDsStT main > main.dump 可以看到以下內容:
  1. Contents of section .got.plt:
  2. 4107b4 00000000 00000000 40044000 40044000 ........@.@.@.@.
所以0x4107b4處的值應該是0. 難道Jalr跳到零地址嗎?
Copyright © Linux教程網 All Rights Reserved