Linux addr2line具體應用指南

調試zSeries上的Linux應用程序類似於調試其他體系結構上的Linux應用程序。對於有經驗的Linux開發人員,最大的挑戰是理解新的系統體系結構。對於剛接觸Linux的大型機開發人員,掌握新的調試工具似乎是一項令人畏懼的任務。不要害怕。本文將提供Linux addr2line一些有用的提示來幫助您入門。

UserDebug日誌記錄

調試一個崩潰的程序的第一步是弄清哪裏出了錯。zSeries上的Linux內核具有這樣一個內置特性,它在用戶進程崩潰時記錄一些基本的調試信息。要啓用這個特性,請以root用戶身份執行如下命令:
echo1>>/proc/sys/kernel/userprocess_debug

當某個進程崩潰時,日誌文件(/var/log/messages)中就會給出附加的信息,包括程序終止原因、故障地址,以及包含程序狀態字(PSW)、通用寄存器和訪問寄存器的簡要寄存器轉儲。
 

  1. Mar3111:34:28l02kernel:Userprocessfault:interruptioncode0x10  
  2. Mar3111:34:28l02kernel:failingaddress:0  
  3. Mar3111:34:28l02kernel:CPU:1  
  4. Mar3111:34:28l02kernel:Processsimple(pid:30122,stackpage=05889000)  
  5. Mar3111:34:28l02kernel:  
  6. Mar3111:34:28l02kernel:UserPSW:070dc000c00ab738  
  7. Mar3111:34:28l02kernel:task:05888000ksp:05889f08pt_regs:05889f68  
  8. Mar3111:34:28l02kernel:UserGPRS:Mar3111:34:28l02kernel:00000000004019a0004019a000000000  
  9. Mar3111:34:28l02kernel:00000003c00ab732004008f800400338  
  10. Mar3111:34:28l02kernel:40018ffc0040061c40018e347ffff800  
  11. Mar3111:34:28l02kernel:00400434804006248040066e7ffff800  
  12. Mar3111:34:28l02kernel:UserACRS:  
  13. Mar3111:34:28l02kernel:00000000000000000000000000000000  
  14. Mar3111:34:28l02kernel:00000001000000000000000000000000  
  15. Mar3111:34:28l02kernel:00000000000000000000000000000000  
  16. Mar3111:34:28l02kernel:00000000000000000000000000000000  
  17. Mar3111:34:28l02kernel:UserCode:  
  18. Mar3111:34:28l02kernel:4440500007fea74a0001185418431835a8240000  

上面表明程序(名爲“simple”)以一個程序中斷代碼0x10終止(操作系統原理表明這是一個段轉換錯誤),而故障地址爲0。毫無疑問,有人使用了空指針。現在我們知道發生了什麼,下面需要弄清它發生在何處。

Linux addr2line基本的診斷

UserDebug日誌條目所提供的信息可用於確定程序的崩潰位置。一些可用的工具可幫助解決您可能會遇到的各種程序終止問題。我們將在本文中逐步介紹那些工具。

首先,讓我們檢查一下該日誌條目中的用戶PSW。該PSW包含指令地址、狀態碼以及關於機器狀態的其他信息。眼下,我們僅關心指令地址(第33至第63位)。爲簡化起見,讓我們假設用戶PSW是070dc00080400618。記住,我們是在考察一個ESA/390(31位尋址)PSW。第32位不是指令地址的一部分,它是指示31位尋址模式的標誌,但是在研究PSW值時必須處理它。爲了獲得實際的指令指針,可把PSW的第二個字減去0x80000000。結果是一個指令地址0x400618。爲了定位代碼,您需要可執行文件中的一些信息。首先使用readelf來打印一些程序頭信息。
 

  1. ElffiletypeisEXEC(Executablefile)Entrypoint0x400474Thereare6programheaders,startingatoffset52ProgramHeaders:  
  2. TypeOffsetVirtAddrPhysAddrFileSizMemSizFlgAlign  
  3. PHDR0x0000340x004000340x004000340x000c00x000c0RE0x4  
  4. INTERP0x0000f40x004000f40x004000f40x0000d0x0000dR0x1[Requestingprograminterpreter:/lib/ld.so.1]  
  5. LOAD0x0000000x004000000x004000000x009900x00990RE0x1000  
  6. LOAD0x0009900x004019900x004019900x000fc0x00114RW0x1000  
  7. DYNAMIC0x0009ac0x004019ac0x004019ac0x000a00x000a0RW0x4  
  8. NOTE0x0001040x004001040x004001040x000200x00020R0x4  
  9. SectiontoSegmentmapping:SegmentSections...  
  10. 00  
  11. 01.interp  
  12. 02.interp.note.ABI-tag.hash.dynsym.dynstr.gnu.version  
  13. .gnu.version_r.rela.got.rela.plt.init.plt.text.fini.rodata  
  14. 03.data.eh_frame.dynamic.ctors.dtors.got.bss  
  15. 04.dynamic  
  16. 05.note.ABI-tag  

上述顯示了readelf-lsimple的結果(記住“simple”是我們的測試程序的名稱)。在ProgramHeaders部分,第一個LOAD行提供了關於程序從哪裏加載的信息。在Flg列,該段被標記爲R(read)E(executable)。VirtAddr是程序開始加載的地址。MemSiz是正在被加載到這個段中的代碼長度。把它加到VirtAddr上,這個程序的基本地址範圍就是0x400000-0x400990。程序發生崩潰的指令地址爲0x400618,在程序的加載範圍之內。現在我們知道了問題直接發生在代碼中。

如果可執行文件包括調試符號,那麼確定哪一行代碼導致了問題是可以做到的。對該地址和可執行文件使用addr2line程序,如下所示:
addr2line-esimple0x400618

將返回:
/home/devuser/simple.c:34

要研究該問題,可以檢查第34行。

對於Linux addr2line原始的程序崩潰,PSW爲070dc000c00ab738。要獲得指令地址,可減去0x80000000。結果爲0x400ab738。這個地址並不準確地落在我們的小程序之內。那麼,它是什麼呢?是來自共享庫的代碼。如果對可執行文件運行ldd命令(lddsimple),將會返回程序運行所需的共享對象的列表,以及該庫在那裏可用的地址。
libc.so.6=>/lib/libc.so.6(0x40021000)/lib/ld.so.1=>/lib/ld.so.1(0x40000000)

該指令地址對應於加載libc.so.6的地址。在我們的簡單測試案例中,只需要兩個共享對象。其他應用程序可能需要更多共享對象,這使得ldd的輸出更加複雜。我們將以perl作爲例子。輸入:
ldd/usr/bin/perl

將得到:
 

  1. libnsl.so.1=> 
  2. /lib/libnsl.so.1(0x40021000)libdl.so.2=> 
  3. /lib/libdl.so.2(0x40039000)libm.so.6=> 
  4. /lib/libm.so.6(0x4003d000)libc.so.6=> 
  5. /lib/libc.so.6(0x40064000)libcrypt.so.1=> 
  6. /lib/libcrypt.so.1(0x4018f000)/lib/ld.so.1=> 
  7. /lib/ld.so.1(0x40000000)  

所需要的一切都在那裏了,但是我發現對於這個進程,下面的內容讀起來更快一點:
 

  1. ldd/usr/bin/perl|awk‘{print?$4““$3}’  
  2. |sort(0x40000000)/lib/ld.so.1(0x40021000)  
  3. /lib/libnsl.so.1(0x40039000)  
  4. /lib/libdl.so.2(0x4003d000)  
  5. /lib/libm.so.6(0x40064000)  
  6. /lib/libc.so.6(0x4018f000)  
  7. /lib/libcrypt.so.1  

現在我們來確定Linux addr2line崩潰發生在libc中的何處。假設libc.so.6的加載地址是0x40021000,從指令地址0x400ab738減去它,結果爲0x8a738。這是進入libc.so.6的偏移。使用nm命令,從libc.so.6轉儲符號,然後嘗試確定該地址位於哪個函數中。對於libc.so.6,nm將生成7,000多行輸出。通過對計算得出的偏移部分執行grep(正則表達式查找程序)可以削減必須檢查的數據量。輸入:
nm/lib/libc.so.6|sort|grep0008a

將返回66行,在該輸出的中間,我們會發現:
0008a6fcTmemcpy0008a754t_wordcopy_fwd_aligned

該偏移落在memcpy中的某個位置。在此例中,一個空指針被當作目標地址傳遞給了memcpy。我們在何處調用的memcpy呢?問得好。我們可以通過檢查輸出在日誌文件中的寄存器轉儲來確定目標區域。寄存器14包含執行某個函數調用時的返回地址。根據圖1,R14是0x8040066e,它在截去高位之後產生一個地址0x40066e。這個地址落在我們的程序範圍之內,因此可以運行addr2line來確定該地址在何處。輸入:
addr2line-esimple0x40066e

將返回:
/home/devuser/simple.c:36

這是我們調用memcpy之後的那一行。關於addr2line的一點補充:如果可執行文件中沒有包括調試符號,您將獲得??:0作爲響應。

 

 

 

 

arm-eabi-addr2line 

Usage: arm-eabi-addr2line [option(s)] [addr(s)]

for example:

D:\gat\iBox\TRUNK\lib>arm-eabi-addr2line -e "D:\*.out.symbols\alps\out\target\product\*\symbols/../../../../../kernel/vmlinux" c0037aa0

 The options are:
  @<file>                Read options from <file>
  -b --target=<bfdname>  Set the binary file format
  -e --exe=<executable>  Set the input file name (default is a.out)
  -i --inlines           Unwind inlined functions
  -j --section=<name>    Read section-relative offsets instead of addresses
  -s --basenames         Strip directory names
  -f --functions         Show function names
  -C --demangle[=style]  Demangle function names
  -h --help              Display this information
  -v --version           Display the program's version.

常用-f, -C, -e.

 

KE debug的方法:

首先在在dump的kernel log中找[<>]這樣的東西,然後再通過addr2line在vmlinux中找到其對應的line

有的地址,比如bf開頭的是debug不了的,因爲3GB-8MB ~ 3GB是kernel module載入的地址,而vmlinux中沒有module的symbol table.

NE debug的方法

首先在ne log中找signal 11 (SIGSEGV), fault addr baae8fd1

的東西,找到後

通過分析,如果fault addr 是deaddead,那就表示heap error出現,然後再看是deadadd0,就是heap corruption,如果是deadadd0,就是heap usage error.

然後將heap信息保存下載

然後再通過將signal 11 (SIGSEGV), fault addr baae8fd1中的地址通過addr2line來進行轉換得道C庫出錯的地址

NE的decode是通過

         #01  pc 0001cd4a  /system/lib/libc.so

中間後面libc.so的路徑,將其加到symbols path後面,然後再通過addr2line來解析出來。

 arm-eabi-addr2line -f -C -e D:\gat\Department\SP_SS\OSS5_ST_Share\GAT\TRUNK\Test_Cases\NE\ztemt73v2_ALPS.GB.p52_eng.out.symbols\alps\out\target\product\ztemt73v2\symbols\system\lib\libc.so 0001cd0e

輸出的話就是

__findenv
/alps/GB/Of/alps/bionic/libc/stdlib/getenv.c:90

 

發佈了15 篇原創文章 · 獲贊 11 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章