誰在CALL我 callstack的實現原理

2021-08-22 01:58:30 字數 1522 閱讀 2938

開發嵌入式軟體通常是比較麻煩的事,一些常用的工具往往無法使用,在開發pc軟體時簡單的任務,此時變得很複雜。今天就遇到了這樣一件事,折騰了幾個小時,僅僅是為知道call stack。

我編譯了乙個程式放到pda(arm9+linux+uclibc)上面執行,出現了乙個assert,並顯示了檔名和行號,原來是呼叫了乙個沒有實現的函式,我很想知道是誰呼叫了它,這看似簡單的問題卻讓我很頭疼,如果有gdb,那好辦-用bt命令就可以搞定,如果用的libc,那也好辦-用backtrace函式就可以搞定,問題是兩者都沒有。

想來想去只有自己寫乙個backtrace,要實現這個功能並不難,如果我們知道呼叫堆疊的格式,就可以很容易取出上層呼叫者的指令位址,有了這些上層呼叫者的指令位址,我們可以通過map檔案找到指令位址對應的源檔名和行號。

下面簡要介紹一下實現原理:

要獲得呼叫者的位址,有必要介紹一下堆疊的格式:

+---------------------------+ (高位址)

+_引數1__________+

+---------------------------+

+_引數2__________+

+---------------------------+ 引數的順序依賴於呼叫方式

+_引數.__________+

+---------------------------+

+_引數n__________+

+---------------------------+

+_eip____________+ 返回本次呼叫後,下一條指令的位址

+----------------------------+

+_ebp____________+ 這裡儲存的呼叫者的ebp

+----------------------------+

(ebp 指向這裡:相當於呼叫者和被呼叫者的分界線)

+----------------------------+

+_臨時變數1_______+

+----------------------------+

+_臨時變數2_______+

+----------------------------+

+_臨時變數.________+

+----------------------------+

+----------------------------+

+_臨時變數n_______+

+----------------------------+(低位址)

由於優化、呼叫方式、編譯器的不同,上述布局部可能有所不同,但一般來說,第乙個區域性變數前是呼叫者的ebp,ebp前是返回後下一條指令的位址。

知道了這個結構,要獲得上層呼叫的者指令位址就容易了,我們可以用如下**模擬glibc提供的backtrace的功能:

int backtrace (void **buffer, int size)

return size;}附:

通過addr2line可以找到位址對應的檔名和行號,不用手動去查map檔案了。

誰在CALL我 callstack的實現原理

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!開發嵌入式軟體通常是比較麻煩的事,一些常用的工具往往無法使用,在開發pc軟體時簡單的任務,此時變得很複雜。今天就遇到了這樣一件事,折騰了幾個小時,僅僅是為知道call stack。我編譯了乙個程式放到pda arm9 linux uclibc 上面...

誰在CALL我 callstack的實現原理

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!開發嵌入式軟體通常是比較麻煩的事,一些常用的工具往往無法使用,在開發pc軟體時簡單的任務,此時變得很複雜。今天就遇到了這樣一件事,折騰了幾個小時,僅僅是為知道call stack。我編譯了乙個程式放到pda arm9 linux uclibc 上面...

我所理解的call和apply

一 先來看看示例 這兩個方法的用途都在特定的作用域中呼叫函式,實際上等於設定函式體內this物件的值。例一 123 4567 891011 1213 1415 function box num1,num2 function sum num1,num2 alert sum 10,10 function...