用彙編除錯的方法窺探庫函式的實現

2021-06-09 06:43:55 字數 1710 閱讀 9977

這篇筆記的起因是我想要**一下intelmkl庫中spmv的實現細節,有可能的話看是不是可以在彙編層面做一些優化的工作。需要分析的mkl庫函式是:

void

mkl_scsrmv

(char

*transa

,mkl_int*m,

mkl_int*k,

float

*alpha

,char

*matdescra

,float

*val

,mkl_int

*indx

,mkl_int

*pntrb

,mkl_int

*pntre

,float*x,

float

*beta

,float

*y);

libmkl_core.a

最內層的核心運算函式

libmkl_intel_thread.a

中間層分支介面

libmkl_intel_lp64.a

最外層運算介面

反彙編的大概方法:

(1)    使用 nm –s 命令匯出庫檔案符號表

(2)    在符號表中搜尋頂層函式名稱(前面標記t),找出其呼叫的下層函式(前面標記u)

(3)    如果沒有到達最內層的核心運算**,重複進行第(2)步;否則在符號表中搜尋下層函式名稱,確定其所在的obj檔案

(4)    使用ar –x [file] 命令從庫檔案中解壓出obj檔案

(5)    使用 objdump –d 命令反彙編obj檔案,得到彙編**

實際操作中發現這種方法想要dump出期望的核心運算函式的彙編**非常困難,原因有兩個:

1.      根據呼叫的資料格式和引數不同,以及具體硬體平台支援的如sse等優化指令集的區別,最外層運算函式對應的核心運算函式的分支數量非常大,分支的命名不易弄懂,不易確定正確的呼叫路徑;

2.      在尋找呼叫路徑的過程中,如果僅從符號表不能確定,則需要反彙編出中間層呼叫部分的**做參考,但是有些函式呼叫是用函式指標來實現的,從彙編**中看不到被呼叫的函式符號,使得這個過程變得困難。

最終我採用的是使用gdb動態除錯的方法,來確定最終實際被呼叫的核心運算函式。具體步驟如下:

1.        編寫乙個呼叫mkl_scsrmv的「殼程式」,並靜態編譯鏈結mkl庫(使用-static編譯)

2.        gdb啟動程式,設定「display/i $pc」(每次中斷列印下一條彙編指令)

3.        b main,run

4.        disassemble顯示彙編指令

5.        在欲考察的函式處設定斷點,格式是 b *指令位址,如b *( main+offset ) 或 b *0xnnnn

6.        c 執行到斷點

7.        si進入函式(對應源**除錯的s);ni執行下一條彙編指令(對應源**除錯的n)

8.        重複4-7,直到進入最內層的核心**,這時可以直接拷貝disassemble的結果,也可以使用函式分支的符號,到開始匯出的符號表中查詢對應的obj檔案,之後dump出彙編**

確定斷點的位置有一些技巧,對於函式分支的命名規律的了解能節省大量的時間;另外需要關注的指令基本只有callq一種,但是要注意callq接著的不一定是函式符號,比如使用函式指標的情況下,callq後面是暫存器名,此時要確定呼叫的函式分支就完全依靠gdb的動態除錯了。

c語言呼叫彙編的方法

c部分很簡單,檔名隨便,如main.c 複製 如下 include include void dectobin long dec,char b 宣告外部彙編函式 int main 本人使用的是mac 64位系統,所以64bit暫存器為r開頭,如 kmabupbrax rbx等。c呼叫的 如下,儲存的...

用彙編的眼光看C (之特殊函式)

這裡說的函式主要指的是inline函式 static函式。inline函式比較特殊,它既具有巨集的性質,同時也能讓編譯器對它進行函式檢查。static函式同樣也比較特殊,它只可以被同檔案的函式使用。如果static函式在include檔案中,那麼這個標頭檔案只要被使用一次,那麼這個函式就要在exec...

用彙編的眼光看c (之模板函式)

如果說模板類定義的是一種資料型別,那麼模板函式定義的就是一種函式。既然是函式,那麼就有輸入資料和輸出資料。和模板類的概念差不多,模板函式的初衷也是為了在函式操作上抽取共同的特性,遮蔽的是型別的不同和差異。我們可以通過下面乙個簡單的 說明問題 view plain intint compare int...