【问题标题】:How to map function address to function in *.so files如何将函数地址映射到 *.so 文件中的函数
【发布时间】:2011-11-25 05:50:37
【问题描述】:

回溯函数给出一组回溯如何映射到函数名/文件名/行号?

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 backtrace@@GLIBC_2.1
     U backtrace_symbols@@GLIBC_2.1
     U free@@GLIBC_2.0
     U __isoc99_scanf@@GLIBC_2.7
     U perror@@GLIBC_2.0
     U printf@@GLIBC_2.0
     U puts@@GLIBC_2.0
     w __cxa_finalize@@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;
}

如果需要更多信息,请告诉我...

【问题讨论】:

  • 您是否使用调试信息 (-g) 进行编译?
  • @qrdl 是用 (-g) gcc -shared -ldl -fPIC libtst.c -o libtst.so -g 编译的
  • 然后使用backtrace_symbols()获取函数名。要从地址中获取函数名称和行号,您可以使用 dwarf 信息 - 检查 libdwarf 附带的 dwarfdump 实用程序。

标签: c debugging shared-libraries stack-trace


【解决方案1】:

尝试将偏移量与节名一起提供给 addr2line。像这样:

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

编辑:顺便说一句,如果不清楚,0x26887 来自您提供的内容:

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

【讨论】:

  • addr2line -j .text -e libtst.so 0x26887 ??:0 抱歉没有运气。
  • 你确定你的基地址是正确的吗?确保您获得了库的可执行部分的基地址(检查 /proc/pid/maps 并找到具有 'r-xp' 权限的 libtst.so 行)。
  • 为了一个完整的新手,你能否解释一下这是如何得出的:0xb77dc887(fun2 addr+offset)
  • 我的猜测是./libtst.so(fun2+0x35) [0xb77dc887]来自稍微修改的程序的堆栈跟踪+libtst.so被加载​​到不同的地址,导致同一个函数中的相同偏移量(fun2+0x35)有不同的地址和差异地址 0xb77dc887 - 0xb77678ba == 0x00074fcd1 之间不是页面大小的倍数(Linux 为 4096)。由于 ASLR (en.wikipedia.org/wiki/Address_space_layout_randomization),库基地址可能会因启动而有所不同。
  • @ch271828n 您可以简单地使用objdump -h libtst.so 并查看字段start address
【解决方案2】:

我查看了 glibc 源代码中的文件 backtrace.cbacktracesyms.c 文件(git://sourceware.org/git/glibc.git,提交 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 符号偏移量,或符号名称加上偏移量。

【讨论】:

  • 由于myfunc 是 GDB 中已经存在的有效符号,您只需执行 info line *(myfunc+0x2b)
【解决方案3】:
objdump -x --disassemble -l <objfile>

除其他外,这应该将机器代码的每条编译指令与其来自的 C 文件的行一起转储。

【讨论】:

  • 我没有,这是什么意思。当我尝试使用 objdump 和 nm 时,符号地址是相同的。更多信息会有所帮助。
  • 确保使用调试符号编译库。
【解决方案4】:

在运行时使用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");
}

如果您的调试文件位于其他位置(与 build-id 等匹配),请添加 --debuginfo-path=... 选项。

eu-addr2line 在您的发行版的 elfutils 包中。

【讨论】:

  • 还要注意上方编辑选项卡中的“水平规则”图标。您可以用它分隔问题的各个部分。一定要看?下次编辑问题或答案时,在编辑菜单栏中提供帮助。非常有用的编辑指南,从简单到高级。
  • 看起来我的应用找不到“eu-addr2line”。因此显示以下错误。 eu-addr2line:加载共享库时出错:libdw.so.dts.1:无法打开共享对象文件:没有这样的文件或目录
  • @PabitraDash,它找到了eu-addr2line,但没有找到需要的库libdw.so.dts。使用ldd 实用程序查看可执行文件或库需要哪些库。然后找出安装包时出了什么问题,该库属于哪个包,需要正确重新安装哪些包。
猜你喜欢
  • 1970-01-01
  • 2018-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多