I am using the dladdr
from libld
(http://linux.die.net/man/3/dladdr) to get a trace of the function calls. Here it is minimal example with a single traced element:
#include<iostream>
#include <dlfcn.h> // link with -ldl -rdynamic a
void f(){
void **frame = static_cast<void **>(__builtin_frame_address(0));
void **bp = static_cast<void **>(*frame);
void *ip = frame[1];
Dl_info info;
dladdr(ip, &info);
std::cout << info.dli_sname << " " << info.dli_fname << " " << info.dli_saddr << std::endl;
ip = bp[1];
bp = static_cast<void**>(bp[0]);
dladdr(ip, &info);
std::cout << info.dli_sname << " " << info.dli_fname << " " << info.dli_saddr << std::endl;
}
int main(){
f();
}
which outputs:
main ./a.out 0x402800
__libc_start_main /lib64/libc.so.6 0x7febf6bf2610
that is, Dl_info
has the traced function name, the compiled file where it belongs and some address (0x7f...
) described in the man page as "Exact address of symbol named".
This address has information of the source file location (from where the function has been called). In fact with the help of some utility I can get that information:
$ addr2line -e a.out
/home/user/test.cpp:34
(give the exact line where main
is defined in source file). And this works as long as the program was compiled with the -g
option.
Now what I want is to extract this information programmaticaly. Supposedly, this is possible with the BFD library.
This is my attempt, based on BFD examples found for example here: http://opensource.apple.com/source/X11libs/X11libs-40.2/cairo/cairo-1.8.6/util/backtrace-symbols.c
1) first I have to define a function find_addr_sect
that will be called by bfd_map_over_sections
(through a pointer) later.
static void find_addr_sect(bfd *abfd, asection *section, void *obj){
bfd_data *data = (bfd_data *)obj;
bfd_vma vma;
bfd_size_type size;
if (data->found)
return;
if (!(bfd_get_section_vma(abfd, section)))
return;
vma = bfd_get_section_vma(abfd, section);
if (data->pc < vma)
return;
size = bfd_get_section_size(section);
if (data->pc >= vma + size)
return;
data->found = bfd_find_nearest_line(abfd, section, syms,
data->pc - vma,
&data->filename,
&data->function,
&data->line);
}
2) I put the code directing inside the function (this replaces the function void f()
above.
void f(){
void **frame = static_cast<void **>(__builtin_frame_address(0));
void **bp = static_cast<void **>(*frame);
void *ip = frame[1];
Dl_info info;
dladdr(ip, &info);
std::cout << info.dli_sname << " " << info.dli_fname << " " << info.dli_saddr << std::endl;
////////////////////
// this will try to find the location of main (first in the stack)
bfd *abfd = bfd_openr(info.dli_fname, NULL); assert(abfd); // the executable file is opened successfully
// bfd_data data;
bfd_map_over_sections(abfd, find_addr_sect, nullptr); // !! doesn't call `find_addr_sect` at all.
///////////////////
ip = bp[1];
bp = static_cast<void**>(bp[0]);
dladdr(ip, &info);
std::cout << info.dli_sname << " " << info.dli_fname << " " << info.dli_saddr << std::endl;
}
Sadly, I am stuck here because the bfd_map_over_sections
call doesn't do anything. I am using bfd_map_over_sections
in the wrong way, why?
Sorry for using C++, in this is a C question. It shortens most of my code and I am more used to it.
EDIT: I added this lines and I can confirm that one clue of the problem is that the number of sections is zero.
unsigned int numSections = -1;
numSections = bfd_count_sections(abfd);
std::cout << "num sections " << numSections << std::endl; // gives "0"
I looked for more examples, it seems that I was missing two things, calling the function bfd_check_format
after opening and also populating and passing the address information in the bfd_data
structure.
...
bfd *abfd = bfd_openr(info.dli_fname, NULL); assert(abfd);
// char **matching;
// bfd_data data;// = (bfd_data *)obj;
if (!bfd_check_format (abfd, bfd_object)){
bfd_close (abfd); assert(0);
}
...
later bfd_data
variable is used as input and output of the find_addr_sect
. Therefore
...
bfd_data data;// = (bfd_data *)obj;
data.pc = (bfd_hostptr_t)info.dli_saddr;
data.found = FALSE;
bfd_map_over_sections(abfd, find_addr_sect, &data);
...
And now it works.