2013-12-22 60 views
2

我有以下程序:哪裏的參數是在gdb和ret的?

void test_function(int a,int b, int c, int d){ 
int flag; 
char buffer[10]; 

flag = 31337; 
buffer[0]='A'; 
} 

int main(){ 
test_function(1,2,3,4); 
} 

gcc -g選項編譯它。

我設置了2個斷點之一,在main和main之後的test_function調用之前。

(gdb) list 
1 void test_function(int a,int b, int c, int d){ 
2 int flag; 
3 char buffer[10]; 
4 
5 flag = 31337; 
6 buffer[0]='A'; 
7 } 
8 
9 int main(){ 
10 test_function(1,2,3,4); 
(gdb) break 10 
Breakpoint 1 at 0x804843c: file stackexample.c, line 10. 
(gdb) break test_function 
Breakpoint 2 at 0x804840a: file stackexample.c, line 1. 
(gdb) run 
Starting program: /root/tests/c-tests/./stackexample 


Breakpoint 1, main() at stackexample.c:10 
10 test_function(1,2,3,4); 
(gdb) i r esp ebp eip 
esp   0xbffff4d0 0xbffff4d0 
ebp   0xbffff4e8 0xbffff4e8 
eip   0x804843c 0x804843c <main+9> 

據我所知,0xbffff4d0該地址是堆棧(頂部最高地址)的當前底部,這將被用於新的堆棧幀的test_function的號召之後的創建(參考)。

(gdb) x/5i $eip 
=> 0x804843c <main+9>: mov DWORD PTR [esp+0xc],0x4 
    0x8048444 <main+17>: mov DWORD PTR [esp+0x8],0x3 
    0x804844c <main+25>: mov DWORD PTR [esp+0x4],0x2 
    0x8048454 <main+33>: mov DWORD PTR [esp],0x1 
    0x804845b <main+40>: call 0x8048404 <test_function> 

在test_function調用之前,參數與這些mov指令一起存儲。

(gdb) info frame 
Stack level 0, frame at 0xbffff4f0: 
eip = 0x804843c in main (stackexample.c:10); saved eip 0xb7e8bbd6 
source language c. 
Arglist at 0xbffff4e8, args: 
Locals at 0xbffff4e8, Previous frame's sp is 0xbffff4f0 
Saved registers: 
ebp at 0xbffff4e8, eip at 0xbffff4ec 
(gdb) cont 
Continuing. 

Breakpoint 2, test_function (a=1, b=2, c=3, d=4) at stackexample.c:1 
1 void test_function(int a,int b, int c, int d){ 
(gdb) info frame 
Stack level 0, frame at 0xbffff4d0: 
eip = 0x804840a in test_function (stackexample.c:1); saved eip 0x8048460 
called by frame at 0xbffff4f0 
source language c. 
Arglist at 0xbffff4c8, args: a=1, b=2, c=3, d=4 
Locals at 0xbffff4c8, Previous frame's sp is 0xbffff4d0 
Saved registers: 
ebp at 0xbffff4c8, eip at 0xbffff4cc 
(gdb) i r esp ebp eip 
esp   0xbffff4a0 0xbffff4a0 
ebp   0xbffff4c8 0xbffff4c8 
eip   0x804840a 0x804840a <test_function+6> 

因此,這裏顯而易見的是,第一幀的esp成爲該幀的當前起始地址。雖然我沒有得到的是在哪個堆棧幀的參數?因爲...

(gdb) info locals 
flag = 134513420 
buffer = "\377\267\364\237\004\b\350\364\377\277" 

這裏我們看不到args。如果我們..

(gdb) info args 
a = 1 
b = 2 
c = 3 
d = 4 
(gdb) print &a 
$4 = (int *) 0xbffff4d0 
(gdb) print &b 
$5 = (int *) 0xbffff4d4 
(gdb) print &c 
$6 = (int *) 0xbffff4d8 
(gdb) print &d 
$7 = (int *) 0xbffff4dc 

所以在這裏我們看到,參數從第一個地址,這個當前棧幀有這0xbffff4d0

而另一個問題是,下面根據該輸出開始

(gdb) x/16xw $esp 
0xbffff4a0: 0xb7fc9ff4 0x08049ff4 0xbffff4b8 0x0804830c 
0xbffff4b0: 0xb7ff1080 0x08049ff4 0xbffff4e8 0x08048499 
0xbffff4c0: 0xb7fca324 0xb7fc9ff4 0xbffff4e8 0x08048460 
0xbffff4d0: 0x00000001 0x00000002 0x00000003 0x00000004 

地址0x08048460eip = 0x804840a in test_function (stackexample.c:1); saved eip 0x8048460並且還`#1 0x08048460在main()在stackexample.c:10(從回溯輸出)

如何來和主要是在頂部(到一個較低的地址)比爭論?不應該ret地址在新的堆棧幀的開始?對不起,但我想了解堆棧如何工作,我有點困惑:S另一件我不明白的地方是本地變量的引用是通過$ esp +(offset)發生的。 esp的值是否總是取決於執行的「當前」堆棧幀?

+0

與論壇網站不同,我們不使用「謝謝」或「任何幫助表示讚賞」,或在[so]上簽名。請參閱[應該'嗨','謝謝',標語和致敬辭職](http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be-刪除 - 從 - 個)。 – rene

回答

2

你拆解程序看起來像這樣我的系統上:

gcc -m32 -c -o stackexample.o stackexample.c 
objdump -d -M intel stackexample.o 


test_function: 
    push ebp 
    mov ebp,esp 
    sub esp,0x10 
    mov DWORD PTR [ebp-0x4],0x7a69 
    mov BYTE PTR [ebp-0xe],0x41 
    leave 
    ret 

main: 
    push ebp 
    mov ebp,esp 
    sub esp,0x10 
    mov DWORD PTR [esp+0xc],0x4 
    mov DWORD PTR [esp+0x8],0x3 
    mov DWORD PTR [esp+0x4],0x2 
    mov DWORD PTR [esp],0x1 
    call test_function 
    leave 
    ret 

讓我們從頭開始。

堆棧從上到下排列在內存中。堆棧的頂部有最低的地址。

esp是堆棧指針。它總是指向堆棧的頂部。

ebp是基本指針。它指向當前堆棧幀的底部。它用於引用當前函數的參數和局部變量。

這些指令

push ebp 
mov ebp,esp 

可以在每個函數的頂部找到。他們做到以下幾點:

  • 保存來電者的基本指針
  • 設置當前函數的基址指針將其分配到堆棧指針。此時,堆棧指針指向當前堆棧幀的底部,因此通過爲其指定基本指針,基本指針將顯示當前底部。堆棧指針可以在函數執行期間增加/減少,因此您使用基本指針來引用變量。基址指針也用於保存/存儲調用者的堆棧指針的服務器。

leave相當於

mov esp, ebp 
pop ebp 

其是上述說明完全相反:

  • 恢復呼叫者的堆棧指針
  • 恢復呼叫者的基址指針

現在回答你的問題

在堆棧幀參數都是???

參數存儲在調用者的堆棧幀中。但是,您可以使用基址指針來訪問它們。 info locals不會顯示有關函數的參數信息GDB的規範的一部分:

http://visualgdb.com/gdbreference/commands/info_locals

如何來RET主要是​​在頂部(到較低地址),比爭論?不應該ret地址在新堆棧幀的開始處

這是因爲參數存儲在調用者的幀中。當調用test_function時,堆棧已經存儲了參數,所以返回的地址存儲的地址比參數高(又稱低地址)。

本地變量的引用是通過$ esp +(offset)發生的。取其方便你的編譯器(不是很確定) -

據我所知,引用局部變量可以使用基指針和堆棧指針都發生。

esp的值是否總是取決於執行的「當前」堆棧幀?

是。堆棧指針是最重要的堆棧寄存器。它指向堆棧的頂部。