Why does backtrace not contain Objective-C symbols regardless of -rdynamic?

天大地大妈咪最大 提交于 2019-12-04 14:20:08
Jeremy W. Sherman

dladdr() only reports global and weak symbols. But all Objective-C function symbols are local:

$ readelf -s so_backtrace

Symbol table '.dynsym' contains 29 entries:
…

Symbol table '.symtab' contains 121 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
…
    49: 08048a01    13 FUNC    LOCAL  DEFAULT   14 _c_TheObject___printTrace
    50: 08048a0e    47 FUNC    LOCAL  DEFAULT   14 _c_TheObject__printTrace
…

You can verify that local symbols are never returned by looking at the GNU libc source code yourself. backtrace_symbols() is defined in sysdeps/generic/elf/backtracesyms.c. It relies on _dl_addr(), which is defined in elf/dl-addr.c, to provide it with the symbol names. That ultimately calls determine_info(). If it can, it uses the the GNU hash table, which does not include local symbols by design:

49       /* We look at all symbol table entries referenced by the hash
50          table.  */
…
60                   /* The hash table never references local symbols so
61                      we can omit that test here.  */

If the GNU hash table isn't present, it falls back to standard hash table. This includes all the symbols, but the determine_info() code filters out all but the global symbols and weak symbols:

90         if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
91              || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)

To symbolicate the Objective-C function addresses, you would have to perform the look-up yourself and not filter out the local function symbols. Further, you would have to demangle the Objective-C function symbols to restore _c_TheObject___printTrace to +[TheObject _printTrace].

GNUstep's NSException implementation doesn't use backtrace, instead it uses libbfd (binary file descriptor). I think the function that actually does the work is called static void find_address, which you can view here. Using this trivial example, I get the results that follow.

#include <Foundation/Foundation.h>

@interface Test : NSObject {}
+ (void) test;
@end

@implementation Test
+ (void) test
{
    Class GSStackTrace = objc_getClass("GSStackTrace");

    id stack = [GSStackTrace currentStack];

    for (int i = 0; i < [stack frameCount]; i++)
    {
        NSLog (@"%@", [[stack frameAt:i] function]);
    }
}
@end

int main(int argc, char **argv)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [Test test];

    [pool release];

    return 0;
}

Output (when compiled with debug symbols):

2010-10-18 14:14:46.188 a.out[29091] +[GSStackTrace currentStack]
2010-10-18 14:14:46.190 a.out[29091] +[Test test]
2010-10-18 14:14:46.190 a.out[29091] main
2010-10-18 14:14:46.190 a.out[29091] __libc_start_main

You may be able to pick apart GSStackTrace. It is a “private” class (that's why I need to use objc_getClass, you'll also get lots of unrecognised selector warnings), but it seems to contain all the code necessary to read Objective-C class names.

Tested on Ubuntu 9.04 with GNUstep configured with --enable-debug (so that GSFunctionInfo is included in the build).

I expect you'll need to ask the ObjC run time about the addresses to get symbol information. The addresses returned from backtrace() could probably be passed to something like object_getClass() to get the class, for example. I haven't tried any of this but it's where I'd look next in this case.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!