天天看點

System.map 檔案的作用

System.map檔案的作用  

2012-09-05 23:18:13|  分類: RHCA_442 |字号 訂閱

System.map檔案的作用

有關System.map檔案的資訊好象很缺乏。其實它一點也不神秘,并且在整個事情當中它并不象看上去那麼得重要。但是由于缺乏必要的文檔說明,使其顯得比較神秘。它就象耳垂,我們每個人都有,但卻不知道是幹什麼用的。本網頁就是用來說明這個問題的。

注意,我并不會是百分之一百正确的。例如,一個系統很可能沒有/proc檔案系統支援,但是大多數系統肯定有。這裡我假定你是“随大流的”,并有一個典型配置的系統。

某些有關核心出錯(oops)的闡述來自于Alessandro Rubini的“Linux裝置驅動程式” 一書,我是從其中學到大部分核心程式設計知識的。

什麼是符号(Symbols)?

    在程式設計中,一個符号(symbol)是一個程式的建立塊:它是一個變量名或一個函數名。正如你自己編制的程式一樣,核心具有各種符号也是不應該感到驚奇的。當然,差別在于核心是一非常複雜的代碼塊,并且含有許多、許多的全局符号。

核心符号表(Kernel Symbol Table)是什麼東西?

    核心并不使用符号名。它是通過變量或函數的位址(指針)來使用變量或函數的,而不是使用size_t BytesRead,核心更喜歡使用(例如)c0343f20來引用這個變量。

而另一方面,人們并不喜歡象c0343f20這樣的名字。我們跟喜歡使用象 size_t

BytesRead這樣的表示。通常,這并不會帶來什麼問題。核心主要

是用C語言寫成的,是以在我們程式設計時編譯器/連接配接程式允許我們使用符号名,并且使核心在運作時使用位址表示。這樣大家都滿意了。

    然而,存在一種情況,此時我們需要知道一個符号的位址(或者一個位址對應的符号)。這是通過符号表來做到的,與gdb能夠從一個位址給出函數名(或者給出一個函數名的位址)的情況很相似。符号表是所有符号及其對應位址的一個清單。這裡是一個符号表例子:

   c03441a0 B dmi_broken

   c03441a4 B is_sony_vaio_laptop

   c03441c0 b dmi_ident

   c0344200 b pci_bios_present

   c0344204 b pirq_table

   c0344208 b pirq_router

   c034420c b pirq_router_dev

   c0344220 b ascii_buffer

   c0344224 b ascii_buf_bytes

    你可以看出名稱為dmi_broken的變量位于核心位址c03441a0處。

什麼是System.map檔案?

    有兩個檔案是用作符号表的:

       1. /proc/ksyms

       2. System.map

    這裡,你現在可以知道System.map檔案是幹什麼用的了。

    每當你編譯一個新核心時,各種符号名的位址定會變化。

/proc/ksyms 是一個 "proc檔案" 并且是在核心啟動時建立的。實際上

它不是一個真實的檔案;它隻是核心資料的簡單表示形式,呈現出象一個磁盤檔案似

的。如果你不相信我,那麼就試試找出/proc/ksyms的檔案大小來。是以,對于目前運作的核心來說,它總是正确的..

    然而,System.map卻是檔案系統上的一個真實檔案。當你編譯一個新核心時,你原來的System.map中的符号資訊就不正确了。随着每次核心的編譯,就會産生一個新的 System.map檔案,并且需要用該檔案取代原來的檔案。

什麼是一個Oops?

    在自己編制的程式中最常見的出錯情況是什麼?是段出錯(segment fault),信号11。

Linux核心中最常見的bug是什麼?也是段出錯。除此,正如你想象的那樣,段出

錯的問題是非常複雜的,而且也是非常嚴重的。當核心引用了一個無效指針時,并不稱其為段出錯 --

而被稱為"oops"。一個oops表明核心存在一個bug,應該總是提出報告并修正該bug。

    請注意,一個oops與一個段出錯并不是一回事。你的程式并不能從段出錯中恢複過來,當出現一個oops時,并不意味着核心肯定處于不穩定的狀态。Linux核心是非常健壯的;一個oops可能僅殺死了目前程序,并使餘下的核心處于一個良好的、穩定的狀态。

一個oops并非是核心死循環(panic)。在核心調用了panic()函數後,核心就不能

繼續運作了;此時系統就處于停頓狀态并且必須重新開機。如果系統中關鍵部分遭到破壞

那麼一個oops也可能會導緻核心進入死循環(panic)。例如,裝置驅動程式中出現的oops就幾乎不會導緻系統進行死循環。

    當出現一個oops時,系統就會顯示出用于調試問題的相關資訊,比如所有CPU寄存器中的内容以及頁描述符表的位置等,尤其會象下面那樣列印出EIP(指令指針)的内容:

   EIP: 0010:[]

   Call Trace: []

一個Oops與System.map檔案有什麼關系呢?

    我想你也會認為EIP和Call Trace所給出的資訊并不多,但是重要的是,對于核心開發人員來說這些資訊也是不夠的。由于一個符号并沒有固定的位址, c010b860可以指向任何地方。

為了幫助我們使用oops含糊的輸出,Linux使用了一個稱為klogd(核心日志背景程式)的

背景程式,klogd會截取核心oops并且使用syslogd将其記錄下來,并将某些象c010b860

的資訊轉換成我們可以識别和使用的資訊。換句話說,klogd是一個核心消息記錄器(logger),

它可以進行名字-位址之間的解析。一旦klogd開始轉換核心消息,它就使用手頭的記錄器,

将整個系統的消息記錄下來,通常是使用syslogd記錄器。

    為了進行名字-位址解析,klogd就要用到System.map檔案。我想你現在知道一個oops與System.map的關系了。

    深入說明:其實klogd會執行兩類位址解析活動。

        * 靜态轉換,将使用System.map檔案。

        * 動态轉換,該方式用于可加載子產品,不使用System.map,是以與本讨論沒有關系,但我仍然對其加以簡單說明。

        Klogd動态轉換

假設你加載了一個産生oops的核心子產品。于是就會産生一個oops消息,klogd就會截獲它,并發現該oops發生在d00cf810處。由于該位址

屬于動态加載子產品,是以在System.map檔案中沒有對應條目。klogd将會在其中尋找并會毫無所獲,于是斷定是一個可加載子產品産生了oops。此

時klogd就會向核心查詢該可加載子產品輸出的符号。即使該子產品的編制者沒有輸出其符号,klogd也起碼會知道是哪個子產品産生了oops,這總比對一個

oops一無所知要好。

    還有其它的軟體會使用System.map,我将在後面作一說明。

System.map應該位于什麼地方?

    System.map應該位于使用它的軟體能夠尋找到的地方,也就是說,klogd會在什麼地方尋找它。在系統啟動時,如果沒有以一個參數的形式為klogd給出System.map的位置,則klogd将會在三個地方搜尋System.map。依次為:

       1. /boot/System.map

       2. /System.map

       3. /usr/src/linux/System.map

System.map

同樣也含有版本資訊,并且klogd能夠智能化地搜尋正确的map檔案。例如,假設你正在運作核心2.4.18并且相應的map檔案位于

/boot/System.map。現在你在目錄/usr/src/linux中編譯一個新核心2.5.1。在編譯期間,檔案

/usr/src/linux/System.map就會被建立。當你啟動該新核心時,klogd将首先查詢

/boot/System.map,确認它不是啟動核心正确的map檔案,就會查詢 /usr/src/linux/System.map,

确定該檔案是啟動核心正确的map檔案并開始讀取其中的符号資訊。

    幾個注意點:

* 在2.5.x系列核心的某個版本,Linux核心會開始untar成linux-version,而非隻是linux (請舉手表決

--

有多少人一直等待着這樣做?)。我不知道klogd是否已經修改為在/usr/src/linux-version/System.map中搜尋。

TODO:檢視klogd源代碼。

        * 線上手冊上對此也沒有完整描述,請看:

   # strace -f /sbin/klogd | grep 'System.map'

   31208 open("/boot/System.map-2.4.18", O_RDONLY|O_LARGEFILE) = 2

          顯然,不僅klogd在三個搜尋目錄中尋找正确版本的map檔案,klogd也同樣知道尋找名字為 "System.map" 後加"-核心版本",象 System.map-2.4.18. 這是klogd未公開的特性。

有一些驅動程式将使用System.map來解析符号(因為它們與核心頭連接配接而非glibc庫等),如果沒有System.map檔案,它們将不能正确地

工作。這與一個子產品由于核心版本不比對而沒有得到加載是兩碼事。子產品加載是與核心版本有關,而與即使是同一版本核心其符号表也會變化的編譯後核心無關。

還有誰使用了System.map?

    不要認為System.map檔案僅對核心oops有用。盡管核心本身實際上不使用System.map,其它程式,象klogd,lsof,

   satan# strace lsof 2>&1 1> /dev/null | grep System

   readlink("/proc/22711/fd/4", "/boot/System.map-2.4.18", 4095) = 23

    ps,

   satan# strace ps 2>&1 1> /dev/null | grep System

   open("/boot/System.map-2.4.18", O_RDONLY|O_NONBLOCK|O_NOCTTY) = 6

    以及其它許多軟體,象dosemu,需要有一個正确的System.map檔案。

如果我沒有一個好的System.map,會發生什麼問題?

    假設你在同一台機器上有多個核心。則每個核心都需要一個獨立的 System.map檔案!如果所啟動的核心沒有對應的System.map檔案,那麼你将定期地看到這樣一條資訊:

        System.map does not match actual kernel (System.map與實際核心不比對)

    不是一個緻命錯誤,但是每當你執行ps ax時都會惱人地出現。有些軟體,比如dosemu,可能不會正常工作。最後,當出現一個核心oops時,klogd或ksymoops的輸出可能會不可靠。

我如何對上述情況進行補救?

    方法是将你所有的System.map檔案放在目錄/boot下,并使用核心版本号重新對它們進行命名。假設你有以下多個核心:

        * /boot/vmlinuz-2.2.14

        * /boot/vmlinuz-2.2.13

    那麼,隻需對應各核心版本對map檔案進行改名,并放在/boot下,如:

/boot/System.map-2.2.14

   /boot/System.map-2.2.13

    如果你有同一個核心的兩個拷貝怎麼辦?例如:

        * /boot/vmlinuz-2.2.14

        * /boot/vmlinuz-2.2.14.nosound

    最佳解決方案将是所有軟體能夠查找下列檔案:

   /boot/System.map-2.2.14

   /boot/System.map-2.2.14.nosound

但是說實在的,我并不知道這是否是最佳情況。我曾經見到搜尋"System.map-kernelversion",但是對于搜尋"System.map

-kernelversion.othertext"的情況呢?

我不太清楚。此時我所能做的就是利用這樣一個事實:/usr/src/linux是标準map檔案的搜尋路徑,是以你的map檔案将放在:

        * /boot/System.map-2.2.14

        * /usr/src/linux/System.map (對于nosound版本)

    你也可以使用符号連接配接:

   System.map-2.2.14

   System.map-2.2.14.sound

   System.map -> System.map-2.2.14.sound

繼續閱讀