How to map function address to function in *.so files
Asked Answered
F

4

45

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

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;
}

Let me know if need more information...

Foliation answered 26/9, 2011 at 13:45 Comment(3)
Did you compile with debug info (-g)?Proximo
@Proximo yes compiled with (-g) gcc -shared -ldl -fPIC libtst.c -o libtst.so -gFoliation
Then use backtrace_symbols() to get function names. To get function names and line numbers from addresses you can use dwarf information - check dwarfdump utility that comes with libdwarf.Proximo
L
38

Try giving the offset to addr2line, along with the section name. Like this:

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

Edit: By the way, if it wasn't clear, the 0x26887 comes from what you provided:

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

Lug answered 26/9, 2011 at 15:51 Comment(8)
addr2line -j .text -e libtst.so 0x26887 ??:0 sorry no luck.Foliation
Are you sure your base address is correct? Make sure you're getting the base address of the executable section of the library (check in /proc/pid/maps and find the libtst.so line with 'r-xp' permissions).Lug
Thanks you, got it.addr2line -e libtst.so 0x767 /home/test/libtofun/libtst.c:16Foliation
For the benefit of a complete newbie, can you please explain how this was derived: 0xb77dc887(fun2 addr+offset)Stacked
My guess is that ./libtst.so(fun2+0x35) [0xb77dc887] is from stacktrace of slightly modified program + libtst.so was loaded at different address, cause the same offset in the same function (fun2+0x35) has different address and difference between addresses 0xb77dc887 - 0xb77678ba == 0x00074fcd1 isn't a multiple of page size (4096 for Linux). Library base address can change from launch to launch because of ASLR (en.wikipedia.org/wiki/Address_space_layout_randomization).Rains
Hi, how can I get lib starting addr? Thanks!Shovelboard
@Shovelboard You can simply use objdump -h libtst.so and see the field start addressChilson
If you got something like a.so(+0x12345) [...], then just run addr2line -e a.so 0x12345Cesena
S
25

I've had a look at files backtrace.c and backtracesyms.c files in glibc source code (git://sourceware.org/git/glibc.git, commit 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.

So, if you've compiled with -g (or with -rdynamic), then you're in luck. You should be able to do the following:

$ # 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

Or, using 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)

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):

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

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.

Snaky answered 28/5, 2014 at 11:30 Comment(1)
Since myfunc is a valid symbol already in GDB you can just do info line *(myfunc+0x2b).Arsenite
R
8
objdump -x --disassemble -l <objfile>

This should dump, among other things, each compiled instruction of machine code with the line of the C file it came from.

Rhizotomy answered 26/9, 2011 at 13:59 Comment(2)
I did not, what this means. When I try with objdump and nm, symbol address is same. More information would be helpful.Foliation
Make sure you're compiling your library with debugging symbols.Rhizotomy
P
5

At runtime with eu-addr2line (automatically finds the libraries and calculates offsets):

//-------------------------------------
#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");
}

Stick a --debuginfo-path=... option if your debug files are somewhere else (matched by the build-id, etc.).

eu-addr2line is in the elfutils package of your distribution.

Plateau answered 19/10, 2016 at 12:45 Comment(3)
Also notice the "horizontal rule" icon in the editing tab above. You can separate sections of a question with that. Be sure to see the ? help in the editing menu bar next time you edit a question or answer. Very helpful editing guidelines ranging from the simple to the advanced.Toque
Looks like my app could not find 'eu-addr2line'. Hence shows following error. eu-addr2line: error while loading shared libraries: libdw.so.dts.1: cannot open shared object file: No such file or directoryCrus
@PabitraDash, it finds eu-addr2line, but it doesn't find a library libdw.so.dts that is needed. Use ldd utility to see which libraries an executable or a library needs. Then find out what went wrong when the packages were installed, to which package that library belongs, which packages need to be reinstalled properly.Plateau

© 2022 - 2024 — McMap. All rights reserved.