2011-09-26 105 views
27

回溯函數給出回溯集如何用函數名/文件名/行號映射它?如何將函數地址映射到* .so文件中的函數

for ex:- 
backtrace() returned 8 addresses 
./libtst.so(myfunc5+0x2b) [0xb7767767] 
./libtst.so(fun4+0x4a) [0xb7767831] 
./libtst.so(fun3+0x48) [0xb776787f] 
./libtst.so(fun2+0x35) [0xb77678ba] 
./libtst.so(fun1+0x35) [0xb77678f5] 
./a.out() [0x80485b9] 
/lib/libc.so.6(__libc_start_main+0xe5) [0xb75e9be5] 
./a.out() [0x80484f1] 

從上面的堆棧我怎麼能得到文件名和行號? 我做了以下的事情,但沒有運氣。糾正我,如果我錯了:)

for ex:- 
./libtst.so(fun2+0x35) [0xb77dc887] 

0xb77dc887(fun2 addr+offset)-0xb77b6000 (lib starting addr) = 0x26887 (result) 
result is no way related to function in nm output. 

I used addr2line command:- 
addr2line -f -e libtst.so 0xb77dc887 
?? 
??:0 

那麼,如何解決無論是在運行時或後運行時? 在此先感謝...

nm:- 
00000574 T _init 
00000680 t __do_global_dtors_aux 
00000700 t frame_dummy 
00000737 t __i686.get_pc_thunk.bx 
0000073c T myfunc5 
000007e7 T fun4 
00000837 T fun3 
00000885 T fun2 
000008c0 T fun1 
00000900 t __do_global_ctors_aux 
00000938 T _fini 
000009b4 r __FRAME_END__ 
00001efc d __CTOR_LIST__ 
00001f00 d __CTOR_END__ 
00001f04 d __DTOR_LIST__ 
00001f08 d __DTOR_END__ 
00001f0c d __JCR_END__ 
00001f0c d __JCR_LIST__ 
00001f10 a _DYNAMIC 
00001ff4 a _GLOBAL_OFFSET_TABLE_ 
00002030 d __dso_handle 
00002034 A __bss_start 
00002034 A _edata 
00002034 b completed.5773 
00002038 b dtor_idx.5775 
0000203c B funptr 
00002040 A _end 
    U [email protected]@GLIBC_2.1 
    U [email protected]@GLIBC_2.1 
    U [email protected]@GLIBC_2.0 
    U [email protected]@GLIBC_2.7 
    U [email protected]@GLIBC_2.0 
    U [email protected]@GLIBC_2.0 
    U [email protected]@GLIBC_2.0 
    w __cxa_finali[email protected]@GLIBC_2.1.3 
    w __gmon_start__ 
    w _Jv_RegisterClasses 

pmap:- 
START  SIZE  RSS  PSS DIRTY SWAP PERM MAPPING 
08048000  4K  4K  4K  0K  0K r-xp /home/test/libtofun/a.out 
08049000  4K  4K  4K  4K  0K r--p /home/test/libtofun/a.out 
0804a000  4K  4K  4K  4K  0K rw-p /home/test/libtofun/a.out 
... 
b7767000  4K  4K  4K  0K  0K r-xp /home/test/libtofun/libtst.so 
b7768000  4K  4K  4K  4K  0K r--p /home/test/libtofun/libtst.so 
b7769000  4K  4K  4K  4K  0K rw-p /home/test/libtofun/libtst.so 
.... 
Total:  1688K 376K  82K  72K  0K 

128K可寫的,私人的,1560K只讀和私營部門,0K共享和376K引用

libtst.c:- 

void myfunc5(void){ 
int j, nptrs; 
#define SIZE 100 
void *buffer[100]; 
char **strings; 

nptrs = backtrace(buffer, SIZE); 
printf("backtrace() returned %d addresses\n", nptrs); 

strings = backtrace_symbols(buffer, nptrs); 
if (strings == NULL) { 
    perror("backtrace_symbols"); 
} 

for (j = 0; j < nptrs; j++) 
    printf("%s\n", strings[j]); 

free(strings); 
} 

void fun4(){ 
char ip; 
char *fun = "fun4\0"; 
printf("Fun name %s\n",fun); 
scanf("%c",&ip); 
myfunc5(); 
} 


void fun3(){ 
char *fun = "fun3\0"; 
printf("Fun name %s\n",fun); 
funptr = fun4; 
funptr(); 
} 


void fun2(){ 
char *fun = "fun2\0"; 
printf("Fun name %s\n",fun); 
fun3(); 
} 


void fun1(){ 
char *fun = "fun1\0"; 
printf("Fun name %s\n",fun); 
fun2(); 
} 

main.c:- 

int main(){ 
char ip; 
funptr = &fun1; 
scanf("%c",&ip); 
funptr(); 
return 0; 
} 

讓我知道如果需要了解更多信息...

+1

你用調試信息('-g')編譯過嗎? – qrdl

+0

@qrdl是用(-g)gcc -shared -ldl -fPIC編譯libtst.c -o libtst.so -g – Thangaraj

+0

然後使用'backtrace_symbols()'獲取函數名稱。要從地址獲取函數名稱和行號,可以使用矮小信息 - 檢查'libdwarf'附帶的'dwarfdump'實用程序。 – qrdl

回答

28

嘗試給出addr2line的偏移量以及節名稱。就像這樣:

addr2line -j .text -e libtst.so 0x26887

編輯:順便說一下,如果它是不明確的,0x26887來自於你提供了什麼:

0xb77dc887(fun2 addr+offset)-0xb77b6000 (lib starting addr) = 0x26887 (result)

+0

addr2line -j .text -e libtst.so 0x26887 ??:0 抱歉沒有運氣。 – Thangaraj

+0

你確定你的基地址正確嗎?確保你獲得了庫的可執行部分的基地址(檢入/ proc/pid/maps並找到具有'r-xp'權限的libtst.so行)。 –

+0

謝謝你,得到它.addr2line -e libtst.so 0x767 /home/test/libtofun/libtst.c:16 – Thangaraj

6
objdump -x --disassemble -l <objfile> 

這應該轉儲,除其他外,機器碼的每個編譯指令與它來自C文件的行。

+0

我沒有,這是什麼意思。當我嘗試使用objdump和nm時,符號地址是相同的。更多信息將有所幫助。 – Thangaraj

+0

確保您使用調試符號編譯庫。 – nmichaels

+0

是的,我用-g選項編譯... – Thangaraj

16

我有看glibc源代碼中的文件backtrace.cbacktracesyms.c文件(git://sourceware.org/git/glibc.git,commit 2482ae433a4249495859343ae1fba408300f2c2e)。

假設我沒有誤讀/誤解的東西:backtrace()本身看起來像它只會給你符號地址,因爲它們是在運行時,這意味着你需要像pmap或類似的庫加載地址。然而,backtrace_symbols()重新計算的東西,使地址是相對於共享庫ELF,而不是運行時的進程,這是非常方便的。這意味着你不需要pmap的信息。

所以,如果你用-g(或者-rdynamic)編譯,那麼你很幸運。你應該能夠做到以下幾點:

$ # get the address in the ELF so using objdump or nm 
$ nm libtst.so | grep myfunc 
0000073c T myfunc5 
$ # get the (hex) address after adding the offset 
$ # from the start of the symbol (as provided by backtrace_syms()) 
$ python -c 'print hex(0x0000073c+0x2b)' 
0x767 
$ # use addr2line to get the line information, assuming any is available    
addr2line -e libtst.so 0x767 

或者,用gdb:

$ gdb libtst.so 
(gdb) info address myfunc 
Symbol "myfunc" is at 0x073c in a file compiled without debugging. # (Faked output) 
(gdb) info line *(0x073c+0x2b) 
Line 27 of "foo.cpp" starts at address 0x767 <myfunc()+21> and ends at 0x769 <something>. # (Faked output) 

此外,if you've stripped the library, but stashed off debug symbols for later use,那麼你可能只需要()由backtrace_syms打印出來ELF偏移和無符號名稱(在原始問題中並非如此):在這種情況下,使用gdb可能比使用其他命令行工具更方便。假設你已經做到了這一點,你需要調用GDB像這樣(例如):

$ gdb -s debug/libtst.debug -e libtst.so 

,然後再通過類似的序列如上,使用「信息線」,並根據「信息地址」無論您是隻有ELF符號偏移量,還是符號名稱和偏移量。

+1

由於'myfunc'是GDB中的一個有效符號,因此您可以'info line *(myfunc + 0x2b)'。 –

3

在與eu-addr2line運行時(自動查找圖書館和計算偏移):

//------------------------------------- 
#include <sys/types.h> 
#include <unistd.h> 

int i; 
#define SIZE 100 
void *buffer[100]; 

int nptrs = backtrace(buffer, SIZE); 

for (i = 1; i < nptrs; ++i) { 
    char syscom[1024]; 
    syscom[0] = '\0'; 
    snprintf(syscom, 1024, "eu-addr2line '%p' --pid=%d > /dev/stderr\n", buffer[i], getpid()); 
    if (system(syscom) != 0) 
     fprintf(stderr, "eu-addr2line failed\n"); 
} 

棒一個--debuginfo-path=...的選擇,如果您的調試文件是在其他地方(由build-ID匹配,等等)。

eu-addr2line位於您的發行版的elfutils包中。

+1

另請注意上方編輯選項卡中的「水平線」圖標。你可以用它來分隔問題的各個部分。一定要看到?下次編輯問題或答案時,請在編輯菜單欄中提供幫助。非常有用的編輯指南,從簡單到高級。 – clearlight