Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to map function address to function in *.so files

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...

like image 796
Thangaraj Avatar asked Sep 26 '11 13:09

Thangaraj


4 Answers

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)

like image 97
tdenniston Avatar answered Nov 19 '22 12:11

tdenniston


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.

like image 24
Rob Avatar answered Nov 19 '22 12:11

Rob


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.

like image 8
nmichaels Avatar answered Nov 19 '22 12:11

nmichaels


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.

like image 5
Velkan Avatar answered Nov 19 '22 12:11

Velkan