日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

dump_stack介绍以及内核符号表的生成和查找过程

發(fā)布時(shí)間:2023/12/20 编程问答 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 dump_stack介绍以及内核符号表的生成和查找过程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

內(nèi)核中的dump_stack()

獲得內(nèi)核中當(dāng)前進(jìn)程的棧回溯信息需要用到的最重要的三個(gè)內(nèi)容就是:

棧指針:sp寄存器,用來跟蹤程序執(zhí)行過程。

返回地址:ra寄存器,用來獲取函數(shù)的返回地址。

程序計(jì)數(shù)器:epc,用于定位當(dāng)前指令的位置。

本文的內(nèi)容都是基于mips體系架構(gòu)的,如果你不搞mips,就只看個(gè)大致流程就可以了,不然可能會(huì)被某些內(nèi)容誤導(dǎo)。在ARM中,這三個(gè)寄存器分別為SP、LR和PC寄存器。

dump_stack()用于回溯函數(shù)調(diào)用關(guān)系,他需要做的工作很簡(jiǎn)單:

1.???從進(jìn)程棧中找到當(dāng)前函數(shù)(callee)的返回地址。

2.???根據(jù)函數(shù)返回地址,從代碼段中定位該地址位于哪個(gè)函數(shù)中,找到的函數(shù)即為caller函數(shù)。

3.???打印caller函數(shù)的函數(shù)名。

4.???重復(fù)前3個(gè)步驟。直到返回值為0或不在內(nèi)核記錄的符號(hào)表范圍內(nèi)。

在編譯程序的時(shí)候,所有函數(shù)所需要的棧空間的大小都已經(jīng)計(jì)算出來,如果函數(shù)需要保存返回地址,返回地址在該函數(shù)的棧空間中保存的位置也都計(jì)算出來了。所以,我們想得到返回地址,只需得到每個(gè)函數(shù)棧即可,而所有函數(shù)棧都放在進(jìn)程的棧中,棧頂為sp。

返回地址是caller函數(shù)中將要執(zhí)行的指令,是指向代碼段的,這個(gè)更容易得到,因?yàn)榇a段在編譯時(shí)就確定了。

當(dāng)前函數(shù)的位置通過pc的值可以得到。

例如,現(xiàn)在有func0調(diào)用func1,func1又調(diào)用func2,在func2執(zhí)行過程中,進(jìn)程棧空間大致如下:


左圖為棧空間,棧頂為sp,右圖為程序代碼的部分內(nèi)容。右圖中的實(shí)曲線表示出了函數(shù)之間的調(diào)用和返回關(guān)系。調(diào)用關(guān)系通過跳轉(zhuǎn)指令完成,返回地址通過左圖每個(gè)函數(shù)棧空間中存儲(chǔ)的返回地址指定。這樣我們就可以得到函數(shù)的調(diào)用關(guān)系,并通過每個(gè)函數(shù)的地址打印出函數(shù)名。

那dump_stack的工作流程就很清楚了。我就不帖代碼了,因?yàn)榛旧隙际求w系結(jié)構(gòu)相關(guān)的操作。

需要說明的一個(gè)地方是,通過函數(shù)的地址來打印函數(shù)名是通過格式控制符%pS來打印的:

printk("[<%p>] %pS\n", (void *) ip,(void *) ip);

在內(nèi)核代碼樹的lib/vsprintf.c中的pointer函數(shù)中,說明了printk中的%pS的意思:

[cpp]?view plaincopy
  • case?'S':??
  • ???????return?symbol_string(buf,?end,?ptr,?spec,?*fmt);??
  • 即'S'表示打印符號(hào)名,而這個(gè)符號(hào)名是kallsyms里獲取的。

    可以看一下kernel/kallsyms.c中的kallsyms_lookup()函數(shù),它負(fù)責(zé)通過地址找到函數(shù)名,分為兩部分:

    1. 如果地址在編譯內(nèi)核時(shí)得到的地址范圍內(nèi),就查找kallsyms_names數(shù)組來獲得函數(shù)名。

    2. 如果這個(gè)地址是某個(gè)內(nèi)核模塊中的函數(shù),則在模塊加載后的地址表中查找。

    kallsyms_lookup()最終返回字符串“函數(shù)名+offset/size[mod]”,交給printk打印。

    關(guān)于內(nèi)核符號(hào)表kallsyms_names可參考我的另一篇文章點(diǎn)擊打開鏈接。

    實(shí)現(xiàn)應(yīng)用程序中的dump_stack()

    按照如上所述,實(shí)現(xiàn)一個(gè)用戶態(tài)程序的dump_stack好像不是什么難事,因?yàn)樯厦嬲f的步驟在用戶態(tài)都可以完成,程序運(yùn)行的方式也基本上是相同的。

    那我們實(shí)現(xiàn)一個(gè)dump_stack需要做的事情只有兩點(diǎn):

    1.???獲得程序當(dāng)前運(yùn)行時(shí)間點(diǎn)的pc值和棧指針sp。這樣就可以得到每個(gè)函數(shù)棧中的返回地址。

    2.???構(gòu)造和內(nèi)核符號(hào)表相同的應(yīng)用程序符號(hào)表。

    需要注意,不同用戶進(jìn)程都擁有自己的虛擬地址空間,所以棧回溯只能在本進(jìn)程中完成。

    具體實(shí)現(xiàn)當(dāng)然也是體系結(jié)構(gòu)相關(guān)的。既然原理都知道了,那我就直接給出代碼供參考(mips的)。代碼見https://github.com/castoz/backtrace。

    其中backtrace.c實(shí)現(xiàn)了棧回溯,uallsyms.c用于生成符號(hào)表,main.c中為測(cè)試代碼。
    backtrace.c中提供了兩個(gè)接口供其他文件調(diào)用:
    show_backtrace():打印函數(shù)的回溯信息。
    addr_to_name(addr):打印addr對(duì)應(yīng)的函數(shù)名。
    uallsyms.c文件直接使用內(nèi)核中的scripts/kallsyms.c,只需要做少量修改,具體的改動(dòng)為:
    1. 符號(hào)基準(zhǔn)地址改為__start。
    2. 需要記錄的符號(hào)范圍改為在_init到_fini之間或_init到_end之間。
    3. 維護(hù)uallsyms_addresses、uallsyms_num_syms和uallsyms_names三個(gè)全局變量,不使用壓縮算法,所以不需要其他三個(gè)全局變量。
    4. 在生成的匯編代碼中刪除"#include <asm/types.h>"一行,因?yàn)樵诰幾g時(shí)不需要。

    測(cè)試文件main.c的內(nèi)容:

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?"backtrace.h"??
  • ??
  • int?func2(int?a,?int?b);??
  • int?func1(int?a,?int?b);??
  • int?func0(int?a,?int?b);??
  • ??
  • int?func2(int?a,?int?b)??
  • {??
  • ????int?c?=?a?*?b;??
  • ????printf("%s:?c?=?%d\n",?__FUNCTION__,?c);??
  • ????show_backtrace();??
  • ????return?c;??
  • }??
  • ??
  • int?func1(int?a,?int?b)??
  • {??
  • ????int?c?=?func2(a,?b);??
  • ????printf("%s:?c?=?%d\n",?__FUNCTION__,?c);??
  • ????return?c;??
  • }??
  • ??
  • int?func0(int?a,?int?b)??
  • {??
  • ????int?c?=?func1(a,?b);??
  • ????printf("%s:?c?=?%d\n",?__FUNCTION__,?c);??
  • ????return?c;??
  • }??
  • ??
  • int?main()??
  • {??
  • ????int?a?=?4,?b?=?5;??
  • ????int?(*funcptr)(int,?int)?=?func0;??
  • ??????
  • ????int?c?=?func0(a,?b);??
  • ????printf("%s:?c?=?%d\n",?__FUNCTION__,?c);??
  • ??????
  • ????printf("funcptr's?name?=?%s\n",?addr_to_name((unsigned?long)funcptr));??
  • ????return?0;??
  • }??
  • 執(zhí)行make all生成可執(zhí)行文件testbt,放到mips的系統(tǒng)上運(yùn)行。
    運(yùn)行結(jié)果:
    [plain]?view plaincopy
  • root@openwrt:/tmp#?./testbt??
  • func2:?c?=?20??
  • 4362??
  • Call?trace:??
  • ????????=>show_backtrace()+0x20??
  • ????????=>func2()+0x34??
  • ????????=>func1()+0x10??
  • ????????=>func0()+0x10??
  • ????????=>main()+0x14??
  • ??
  • ??
  • func1:?c?=?20??
  • func0:?c?=?20??
  • main:?c?=?20??
  • funcptr's?name?=?func0 ?
  • 參考自:http://blog.csdn.net/jasonchen_gbd/article/details/44066815

    在內(nèi)核中維護(hù)者一張符號(hào)表,記錄了內(nèi)核中所有的符號(hào)(函數(shù)、全局變量等)的地址以及名字,這個(gè)符號(hào)表被嵌入到內(nèi)核鏡像中,使得內(nèi)核可以在運(yùn)行過程中隨時(shí)獲得一個(gè)符號(hào)地址對(duì)應(yīng)的符號(hào)名。而內(nèi)核代碼中可以通過?printk("%pS\n", addr)?打印符號(hào)名。

    本文介紹內(nèi)核符號(hào)表的生成和查找過程。

    1. System.map和/proc/kallsyms

    System.map文件是編譯內(nèi)核時(shí)生成的,它記錄了內(nèi)核中的符號(hào)列表,以及符號(hào)在內(nèi)存中的虛擬地址。這個(gè)文件通過nm命令生成,具體可參考內(nèi)核目錄下的scripts/mksysmap腳本。System.map中每個(gè)條目由三部分組成,例如:

    f0081e80 T alloc_vfsmnt

    即“地址? 符號(hào)類型? 符號(hào)名”

    其中符號(hào)類型有如下幾種:

    • ? A =Absolute
    • ? B =Uninitialised data (.bss)
    • ? C = Comonsymbol
    • ? D =Initialised data
    • ? G =Initialised data for small objects
    • ? I = Indirectreference to another symbol
    • ? N =Debugging symbol
    • ? R = Readonly
    • ? S =Uninitialised data for small objects
    • ? T = Textcode symbol
    • ? U =Undefined symbol
    • ? V = Weaksymbol
    • ? W = Weaksymbol
    • ?Corresponding small letters are local symbols

    /proc/kallsyms文件是在內(nèi)核啟動(dòng)后生成的,位于文件系統(tǒng)的/proc目錄下,實(shí)現(xiàn)代碼見kernel/kallsyms.c。前提是內(nèi)核必須打開CONFIG_KALLSYMS編譯選項(xiàng)。它和System.map的區(qū)別是它同時(shí)包含了內(nèi)核模塊的符號(hào)列表。

    通常情況下我們只需要_stext~_etext_sinittext~_einittext之間的符號(hào),如果需要將nm命令獲得的所有符號(hào)都記錄下來,則需要開啟內(nèi)核的CONFIG_KALLSYMS_ALL編譯選項(xiàng),不過一般是不需要打開的。

    2. 內(nèi)核符號(hào)表

    內(nèi)核在執(zhí)行過程中,可能需要獲得一個(gè)地址所在的函數(shù),比如在輸出某些調(diào)試信息的時(shí)候。一個(gè)典型的例子就是使用dump_stack()函數(shù)打印棧回溯信息。

    但是內(nèi)核在查找一個(gè)地址對(duì)應(yīng)的函數(shù)名時(shí),沒有求助于上述兩個(gè)文件,而是在編譯內(nèi)核時(shí),向vmlinux嵌入了一個(gè)符號(hào)表,這樣做可能是為了方便快速的查找并避免文件操作帶來的不良影響。

    2.1 內(nèi)核符號(hào)表的結(jié)構(gòu)

    內(nèi)嵌的符號(hào)表是通過內(nèi)核目錄下的scripts/kallsyms工具生成的,工具的源碼為相同目錄下的kallsyms.c。這個(gè)工具的用法如下:

    [plain]?view plaincopy
  • nm?-n?vmlinux?|?scripts/kallsyms?[--all-symbols]?>?symbols.S??
  • 可見同樣是通過nm命令得到vmlinux的符號(hào)表,并將這些符號(hào)表信息進(jìn)行調(diào)整,最終生成一個(gè)匯編文件。這個(gè)匯編文件中包含了6個(gè)全局變量:kallsyms_addresses,kallsyms_num_syms,kallsyms_names,kallsyms_markers,kallsyms_token_tablekallsyms_token_index,其中:

    • kallsyms_addresses:一個(gè)數(shù)組,存放所有符號(hào)的地址列表,按地址升序排列。
    • kallsyms_num_syms:符號(hào)的數(shù)量。
    • kallsyms_names:一個(gè)數(shù)組,存放所有符號(hào)的名稱,和kallsyms_addresses一一對(duì)應(yīng)。

    其他三個(gè)全局變量的含義后續(xù)會(huì)提到。

    這些變量被嵌入在vmlinux中,所以在內(nèi)核代碼中直接extern就可以使用。例如dump_stack()就是通過這些變量來查找一個(gè)地址對(duì)應(yīng)的函數(shù)名的。

    那由scripts/kallsyms生成的匯編文件是如何嵌入到vmlinux中的呢。在編譯內(nèi)核的后期主要進(jìn)行了一下幾步額外的編譯和鏈接過程:

  • ? 鏈接器ld將內(nèi)核的絕大部分組件鏈接成臨時(shí)內(nèi)核映像.tmp_vmlinux1。
  • ? 使用nm命令將.tmp_vmlinux1中符號(hào)和相對(duì)的地址導(dǎo)出來,并使用kallsyms工具生成tmp_kallsyms1.S的文件。
  • ? 對(duì).tmp_kallsyms1.S文件進(jìn)行編譯生成.tmp_kallsyms1.o文件。
  • ? 重復(fù)1的鏈接過程,這次將步驟3得到的.tmp_kallsyms1.o文件鏈接進(jìn)入內(nèi)核得到臨時(shí)內(nèi)核映像.tmp_vmlinux2文件,其中包含的部分函數(shù)和非棧變量的地址發(fā)生了變化,但但由于.tmp_kallsyms1.S中的符號(hào)表還是舊的,所以.tmp_vmlinux2還不能作為最終的內(nèi)核映像。
  • ? 再使用nm命令將.tmp_vmlinux2中符號(hào)和相對(duì)的地址導(dǎo)出來,并使用kallsyms工具生成tmp_kallsyms2.S的文件。
  • ? 對(duì).tmp_kallsyms2.S文件進(jìn)行編譯生成.tmp_kallsyms2.o文件。
  • ? .tmp_kallsyms2.o即為最終的kallsyms.o目標(biāo),并鏈接進(jìn)入內(nèi)核生成vmlinux文件。
  • 此時(shí),上面的那6個(gè)全局變量被寫進(jìn)vmlinux中的“.rodata”段(所以還是叫全局常量吧),內(nèi)核代碼就可以使用了,使用前需extern一下:

    [cpp]?view plaincopy
  • extern?const?unsigned?long?kallsyms_addresses[]?__attribute__((weak));??
  • weak屬性表示當(dāng)我們不確定外部模塊是否提供了某個(gè)變量或函數(shù)時(shí),可以將這個(gè)變量或函數(shù)定義為弱屬性,如果外部有定義則使用,沒有定義則相當(dāng)于自己定義。

    在使用這6個(gè)全局常量之前,我們先要弄清楚他們都是干什么用的。kallsyms_addresses、kallsyms_num_syms和kallsyms_names在前面已經(jīng)講過,實(shí)際上他們已經(jīng)可以提供一個(gè)[地址 : 符號(hào)]的映射關(guān)系了,但是內(nèi)核中幾萬(wàn)個(gè)符號(hào)這樣一條一條的存起來會(huì)占用大量的空間,所以內(nèi)核采用一種壓縮算法,將所有符號(hào)中出現(xiàn)頻率較高的字符串記錄成一個(gè)個(gè)的token,然后將原來的符號(hào)中和token匹配的子串進(jìn)行壓縮,這樣可以實(shí)現(xiàn)使用一個(gè)字符來代替n個(gè)字符,以減小符號(hào)存儲(chǔ)長(zhǎng)度。

    因此符號(hào)表維護(hù)了一個(gè)kallsyms_token_table,他有256個(gè)元素,對(duì)應(yīng)一個(gè)字節(jié)的長(zhǎng)度。由于符號(hào)名的只能出現(xiàn)下劃線、數(shù)字和字母,那在kallsyms_token_table[256]數(shù)組中,除了這些字符的ASCII碼對(duì)應(yīng)的位置,還有很多未被使用的位置就可以用來存儲(chǔ)壓縮串。kallsyms_token_table表的內(nèi)容像下面這樣:

    [cpp]?view plaincopy
  • kallsyms_token_table:??
  • ???.asciz??"end"??
  • ???.asciz??"Tjffs2"??
  • ???.asciz??"map_"??
  • ???.asciz??"int"??
  • ???.asciz??"to_"??
  • ???.asciz??"Tn"??
  • .asciz??"t__"??
  • .asciz??"unregist"??
  • ...?...??
  • .asciz??"a"??
  • .asciz??"b"??
  • .asciz??"c"??
  • .asciz??"d"??
  • .asciz??"e"??
  • .asciz??"f"??
  • .asciz??"g"??
  • .asciz??"h"??
  • ...?...??
  • 那我們?cè)诒硎疽粋€(gè)函數(shù)名時(shí),就可以用0x00來表示“end”,用0x04來表示“to_”等。沒有被壓縮的如0x61仍然表示“a”。

    kallsyms_token_index記錄每個(gè)token首字符在kallsyms_token_table中的偏移。同token table共256條,在打印token時(shí)需要用到。

    [cpp]?view plaincopy
  • kallsyms_token_index:??
  • ???.short??0??
  • ???.short??4?????//Tjffs2第一個(gè)字符在kallsyms_token_table中的偏移??
  • ???.short??11??
  • ???.short??16??
  • 至于kallsyms_token_table表是如何生成的,可以閱讀scripts/kallsyms.c的實(shí)現(xiàn),大致就是將所有符號(hào)出現(xiàn)的相鄰的兩個(gè)字符出現(xiàn)的次數(shù)都記錄起來,例如對(duì)于“nf_nat_nf_init”,就記錄下“nf”、“f_”、“_n”、“na”、……,每?jī)蓚€(gè)字符組合出現(xiàn)的次數(shù)記錄在token_profit[0x10000]數(shù)組中(兩個(gè)字符一組,共有2^8 * 2^8 = 0x10000中可能組合),然后挑選出現(xiàn)次數(shù)最多的一個(gè)組合形成一個(gè)token,比如用“g”來表示“nf”,那“nf_nat_nf_init”就被改為“g_nat_g_init”。接下來,再在修改后的所有符號(hào)中計(jì)算每?jī)蓚€(gè)字符的出現(xiàn)次數(shù)來挑選出現(xiàn)次數(shù)最多的組合,例如用“J”來表示“g_”,那“g_nat_g_init”又被改為“Jnat_Jinit”。直到生成最終的token表。

    2.2 內(nèi)核查找一個(gè)符號(hào)的過程

    這時(shí)還沒講到全局常量kallsyms_markers。我們先來看內(nèi)核如何根據(jù)這六個(gè)全局常量來查找一個(gè)地址對(duì)應(yīng)的函數(shù)名的,實(shí)現(xiàn)函數(shù)為kernel/kallsyms.c中的kallsyms_lookup()。

    我不講函數(shù)實(shí)現(xiàn),只是用一個(gè)例子來說明內(nèi)核符號(hào)的查找過程:

    比如我在內(nèi)核中想打印出0x80216bf4地址所在的函數(shù)。首先不管內(nèi)核怎么做,我們可以先在System.map文件中看到這個(gè)地址位于為nf_register_hook和nf_register_hooks兩個(gè)符號(hào)之間,那可以確定它屬于nf_register_hook函數(shù)了。

    80060000 A?_text

    ... ...

    80216b8c T nf_unregister_hooks

    80216be4 T nf_register_hook

    80216c8c T nf_register_hooks

    ... ...

    注意,System.map和內(nèi)核啟動(dòng)后的/proc/kallsyms文件中的符號(hào)表只是給我們看的,內(nèi)核不會(huì)使用它們。

    在由script/kallsyms工具生成的.tmp_kallsyms2.S文件中,kallsyms_addresses數(shù)組存放著所有符號(hào)的地址,并且是按照地址升序排列的,所以通過二分查找可以定位到0x80216bf4所在函數(shù)的起始地址是下面的這個(gè)條目:

    kallsyms_addresses:

    ?? ... ...

    ??? PTR?_text?+ 0x1b6be4

    ?? ... ...

    而這一項(xiàng)在kallsyms_addresses中的index為8801,所以現(xiàn)在需要找到kallsyms_names中的第8801個(gè)符號(hào)。

    我們這時(shí)實(shí)際上可以在kallsyms_names進(jìn)行查找了,怎么找呢?我們先看一下kallsyms_names大致的樣子:

    [cpp]?view plaincopy
  • kallsyms_names:??
  • ????.byte0x04,?0x54,?0x7e,?0xc3,?0x74??
  • ????.byte0x08,?0xa0,?0x6b,?0xfa,?0xda,?0xbc,?0xe4,?0xe2,?0x79??
  • ????.byte0x09,?0xa0,?0x69,?0xd6,?0x93,?0x63,?0x6d,?0x64,?0xa5,?0x65??
  • ????.byte0x09,?0x54,?0xaa,?0x5f,?0xec,?0xfe,?0xc2,?0x63,?0xe7,?0x6c??
  • ????.byte0x09,?0x1a,?0x0d,?0x5f,?0xe3,?0xb2,?0xd3,?0x75,?0x75,?0xa4??
  • ????.byte0x07,?0x05,?0x61,?0x6d,?0xfe,?0x04,?0x95,?0x74??
  • ?????...?...??
  • 其中每一行存儲(chǔ)一個(gè)壓縮后的符號(hào),而index和kallsyms_addresses中的index是一一對(duì)應(yīng)的。每一行的內(nèi)容分為兩部分:第一個(gè)byte指明符號(hào)的長(zhǎng)度,后續(xù)才是符號(hào)自身。雖然我們這里看到的符號(hào)是一行一行分開的,但實(shí)際上kallsyms_names是一個(gè)unsigned char的數(shù)組,所以想要找第8801個(gè)符號(hào),只能這樣來找:

    1. 從第一個(gè)字節(jié)開始,獲得第一個(gè)符號(hào)的長(zhǎng)度len;

    2. 向后移len+1個(gè)字節(jié),就達(dá)到第二個(gè)符號(hào)的長(zhǎng)度字節(jié),這時(shí)記錄下已經(jīng)走過的總長(zhǎng)度;

    3. 重復(fù)前兩步的動(dòng)作,直到走過的總長(zhǎng)度為8801。

    這樣找的話,要找到kallsyms_names的第8801個(gè)符號(hào)就要移動(dòng)8801次,那如果要尋找最后一個(gè)符號(hào),就要移動(dòng)更多次,時(shí)間耗費(fèi)較多,所以內(nèi)核通過一個(gè)kallsyms_markers數(shù)組進(jìn)行查找。

    將kallsyms_names每256個(gè)符號(hào)分為一組,每一組的第一個(gè)字符的位置記錄在kallsyms_markers中,這樣,我們?cè)谡襨allsyms_names中的某個(gè)條目時(shí),可以快速定義到它位于那個(gè)組,然后再在組內(nèi)尋找,組內(nèi)移動(dòng)次數(shù)最多為255次。

    所以我們先通過(8801 >> 8)得到了要找的符號(hào)位于第34組,

    我們看到kallsyms_markers的第34項(xiàng)為:

    ??? PTR 91280

    這個(gè)值指明了kallsyms_names中第34組的起始字符的偏移,所以我們直接找到kallsyms_names[91280]位置,即是第34組所有符號(hào)的第一個(gè)字節(jié)。同時(shí)我們可以通過(8801 && 0xFF)得到要找的符號(hào)在第34組組內(nèi)的序號(hào)為97,即第97個(gè)符號(hào)。

    接下來尋找第97個(gè)符號(hào)就只能通過上面講到的方法了。

    通過上面一系列的查找,我們定位到第34組中第97個(gè)符號(hào)如下:

    .byte 0x08, 0x05, 0x66, 0xdc, 0xb6, 0xc8, 0x68, 0x6f,0x0b

    這個(gè)是壓縮后的符號(hào),第一個(gè)字節(jié)0x08是符號(hào)長(zhǎng)度,所以我們接下來的任務(wù)就剩下解壓了。

    每個(gè)字節(jié)解壓后對(duì)應(yīng)的字符串在kallsyms_token_table中可以找到。于是在kallsyms_token_table表中尋找第5(0x05)項(xiàng)、第5(0x05)項(xiàng)、第102(0x66)項(xiàng)、……、第11(0x0b)項(xiàng),得到的結(jié)果分別為:

    "Tn", "f", "_re","gist", "er_", "h", "o", "ok"

    由于在壓縮的時(shí)候?qū)⒎?hào)類型“T”也壓進(jìn)去了,所以要去掉第一個(gè)字符,至此就獲得了0x80216bf4地址所在的函數(shù)為nf_register_hook。

    參考自:http://blog.csdn.net/jasonchen_gbd/article/details/44025681

    3. 內(nèi)核模塊的符號(hào)

    內(nèi)核模塊是在內(nèi)核啟動(dòng)過程中動(dòng)態(tài)加載到內(nèi)核中的,所以,不能試圖將模塊中的符號(hào)嵌入到vmlinux中。加載模塊時(shí),模塊的符號(hào)表被存放在該模塊的struct module結(jié)構(gòu)中。所有已加載的模塊的structmodule結(jié)構(gòu)都放在一個(gè)全局鏈表中。

    在查找一個(gè)內(nèi)核模塊的符號(hào)時(shí),調(diào)用的函數(shù)依然是kallsyms_lookup(),模塊符號(hào)的實(shí)際查找工作在get_ksymbol()函數(shù)中完成。

    附錄:一個(gè).tmp_kallsyms2.S文件

    [cpp]?view plaincopy
  • #include?<asm/types.h>??
  • #if?BITS_PER_LONG?==?64??
  • #define?PTR?.quad??
  • #define?ALGN?.align?8??
  • #else??
  • #define?PTR?.long??
  • #define?ALGN?.align?4??
  • #endif??
  • ???.section.rodata,?"a"??
  • .globl?kallsyms_addresses??
  • ???ALGN??
  • kallsyms_addresses:??
  • ???PTR?_text?+?0x400??
  • ???PTR?_text?+?0x400??
  • ???PTR?_text?+?0x410??
  • ???PTR?_text?+?0x810??
  • ???PTR?_text?+?0x9e0??
  • ???PTR?_text?+?0xa14??
  • ???PTR?_text?+?0xea0??
  • ???PTR?_text?+?0xec4??
  • ???PTR?_text?+?0xf00??
  • ???PTR?_text?+?0xf10??
  • ???...?...??
  • ????
  • .globl?kallsyms_num_syms??
  • ???ALGN??
  • kallsyms_num_syms:??
  • ???PTR?11132??
  • ???
  • .globl?kallsyms_names??
  • ???ALGN??
  • kallsyms_names:??
  • ???.byte?0x04,0x54,?0x7e,?0xc3,?0x74??
  • ???.byte?0x08,0xa0,?0x6b,?0xfa,?0xda,?0xbc,?0xe4,?0xe2,?0x79??
  • ???.byte?0x09,0xa0,?0x69,?0xd6,?0x93,?0x63,?0x6d,?0x64,?0xa5,?0x65??
  • ???.byte?0x09,0x54,?0xaa,?0x5f,?0xec,?0xfe,?0xc2,?0x63,?0xe7,?0x6c??
  • ???.byte?0x09,0x1a,?0x0d,?0x5f,?0xe3,?0xb2,?0xd3,?0x75,?0x75,?0xa4??
  • ???.byte?0x07,0x05,?0x61,?0x6d,?0xfe,?0x04,?0x95,?0x74??
  • ???.byte?0x09,0x74,?0xf6,?0x68,?0x37,?0x39,?0x5f,?0x68,?0xe7,?0x74??
  • ???.byte?0x09,0x74,?0xf6,?0x68,?0x37,?0x39,?0xdc,?0xf1,?0xee,?0x74??
  • ???.byte?0x0b,0x54,?0xc4,?0x73,?0x79,?0xf1,?0x65,?0xcc,?0x74,?0x79,?0x70,?0x65??
  • ???...?...??
  • ????
  • .globl?kallsyms_markers??
  • ???ALGN??
  • kallsyms_markers:??
  • ???PTR?0??
  • ???PTR?2831??
  • ???PTR?5578??
  • ???PTR?8289??
  • ???PTR?10855??
  • ???PTR?13684??
  • ???PTR?16544??
  • ???PTR?19519??
  • ???PTR?22294??
  • ???PTR?25225??
  • ???PTR?27761??
  • ???PTR?30097??
  • ???...?...??
  • ???
  • .globl?kallsyms_token_table??
  • ???ALGN??
  • kallsyms_token_table:??
  • ???.asciz?"end"??
  • ???.asciz?"Tjffs2"??
  • ???.asciz?"map_"??
  • ???.asciz?"int"??
  • ???.asciz?"to_"??
  • ???.asciz?"Tn"??
  • ???.asciz?"t__"??
  • ???.asciz?"unregist"??
  • ???.asciz?"tn"??
  • ???.asciz?"yn"??
  • ???.asciz?"Tf"??
  • ???...?...??
  • ???
  • .globl?kallsyms_token_index??
  • ???ALGN??
  • kallsyms_token_index:??
  • ???.short?0??
  • ???.short?4??
  • ???.short?11??
  • ???.short?16??
  • ???.short?20??
  • ???.short?24??
  • ???.short?27??
  • ???.short?31??
  • ???.short?40??
  • ???...?... ?
  • 總結(jié)

    以上是生活随笔為你收集整理的dump_stack介绍以及内核符号表的生成和查找过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。