簡體   English   中英

如何確定堆棧上的返回地址?

[英]How can I determine the return address on stack?

我知道,如果我在某個函數foo()里面,它是從bar()函數調用的,那么這個返回地址就被推送到堆棧上。

    #include <stdio.h>

    void foo()
    {
            unsigned int x;
            printf("inside foo %x\n", &x);
    }
    int main()
    {
            foo();
            printf("in main\n");
            return 0;
    }

在上面的代碼中,當foo函數處於活動狀態時,我將獲得堆棧上第一個推送的局部變量的地址。 如何訪問在堆棧上此變量之前某處推送的返回地址(主要稱為foo)? 該位置是固定的,可以相對於第一個局部變量訪問嗎? 我怎么修改它?

編輯:我的環境是使用gcc編譯器的x86處理器上的Ubuntu 9.04。

有一個內置的gcc: void * __builtin_return_address (unsigned int level)

請參閱http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html

在某些體系結構上,您可以在堆棧中找到它相對於第一個參數。 例如,在ia32上,按下參數(按相反順序),然​​后調用將推送返回地址。 請記住,堆棧幾乎總是(並且在ia32上) 向下增長。 雖然從技術上講,您需要為您的語言和硬件平台使用ABI調用約定 (有時稱為鏈接約定 ),但實際上您通常可以猜測您是否知道過程調用機器op的工作方式。

函數的第一個參數與堆棧上的返回地址的位置之間的關系更可能是一個可靠的固定值,而不是本地和返回地址之間的關系。 但是,您當然可以打印出本地地址和第一個參數的地址,並且您經常可以找到它們之間的PC。

$ expand < ra.c
#include <stdio.h>

int main(int ac, char **av) {
  printf("%p\n", __builtin_return_address(0));
  return 0;
}
$ cc -Wall ra.c; ./a.out
0xb7e09775
$ 

當你聲明局部變量時,它們也在堆棧上 - 例如x。

如果然后聲明一個int * xptr並將其初始化為&x ,則它將指向x。

沒有(太多)阻止你將指針減少一點之前,或者將它遞增以便稍后查看。 在某處,有你的回信地址。

要知道返回地址的位置,您需要知道調用約定是什么。 這通常由編譯器設置並依賴於平台,但您可以以特定於平台的方式強制它,例如在Windows上使用__declspec(stdcall) 優化編譯器還可以為沒有外部作用域的函數創建自己的調用約定。

除非使用編譯器內置函數來獲取返回地址,否則您必須求助於內聯匯編程序來獲取值。 其他似乎在調試中工作的技術對編譯器優化非常有用。

您可以像這樣探測堆棧

// assuming a 32 bit machine here
void digInStack(void) {

    int i;
    long sneak[1];

    // feel free to adjust the search limits
    for( i = -32; i <= 32; ++i) {
        printf("offset %3d: data 0x%08X\n", i, sneak[i]);
    }
 }

你可以逃避這一點,因為C因為你對數組的索引不是特別有名而聞名。 在這里,你在堆棧上聲明一個虛擬數組,然后相對於它調整+/-。

正如Rob Walker所指出的,你肯定需要知道你的編譯器調用約定,以了解你正在查看的數據。 您可以打印出一些函數的地址,並查找相似范圍內的值,以及返回地址相對於虛擬數組的intuit。

注意事項 - 閱讀所有你想要的東西,但不要使用那個數組修改任何東西,除非(a)你完全確定你正在修改的堆棧的哪個部分,或者(b)只想看到一個有趣的/不可預知的崩潰模式。

另請注意,一般情況下,C語言無法保證您的返回地址在堆棧中,或者實際上在RAM中的任何位置。

有一些處理器體系結構將返回地址存儲在寄存器中,僅在調用開始嵌套時才使用RAM。 還有其他架構,其中有一個單獨的堆棧用於返回地址,CPU無法讀取。 這兩者仍然可以為它們實現C編譯器。

這就是為什么你需要更清楚你的環境。

試試這個

//test1.cc
//compile with
//g++ -g test1.cc -o test1 

#include <stdio.h>

void
print_function(void *p) {
    char cmd[128];
    FILE *fp;

    snprintf(cmd, sizeof(cmd), "addr2line -e %s -f %p", "test1", p);
    fp = popen(cmd, "r");
    if (fp) {
    char buf[128];
    while (fgets(buf, sizeof(buf), fp)) {
        printf("%s", buf); 
    }
    }
}

void
f2(void) {
    print_function(__builtin_return_address(0));
}

void
f1(void) {
    f2();
}

int
main(int argc, char *argv[]) {
    f1();
    return(0);
}

輸出應該是這樣的

_Z2f1v
/home/<user>/<dir>/test1.cc:30

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM