2016-01-07 32 views
1

所以我目前正在學習堆棧幀,並且我想實驗打印函數的堆棧幀(手動)。打印堆棧幀

我有一個堆棧幀的心下面的圖片(I可能是錯誤的):

|        | 0xffff0fdc 
+--------------------------------+ 
|    ...    | 0xffff0fd8 
+--------------------------------+ 
|   parameter 2   | 0xffff0fd4 
+--------------------------------+ 
|   parameter 1   | 0xffff0fd0 
+--------------------------------+ 
|   return address   | 0xffff0fcc 
+--------------------------------+ 
|  local variable 2  | 0xffff0fc8 
+--------------------------------+ 
|  local variable 1  | 0xffff0fc4 
+--------------------------------+ 

因而我第一次寫這個函數來實現上述結果和打印:

void func(int a,int b) 
{ 
    uint64_t loc = 0; 
    uint64_t *sp = &loc; 

    printf("%" PRIu64 "\n",*(sp)); 
    printf("%" PRIu64 "\n",*(sp+4)); 
    printf("%" PRIu64 "\n",*(sp+8)); 
    printf("%" PRIu64 "\n",*(sp+12)); 
} 

int main() 
{ 
    func(2,3); 
    return 0; 
} 

,我得到:

0 

12884901890 

51266344552759297 

18034967110614932 

絕對不是什麼預期

我也試過「掃描」通過煙囪找到論據之一:

while (*sp != a) sp++ 

但沒有成功。我的方法有什麼問題?


我也有另外一個問題: 給出一個遞歸函數,採取簡單的階乘(INT N),我們怎麼能其中基本指針位於堆棧中的地址?


的情況下,你需要的彙編代碼: 注意,這僅包含生成的彙編代碼的函數「功能」。 我在彙編代碼與源代碼相關的地方添加了註釋。

    pushq %rbp 
        .cfi_def_cfa_offset 16 
        .cfi_offset 6, -16 
        movq %rsp, %rbp 
        .cfi_def_cfa_register 6 
        subq $32, %rsp 
        movl %edi, -20(%rbp) 
        movl %esi, -24(%rbp) 

        ***// uint64_t loc = 0;*** 

        movq $0, -16(%rbp) 

        ***// uint64_t *sp = &loc;*** 

        leaq -16(%rbp), %rax 
        movq %rax, -8(%rbp) 

        ***// printf("%" PRIu64 "\n",*sp);*** 

        movq -8(%rbp), %rax 
        movq (%rax), %rax 
        movq %rax, %rsi 
        movl $.LC0, %edi 

        movl $0, %eax 

        call printf 

        ***printf("%" PRIu64 "\n",*(sp+8));*** 

        movq -8(%rbp), %rax 
        addq $64, %rax 
        movq (%rax), %rax 
        movq %rax, %rsi 
        movl $.LC0, %edi 

        movl $0, %eax 

        call printf 

        ***// printf("%" PRIu64 "\n",*(sp+16));*** 

        movq -8(%rbp), %rax 
        subq $-128, %rax 
        movq (%rax), %rax 
        movq %rax, %rsi 
        movl $.LC0, %edi 

        movl $0, %eax 

        call printf 

        ***// printf("%" PRIu64 "\n",*(sp+32));*** 

        movq -8(%rbp), %rax 
        addq $256, %rax 

        movq (%rax), %rax 
        movq %rax, %rsi 
        movl $.LC0, %edi 

        movl $0, %eax 

        call printf 

        leave 
        .cfi_def_cfa 7, 8 
        ret 

任何意見,以幫助我更好地處理堆棧將不勝感激!

PS:我是不允許使用任何外部函數

回答

3

X86-64不通過堆棧中的第幾個參數(輸入許可),這樣你就沒有機會來打印這些。此外,本地實際的堆棧佈局取決於編譯器和設置。

由於您提供的彙編代碼,我們可以檢查它看起來像這樣的佈局:

 return address 
rbp  saved rbp 
rbp-8 local variable "sp" 
rbp-16 local variable "loc" 
rbp-20 local copy of argument "a" 
rbp-24 local copy of argument "b" 

還要注意的是ab是4個字節,其餘均爲8.此外,C指針運算秤按項目大小,所以*(sp+4)4 * 8 = 32字節而不是4您可能打算。

如果堆棧佈局是不變的,您可以使用此代碼作爲例證:

#include <stdio.h> 
#include <stdint.h> 
int main(); 
void func(int a,int b) 
{ 
    uint64_t loc = 0; 
    char *sp = (char*)&loc; 

    printf("main = %p\n", main); 
    printf("return address = %p\n", *(void**)(sp + 24)); 
    printf("saved rbp = %p\n", *(void**)(sp + 16)); 
    printf("sp = %p\n", *(void**)(sp + 8)); 
    printf("loc = %lld\n", *(uint64_t*)(sp)); 
    printf("a = %d\n", *(int*)(sp - 4)); 
    printf("b = %d\n", *(int*)(sp - 8)); 
} 

int main() 
{ 
    func(2,3); 
    return 0; 
} 

輸出示例:

main = 0x4005e6 
return address = 0x4005f9 
saved rbp = 0x7ffe057bf240 
sp = 0x7ffe057bf220 
loc = 0 
a = 2 
b = 3 
+0

這是完美的,非常感謝! :) –