Make BFD library find the location of a class member function

573 Views Asked by At

I am using the function bfd_find_nearest_line to find the source location of a function (from an executable with debugging symbols --compiled with -g). Naturally one of the arguments is a pointer to the function I want to locate:

boolean
_bfd_elf_find_nearest_line (abfd, 
                section, 
                symbols, 
                offset, 
                filename_ptr, 
                functionname_ptr, // <- HERE!
                line_ptr)

https://sourceware.org/ml/binutils/2000-08/msg00248.html

After quite a bit of (pure C) boiler plate, I managed this to work with normal functions (where the normal function pointer is casted to *void).

For example, this works:

int my_function(){return 5;}

int main(){
    _bfd_elf_find_nearest_line (...,
                (void*)(&my_function), 
                ...);
}

The question is if bfd_find_nearest_line can be used to locate the source code of a class member function.

struct A{
   int my_member_function(){return 5.;}
};

_bfd_elf_find_nearest_line (...,
                what_should_I_put_here??, 
                ...)

Class member function (in this case if type int (A::*)()) are not functions, an in particular cannot be cast to any function pointer, not even to void*. See here: https://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-memfnptr-to-voidptr

I completely understand the logic behind this, how ever the member-function pointer is the only handle from which I have information of a member function in order to make BFD identify the function. I don't want this pointer to call a function.

I know more or less how C++ works, the compiler will generate silently an equivalent free-C function,

__A_my_member_function(A* this){...}

But I don't know how to access the address of this free function or if that is even possible,and whether the bfd library will be able to locate the source location of the original my_member_function via this pointer. (For the moment at least I am not interested in virtual functions.)

In other words,

1) I need to know if bfd will be able to locate a member function,

2) and if it can how can I map the member function pointer of type int (A::*)() to an argument that bfd can take (void*).


I know by other means (stack trace) that the pointer exists, for example I can get that the free function is called in this case _ZN1A18my_member_functionEv, but the problem is how I can get this from &(A::my_member_function).

2

There are 2 best solutions below

0
On BEST ANSWER

Ok, I found a way. First, I discovered that bfd is pretty happy detecting member functions debug information from member pointers, as long as the pointer can be converted to void*.

I was using clang which wouldn't allow me to cast the member function pointer to any kind of pointer or integer. GCC allows to do this but emits a warning. There is even a flag to allow pointer to member cast called -Wno-pmf-conversions.

With that information in mind I did my best to convert a member function pointer into void* and I ended up doing this using unions.

struct A{
   int my_member_function(){return 5.;}
};

union void_caster_t{
    int (A::*p)(void) value;
    void* casted_value;
};
void_caster_t void_caster = {&A::my_member_function};

_bfd_elf_find_nearest_line (...,
                void_caster.casted_value, 
                ...)

Finally bfd is able to give me debug information of a member function.


What I didn't figure out yet, is how to get the pointer to the constructor and the destructor member functions.

For example

void_caster_t void_caster = {&A::~A};

Gives compiler error: "you can't take the address of the destructor".

For the constructor I wasn't even able to find the correct syntax, since this fails as a syntax error.

void_caster_t void_caster = {&A::A};

Again all the logic behind not being able involves non-sensical callbacks, but this is different because I want the pointer (or address) to get debug information, not callbacks.

1
On

Okay, there's good news and bad news.

The good news: It is possible.

The bad news: It's not straight forward.

You'll need the c++filt utility.

And, some way to read the symbol table of your executable, such as readelf. If you can enumerate the [mangled] symbols with a bfd_* call, you may be able to save yourself a step.

Also, here is a biggie: You'll need the c++ name of your symbol in a text string. So, for &(A::my_member_function), you'll need it in a form: "A::my_member_function()" This shouldn't be too difficult since I presume you have a limited number of them that you care about.

You'll need to get a list of symbols and their addresses from readelf -s <executable>. Be prepared to parse this output. You'll need to decode the hex address from the string to get its binary value.

These will be the mangled names. For each symbol, do c++filt -n mangled_name and capture the output (i.e. a pipe) into something (e.g. nice_name). It will give you back the demangled name (i.e. the nice c++ name you'd like).

Now, if nice_name matches "A:my_member_function()", you now have a match, you already have the mangled name, but, more importantly, the hex address of the symbol. Feed this hex value [suitably cast] to bfd where you were stuffing functionname_ptr


Note: The above works but can be slow with repeated invocations of c++filt

A faster way is to do this is to capture the piped output of:

readelf -s <executable> | c++filt

It's also [probably] easier to do it this way since you only have to parse the filtered output and look for the matching nice name.

Also, if you had multiple symbols that you cared about, you could get all the addresses in a single invocation.