這篇筆記的起因是我想要**一下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...