在內核調試中,經常需要知道某個函數的地址,或者根據函數地址找到對應的函數,從而進行更深一步的debug。
下面介紹四種獲取內核函數地址的方法:
1、System.map
在編譯Linux內核時,會產生一個內核映像文件System.map
,也叫 內核符號表 。
內核符號表是一個映射,它將內核代碼段中的地址映射到對應的函數名或全局變量名。
在System.map
文件中,每一行都包含一個內核符號,每個符號包含三部分:
- 地址 :符號在內核內存中的地址。
- 類型 :符號的類型。例如,"T"表示該符號是一個在代碼段中的函數。
- 名稱 :符號的名字,可以是函數名或者變量名。
例如,我們要查找名為“do_fork
”的函數的地址,可以使用以下命令:
grep ' do_fork' System.map
然后會得到類似于這樣的輸出:
c0105020 T do_fork
這代表,c0105020
就是函數do_fork
的地址,T
代表該符號是個函數。
或者,直接打開System.map
搜索對應函數名或者地址。System.map
內容類似如下:
c0004000 A swapper_pg_dir
c0008000 T __init_begin
c0008000 T _sinittext
c0008000 T _stext
c0008000 T stext
c0008034 t __enable_mmu
c0008060 t __turn_mmu_on
......
A、T、t等都代表不同的類型,具體類型的定義可參考:
類型 | 說明 |
---|---|
A | 該符號的值是不能改變的,等于const |
B | 該符號來自于未初始化代碼段bss段 |
C | 該符號是通用的,通用的符號指未初始化的數據。當鏈接時,多個通用符號可能對應一個名稱,如果該符號在某一個位置定義,這個通用符號被當做未定義的引用。不明白,內核中也沒有該類型的符號 |
D | 該符號位于初始化的數據段 |
G | 位于初始化數據段,專門對應小的數據對象,比如global int x,對應的大數據對象為 數組類型等 |
I | 到其他符號的間接引用,是對于a.out文件的GNU擴展,使用非常少 |
N | 調試符號 |
R | 只讀代碼段的符號 |
S | BSS段(未初始化數據段)的小對象符號 |
T | 代碼段符號,全局函數,t為局部函數 |
U | 未定義的符號 |
V | 該符號是一個weak object,當其連接到為定義的對象上上,該符號的值變為0 |
W | 類似于V |
- | 該符號是a.out文件中的一個stabs symbol,獲取調試信息 |
? | 未知類型的符號 |
2、vmlinux
vmlinux
也是Linux
編譯出來的內核映像文件,可以通過nm
、objdump
和readelf
等工具來查看它的符號表,從而獲取函數地址。
2.1 nm讀取vmlinux
nm
命令是用于查看二進制文件(如可執行文件、目標文件、庫)的符號表的工具,所以可以用nm
命令來讀取vmlinux
里的符號表。
nm
查找vmlinux
中函數名為do_fork
的地址:
nm vmlinux | grep "do_fork"
nm
查找vmlinux
中地址為c0105020
的符號:
nm vmlinux | grep c0105020
nm
命令的輸出,與System.map
類似,會的得到類似于以下的輸出:
c0105020 T do_fork
2.2 objdump反匯編vmlinux
objdump
工具用于反匯編,可以將vmlinux
反匯編出來,得到的反匯編文件就會包含函數名和對應的 地址 ,可以直接查看文本內容。
- 使用objdump查看vmlinux函數地址 :
objdump -d vmlinux | grep "函數名"
這會返回與指定函數名匹配的符號的地址、類型和名稱。objdump
的-d
選項表示反匯編代碼。
也可以將vmlinux的內容全部反匯編出來,重定向到一個文件,然后直接查看文本內容:
objdump -D vmlinux > vmlinux_dump.txt
上述命令會將vmlinux
所有內容反匯編,并將結果輸出到vmlinux_dump.txt
文件中,-D
表示反匯編所有代碼段。
2.3 readelf讀取vmlinux
使用readelf也可以讀取vmlinux的符號表,命令如下:
readelf -s vmlinux
由于vmlinux比較大,如果要查找某個函數的地址,需要使用grep進行過濾。
例如,找到do_fork
函數的地址,你可以使用以下命令:
readelf -s vmlinux | grep "do_fork"
然后,你就會看到類似這樣的輸出:
56481: c10601e0 96 FUNC GLOBAL DEFAULT 1 do_fork
輸出內容表示,do_fork
函數的地址是c10601e0
。
3、/proc/kallsyms
vmlinux
是編譯時生成的,假設你沒有vmlinux
這個文件,只有正在運行的Linux
系統,此時也可以通過/proc/kallsyms
獲取函數地址。
/proc/kallsyms
是一個由運行中的 內核動態生成的虛擬文件 ,它反映了 當前運行的內核的狀態 。
通常這個節點是不會打開的,因為會增加內核大小,要使用這個節點,需要打開內核選項:
CONFIG_KALLSYMS=y
如果想獲取do_fork
函數的地址,可以使用以下命令:
cat /proc/kallsyms | grep " do_fork"
然后會返回一個包含do_fork
函數地址的行,如:
ffffffff810b57b0 T do_fork
輸出內容表示do_fork
函數的地址是ffffffff810b57b0
。
4、內核接口
也可以在代碼中獲取內核符號表,同樣需要打開內核選項CONFIG_KALLSYMS=y
。
kallsyms_lookup_name
- 已知函數名,獲取地址:
kallsyms_lookup_name("函數名" )
該函數會返回對應函數名的地址。
sprint_symbol
- 已知地址,返回對應符號:
#include < linux/kallsyms.h >
int sprint_symbol(char *buffer, unsigned long address)
- buffer:符號名緩存區,保存結果。
- address:符號地址。
-
內核
+關注
關注
3文章
1363瀏覽量
40228 -
Linux
+關注
關注
87文章
11230瀏覽量
208937 -
調試
+關注
關注
7文章
572瀏覽量
33899 -
函數
+關注
關注
3文章
4307瀏覽量
62434
發布評論請先 登錄
相關推薦
評論