1. 程式人生 > >linux核心kallsyms機制分析

linux核心kallsyms機制分析

  1. 轉載自:http://blog.chinaunix.net/uid-27717694-id-3985448.html
  2. 一、前言
  3. Linux核心是一個整體結構,而模組是插入到核心中的外掛。儘管核心不是一個可安裝模組,但為了方便起見,Linux把核心也看作一個模組。那麼模組與模組之間如何進行互動呢,一種常用的方法就是共享變數和函式。但並不是模組中的每個變數和函式都能被共享,核心只把各個模組中主要的變數和函式放在一個特定的區段,這些變數和函式就統稱為符號。
  4. 因此,核心也有一個module結構,叫做kernel_module。另外,從kernel_module開始,所有已安裝模組的module結構都鏈在一起成為一條鏈,核心中的全域性變數module_list就指向這條鏈:
  5. struct module*module_list=&kernel_module;
  6. 一般來說,核心只會匯出由EXPORT_PARM巨集指定的符號給模組使用。為了使debugger提供更好的除錯功能,需要使用kallsyms工具為核心生成__kallsyms段資料,該段描述所有不處在堆疊上的核心符號。這樣debugger就能更好地解析核心符號,而不僅僅是核心指定匯出的符號。
  7. 二、簡介
  8. 在v2.6.0 的核心中,為了更好地除錯核心,引入新的功能kallsyms.kallsyms把核心用到的所有函式地址和名稱連線進核心檔案,當核心啟動後,同時載入到記憶體中.當發生oops,例如在核心中訪問空地址時,核心就會解析eip位於哪個函式中,並打印出形如:
  9. EIPisat cleanup_module+0xb/0x1d[client]的資訊,
  10. 呼叫棧也用可讀的方式顯示出來.
  11. CallTrace:
  12. [<c013096d>]sys_delete_module+0x191/0x1ce
  13. [<c02dd30a>]do_page_fault+0x189/0x51d
  14. [<c0102bc1>]syscall_call+0x7/0xb
  15. 當然功能不僅僅於此,還可以查詢某個函式例如的sys_fork的地址,然後hook它,kprobe就是這麼幹的。在v2.6.20 中,還可以包含所有符號的地址,應此功能更強大,就相當於核心中有了System.
    map了,此時查詢sys_call_table的地址易如反掌。
  16. .sym的生成
  17. 1.形成過程
  18. Linux核心符號表/proc/kallsyms的形成過程
  19. (1)./scripts/kallsyms.c負責生成System.map
  20. (2)./kernel/kallsyms.c負責生成/proc/kallsyms
  21. (3)./scripts/kallsyms.c解析vmlinux(.tmp_vmlinux)生成kallsyms.S(.tmp_kallsyms.S),然後核心編譯過程中將kallsyms.S(核心符號表)編入核心映象uImage.核心啟動後./kernel/kallsyms.c解析uImage形成/proc/kallsyms
  22. 2.核心配置
  23. 在2.6 核心中,為了更好地除錯核心,引入了kallsyms。kallsyms抽取了核心用到的所有函式地址(全域性的、靜態的)和非棧資料變數地址,生成一個數據塊,作為只讀資料鏈接進kernel image,相當於核心中存了一個System.map。需要配置CONFIG_KALLSYMS。
  24. .config
  25. CONFIG_KALLSYMS=y 符號表中包含所有的函式
  26. CONFIG_KALLSYMS_ALL=y 符號表中包括所有的變數(包括沒有用EXPORT_SYMBOL匯出的變數)
  27. CONFIG_KALLSYMS_EXTRA_PASS=y
  28. make menuconfig
  29. General setup--->
  30.     [*]Configure standard kernel features(forsmall systems)--->
  31.         [*]Load all symbolsfordebugging/ksymoops (選中此項,才有/proc/kallsyms介面檔案,oops問題,選中此選項即可,子選項可以忽略)
  32.               [*]Include all symbolsinkallsyms
  33.               [*]Doan extra kallsyms pass
  34. 3.編譯生成列表
  35. 核心編譯的最後階段,make會執行
  36. nm-n vmlinux|scripts/kallsyms
  37. nm-n vmlinux生成所有的核心符號,並按地址排序,形如
  38. ......
  39. c0100000 T startup_32
  40. c0100000 A _text
  41. c01000c6 t checkCPUtype
  42. c0100147 t is486
  43. c010014e t is386
  44. c010019f t L6
  45. c01001a1 t check_x87
  46. c01001ca t setup_idt
  47. c01001e7 t rp_sidt
  48. c01001f4 t ignore_int
  49. c0100228 T calibrate_delay
  50. c0100228 T stext
  51. c0100228 T _stext
  52. c010036b t rest_init
  53. c0100410 t do_pre_smp_initcalls
  54. c0100415 t run_init_process
  55. ......
  56. v2.6.0 的行數是2.5萬左右
  57. 4.處理列表
  58. scripts/kallsyms則處理這個列表,並生成連線所需的S檔案kallsyms.S。在linux3.12中使用/scripts/kallsyms處理此列表。v2.6.0中形如:
  59. #include<asm/types.h>
  60. #ifBITS_PER_LONG==64
  61. #define PTR.quad
  62. #define ALGN.align 8
  63. #else
  64. #define PTR.long
  65. #define ALGN.align 4
  66. #endif
  67. .data
  68. .globl kallsyms_addresses
  69.         ALGN
  70. kallsyms_addresses:
  71.         PTR 0xc0100228
  72.         PTR 0xc010036b
  73.         PTR 0xc0100410
  74.         PTR 0xc0100415
  75.         PTR 0xc010043c
  76.         PTR 0xc0100614
  77. ...
  78. .globl kallsyms_num_syms
  79.         ALGN
  80. kallsyms_num_syms:
  81.         PTR 11228
  82. .globl kallsyms_names
  83.         ALGN
  84. kallsyms_names:
  85.         .byte 0x00
  86.         .asciz"calibrate_delay"
  87.         .byte 0x00
  88.         .asciz"stext"
  89.         .byte 0x00
  90.         .asciz"_stext"
  91. ...
  92. 生成的符號表部分如下:
  93. /*
  94.     ......
  95.     c1618b03 t __raw_write_unlock_irq.constprop.29
  96.     c1618b19 T panic
  97.     c1618c91 T printk
  98.     ......
  99.     c16a4d6b r __func__.17404
  100.     c16a4d78 R kallsyms_addresses
  101.     c16ef0dc R kallsyms_num_syms
  102.     c16ef0e0 R kallsyms_names
  103.     c17d5468 R kallsyms_markers
  104.     c17d590c R kallsyms_token_table
  105.     c17d5c78 R kallsyms_token_index    
  106.     ......
  107. */
  108. 5.生成的符號陣列解析
  109. 1)kallsyms_addresses陣列包含所有核心函式的地址(經過排序的),v2.6.0 中相同的地址在kallsyms_addresses中只允許出現一次,到後面的版本例如相同的地址可以出現多次,這樣就允許同地址函式名的出現。
  110. 例如:
  111. kallsyms_addresses:
  112. PTR 0xc0100228
  113. PTR 0xc0100228
  114. PTR 0xc0100228
  115. PTR 0xc010036b
  116. 當查詢某個地址時所在的函式時,v2.6.0 採用的是線性法,從頭到尾地找,很低效,後來改成了折半查詢,效率好多了。
  117. 2)kallsyms_num_syms是函式個數
  118. 3)kallsyms_names是函式名陣列。
  119. <1>以前的演算法是:函式名陣列組成的一個大串,這個大串是有許多小串組成,格式是:
  120. .bytelen
  121. .asciz 壓縮串
  122. 格式例如:
  123. kallsyms_names:
  124. .byte 0x00
  125. .asciz"calibrate_delay"
  126. .byte 0x00
  127. .asciz"stext"
  128. .byte 0x00
  129. .asciz"_stext"
  130. .byte 0x00
  131. .asciz"rest_init"
  132. len代表本函式名和前一函式名相同字首的大小,例如
  133. .byte 0x00
  134. .asciz"early_param_test"
  135. .byte 0x06
  136. .asciz"setup_test"
  137. .byte 0x06,說明串setup_test和串early_parm_test有著相同的字首,長為6,即early_,所有setup_test最終解壓後的函式名為early_setup_test.由於沒有其他的輔助手段,函式名的解析過程也很低效,從頭一直解析到該函式位置為止。
  138. <2>在後來的版本中,演算法有了改善,使用了偏移索引和高頻字串壓縮。也就是現在常用的演算法。格式是:
  139. .bytelenascii字元 ascii字元...(len個ascii字元)
  140. 先建立token的概念,token就是所有函式名中,出現頻率非常高的那些字串.由於識別符號命名
  141. 規則的限制,有許多ascii字元是未用到的,那麼,可以用這些字元去替代這些高頻串。例如下面的例子:
  142. 字元值 字元代表的串
  143. 190.asciz"t.text.lock."
  144. 191.asciz"text.lock."
  145. 192.asciz"t.lock."
  146. 193.asciz"lock."
  147. 210.asciz"tex"
  148. 229.asciz"t."
  149. 239.asciz"loc"
  150. 249.asciz"oc"
  151. 250.asciz"te"
  152. 例如串.byte 0x03,0xbe,0xbc,0x71的解析
  153. 串長3,
  154. 0xbe(190).asciz"t.text.lock."
  155. 0xbc(189).asciz"ir"
  156. 0x71(113).asciz"q"
  157. 所以該串解析後的值是 t.text.lock.irq,注意實際的串值是.text.lock.irq,前面的t是型別,這是新版本加入的功能,將型別字元放在符號前。
  158. .byte 0x02,0x08,0xc2
  159. 串長2,
  160. 0x08,8.asciz"Tide_"
  161. 0xc2,194.asciz"init"
  162. 所以該串解析後的值是 Tide_init,即ide_init
  163. 4)為了解析而設定了資料結構kallsyms_token_table和kallsyms_token_index.結構kallsyms_token_table記錄每個ascii字元的替代串,kallsyms_token_index記錄每個ascii字元的替代串在kallsyms_token_table中的偏移.
  164. 5)而資料結構的改變是,把函式名每256個分一組,用一個數組kallsyms_markers記錄這些組在
  165. kallsyms_names中的偏移,這樣查詢就方便多了,不必從頭來。
  166. 四、符號表的查詢
  167. 通過對以上格式的瞭解,我們就可以自己編寫程式找到核心中的符號表,進而找到每個核心符號的地址。
  168. 1.首先找到kallsyms_addresses陣列
  169. //首先獲得kallsyms_addresses陣列中的printk的地址
  170. printk_addr_addr=prink_addr_addr();
  171. static void*prink_addr_addr(void){
  172.     unsignedinti=0xc0000000;
  173.     intk=0;
  174.     //kallsyms_addresses陣列中都是儲存的是核心符號的地址,所以這裡查詢的是地址下的儲存的函式地址是否為printk的函式地址
  175.     for(;i<0xf0000000;i+=4){
  176.         if(*((unsignedint*)i)==(unsignedint)printk){
  177.             //判斷該地址前邊都是有效的kallsyms_addresses陣列函式地址        
  178.             if(isfunaddr(*((unsignedint*)(i-4)))&&isfunaddr(*((unsignedint*)(i-8)))){
  179.                 if(!k)
  180.                     return(void*)i;
  181.                 else
  182.                     ++k;
  183.             }
  184.         }        
  185.     }
  186.     returnNULL;
  187. }
  188. //只要該函式符號在kallsyms_addresses陣列中,通過%Ps列印結構一定是...+0x0/...
  189. staticintisfunaddr(unsignedintaddr){
  190.     char buff[200]=

    相關推薦

    linux核心kallsyms機制分析

    轉載自:http://blog.chinaunix.net/uid-27717694-id-3985448.html 一、前言 Linux核心是一個整體結構,而模組是插入到核心中的外掛。儘管核心不是一個可安裝模組,但為了方便起見,Linux把核心也看作一個模組。那麼模組與模組之間如何進行互

    linux RCU鎖機制分析

    nbsp -i html 都在 而且 content 服務器 單詞 插入 openVswitch(OVS)源代碼之linux RCU鎖機制分析 分類: linux內核 | 標簽: 雲計算,openVswitch,linux內核,RCU鎖機制 | 作者: yuzh

    Linux之poll機制分析

    for 可用 報告 超時時間 程序 訪問 events blank linux 應用程序訪問1個設備文件時可用阻塞/非阻塞方式.如果是使用阻塞方式,則直接調用open()、read()、write(),但是在驅動程序層會判斷是否可讀/可寫,如果不可讀/不可寫,則將當前進程休

    2018-2019-1 20189206 《Linux核心原理與分析》第二週作業

    Linux核心分析 第二週學習 知識總結 作業系統與核心 作業系統 指在整個系統中負責完成最基本功能和系統管理的那些部分 核心 實際是作業系統的內在核心 核心獨立於普通應用程式,擁有受保護的記憶體空間和訪問硬體裝置的所有許可權,這種空間被稱為核心空間 當核心執行的時

    2018-2019-1 20189219《Linux核心原理與分析》第四周作業

    1. 首先設定斷點在start_kernel函式處,使用c命令之後提示進入了該啟動函式,如圖: 圖 2. 進入函式之後發現這裡面的大多數函式並不能從名字上看出它們的意義,只能一步一步的試,於是我在init_task這個重要的程序變數處設定斷點b 510,然後c,發現menuOS竟然開始跑了。但是結果卻不盡

    2018-2019-1 20189221《Linux核心原理與分析》第四周作業

    2018-2019-1 20189221《Linux核心原理與分析》第四周作業 教材學習:《庖丁解牛Linux核心分析》 第 3 章 MenuOS的構造 計算機三大法寶:儲存程式計算機,函式呼叫堆疊,中斷 作業系統兩把寶劍:中斷上下文,程序上下文 Linux核心原始碼: Linux核心使用的是第二週

    2018-2019-1 20189203《Linux核心原理與分析》第四周作業

    第一部分 課本學習 核心版本號:Linux核心自2013年12月起,就以A.B.C.D的方式命名。A和B變得無關緊要,C是核心的真實版本。每一個版本的變化都會帶來新的特性,如內部API的變化等,改動的程式碼數量常常上萬行。D是安全補丁和bug修復。 幾個關鍵的目錄: Arch:與體系結構相關的子目

    Linux核心原理與分析》第四周作業

    課本:第3章 MenuOS的構造 內容總結 計算機的“三大法寶” 儲存程式計算機 函式呼叫堆疊 中斷 作業系統的“兩把寶劍” 中斷上下文切換:儲存現場和恢復現場 程序上下文切換 在接觸linux核心原始碼時,linux是基於一個穩定版

    2018-2019-1 20189206 《Linux核心原理與分析》第四周作業

    linux核心分析學習筆記 ——第三章 MenuOS的構造 計算機的“三大法寶”和作業系統的“兩把寶劍” 三大法寶 程式儲存計算機 即馮諾依曼體系結構,基本上是所有計算機的基礎性的邏輯框架 函式呼叫堆疊 高階語言可以執行的起點就是函式呼叫堆疊 中斷機制 中斷上下文 儲存

    2018-2019-1 20189204《Linux核心原理與分析》第四周作業

    《庖丁解牛》第3章——MenuOS的構造 3.1Linux核心原始碼簡介 計算機三大法寶:儲存程式計算機、系統呼叫堆疊、中斷 作業系統兩把寶劍:中斷切換上下文、程序切換上下文 Linux核心原始碼的目錄結構 其中,arch目錄是與體系結構相關的子目錄列表,裡面存放了許多CPU體系結構的相關程式碼,使

    2018-2019-1 20189205 《Linux核心原理與分析》 第四周作業

    MenuOS的構造 Linux核心 本週學習了Linux核心的基本目錄結構,通過qemu構建了簡單的Linux核心,並利用gdb工具進行除錯,瞭解了核心的啟動過程。 Linux的目錄結構 關鍵的目錄 arch:與體系結構相關的子目錄列表。 block:存放Linux儲存體系中關於塊裝置管理的

    2018-2019-1 20189210 《LInux核心原理與分析》第四周作業

    第三章 這一章接觸核心原始碼,對核心原始碼進行編譯和除錯跟蹤 一、預備知識: 核心:整個作業系統的最底層,它負責了整個硬體的驅動以及提供各種系統所需的核心功能。核心實質上是系統上面的一個檔案而已,這個檔案包含了驅動主機各項硬體的檢測程式與驅動模組。當系統讀完BIOS並載入MBR內的引導裝載程式後,就能夠載入核

    2018-2019-1 20189215《Linux核心原理與分析》第二週作業

    本週學習了《庖丁解牛》第1章,以及《Linux核心設計與實現》第1、2、18章。通過視訊和實驗,學會了反彙編一個簡單的C程式,也學習了Linux核心除錯的一些小技巧和printk函式。 反彙編一個簡單的C程式 程式編寫及編譯 使用vi編輯原始碼 返回值是15,我學號的後兩位。 使用

    20189205 《Linux核心原理與分析》第二週作業

    反編譯 在實驗樓中我編寫了如下程式碼: 通過gcc編譯,得到了如下彙編程式碼: 將其簡化為可見部分後可得到如下彙編程式碼: 5 g: 8 pushl %ebp 11 movl %esp,%ebp 13 movl 8(%eb

    20189210牟健 《Linux核心原理與分析》第二週作業

    本週學習了彙編指令以及通過反彙編一個小程式來了解棧的變化 寫了一個簡單的C程式,如圖所示: 通過gcc -s -o main.s main.c -m32指令將其編譯成彙編程式 開啟該彙編檔案並刪除不重要的資訊,如圖所示: 分析該彙編指令(為了方便直接用手寫畫圖,為了區分不同時期的暫存器,將其後面加了個

    20189220 餘超《Linux核心原理與分析》第二週作業

    計算機如何工作的 一.儲存程式計算機工作模型 馮諾依曼體系結構:核心思想為儲存程式計算機。兩個層面: (1)硬體的角度(計算機主機板):一個CPU,一塊記憶體,之間有匯流排連線。CPU內部有一個IP計算器,IP指向記憶體中的指令,並依次加一執行; (2)另一個層面,程式設計師的角度:儲存程式計算機工作模

    2018-2019-1 20189213《Linux核心原理與分析》第五週作業

    第四章:系統呼叫的三層機制(上) 系統呼叫的"三層皮" 分別指的是:使用者態函式(API)、system_call(中斷服務程式入口)以及sys_xyz()系統呼叫處理函式封裝例程。它們各自的作用如下: API 第一層是指Libc中定義的API,這些API封裝了系統呼叫,使用int0x80觸發一個系統

    2018-2019-1 20189218《Linux核心原理與分析》第五週作業

    系統呼叫的三層機制 使用者態、核心態和中斷 使用者態。較低的執行級別,只能訪問一部分記憶體,只能執行一部分指令。 核心態。高階執行級別,可以訪問任意實體記憶體,可以執行特權指令。 中斷。系統從使用者態進入核心態的主要方式。有硬體中斷和軟中斷。系統呼叫就是通過軟中斷進入核心態。 上下文切

    Linux核心原理與分析》第二週作業

    反彙編一個簡單的C程式 1、實驗要求 使用: gcc –S –o test.s test.c -m32 命令編譯成彙編程式碼,對彙編程式碼進行分析總結。其中test.c的具體內容如下: int g(int x) { return x + 3; } int f(int x) { return

    2018-2019-1 20189203《Linux核心原理與分析》第二週作業

    一、本週學習情況 我本週結合《庖丁解牛》教材學習了藍墨雲的視訊課,主要學習內容如下: 1、學習了計算機的工作原理,深入理解了馮諾依曼體系結構。 2、學習了X86-32 CPU的暫存器 3、學習了定址方式和常用匯編指令 - 立即數即常數,如$8,表示$開頭後跟一個數值; - 暫