C语言 如何将函数地址映射到 *.so 文件中的函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7556045/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to map function address to function in *.so files
提问by Thangaraj
backtrace function give set of backtrace how to map it with function name/file name/line number?
回溯函数给出一组回溯如何将其与函数名/文件名/行号映射?
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]
From the above stack how can I get the file name and line number? I did following things, but no luck. Correct me if I am wrong :)
从上面的堆栈如何获取文件名和行号?我做了以下事情,但没有运气。如果我错了,请纠正我:)
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
So, how can I resolve either at runtime or post runtime? Thanks in advance...
那么,如何在运行时或运行后解决?提前致谢...
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 writable-private, 1560K readonly-private, 0K shared, and 376K referenced
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$ # 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
";
printf("Fun name %s\n",fun);
scanf("%c",&ip);
myfunc5();
}
void fun3(){
char *fun = "fun3$ 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)
";
printf("Fun name %s\n",fun);
funptr = fun4;
funptr();
}
void fun2(){
char *fun = "fun2$ gdb -s debug/libtst.debug -e libtst.so
";
printf("Fun name %s\n",fun);
fun3();
}
void fun1(){
char *fun = "fun1objdump -x --disassemble -l <objfile>
";
printf("Fun name %s\n",fun);
fun2();
}
main.c:-
int main(){
char ip;
funptr = &fun1;
scanf("%c",&ip);
funptr();
return 0;
}
Let me know if need more information...
如果需要更多信息,请告诉我...
采纳答案by tdenniston
Try giving the offset to addr2line, along with the section name. Like this:
尝试将偏移量与部分名称一起提供给 addr2line。像这样:
addr2line -j .text -e libtst.so 0x26887
addr2line -j .text -e libtst.so 0x26887
Edit: By the way, if it wasn't clear, the 0x26887comes from what you provided:
编辑:顺便说一句,如果不清楚,则0x26887来自您提供的内容:
0xb77dc887(fun2 addr+offset)-0xb77b6000 (lib starting addr) = 0x26887 (result)
0xb77dc887(fun2 addr+offset)-0xb77b6000 (lib starting addr) = 0x26887 (result)
回答by Rob
I've had a look at files backtrace.cand backtracesyms.cfiles in glibc source code (git://sourceware.org/git/glibc.git, commit 2482ae433a4249495859343ae1fba408300f2c2e).
我查看了 glibc 源代码中的文件backtrace.c和backtracesyms.c文件(git://sourceware.org/git/glibc.git,提交 2482ae433a4249495859343ae1fba408300f2c2e)。
Assuming I haven't misread/misunderstood things: backtrace()itself looks like it will only give you symbol addresses as they are at runtime, which I think means you need the library load address as it was from pmap or similar. However, backtrace_symbols()recalculates things so that the addresses are relative to the shared library ELF, and not the process at runtime, which is really convenient. It means you don't need information from pmap.
假设我没有误读/误解事情:backtrace()本身看起来只会为您提供运行时的符号地址,我认为这意味着您需要库加载地址,因为它来自 pmap 或类似地址。但是,backtrace_symbols() 会重新计算,使得地址是相对于共享库ELF,而不是运行时的进程,这真的很方便。这意味着您不需要来自 pmap 的信息。
So, if you've compiled with -g (or with -rdynamic), then you're in luck. You should be able to do the following:
因此,如果您使用 -g(或使用 -rdynamic)进行编译,那么您很幸运。您应该能够执行以下操作:
//-------------------------------------
#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] = '##代码##';
snprintf(syscom, 1024, "eu-addr2line '%p' --pid=%d > /dev/stderr\n", buffer[i], getpid());
if (system(syscom) != 0)
fprintf(stderr, "eu-addr2line failed\n");
}
Or, using gdb:
或者,使用 gdb:
##代码##Also, if you've stripped the library, but stashed off debug symbols for later use, then you'll likely only have ELF offsets printed out by backtrace_syms() and no symbol names (so not quite the case in the original question): In this instance, using gdb is arguably more convenient than using other command line tools. Assuming you've done this, you'll need to invoke gdb like so (for example):
此外,如果您已经剥离了库,但将调试符号藏起来供以后使用,那么您可能只会通过 backtrace_syms() 打印出 ELF 偏移量而没有符号名称(因此在原始问题中并非如此):在这种情况下,使用 gdb 可以说比使用其他命令行工具更方便。假设你已经这样做了,你需要像这样调用 gdb(例如):
##代码##And then go through a similar sequence as above, using 'info line' and 'info address' depending on whether you only have ELF symbol offsets, or symbol names plus offsets.
然后通过与上述类似的序列,使用“信息行”和“信息地址”,具体取决于您是只有 ELF 符号偏移量,还是符号名称加上偏移量。
回答by nmichaels
This should dump, among other things, each compiled instruction of machine code with the line of the C file it came from.
这应该转储机器代码的每个编译指令及其来自的 C 文件的行。
回答by Velkan
At runtime with eu-addr2line(automatically finds the libraries and calculates offsets):
在运行时使用eu-addr2line(自动查找库并计算偏移量):
Stick a --debuginfo-path=...option if your debug files are somewhere else (matched by the build-id, etc.).
--debuginfo-path=...如果您的调试文件在其他地方(与 build-id 等匹配),请选择一个选项。
eu-addr2lineis in the elfutilspackage of your distribution.
eu-addr2line位于elfutils您的发行版的包中。

