views:

140

answers:

3

Update: I'm working with the GNU-runtime on Linux. The problem does not occur on MacOS with the Apple-runtime.

Update 2: I compiled the GNU-runtime on MacOS and build the example with it. The error does not occur on MacOS with the GNU-runtime. I would say the problem is the glibc (since backtrace and backtrace_symbols are glibc extensions).

When printing a backtrace in a GCC compiled Objective-C app using backtraceand backtrace_symbols, I don't get any Objective-C symbols. Only the filenames, addresses and C-symbols appear.

I compiled with -g and linked with -rdynamic.

My test app:

void _printTrace()
{
    void *addr[1024];
    int aCount = backtrace(addr, 1024);
    char **frameStrings = backtrace_symbols(addr, aCount);
    for (int i = 0; i < aCount; i++) {
        printf("%s\n", frameStrings[i]);
    }
    free(frameStrings);
}

@interface TheObject
+ (void)_printTrace;
+ (void)printTrace;
@end

@implementation TheObject
+ (void)_printTrace
{
    _printTrace();
}

+ (void)printTrace
{
    [self _printTrace];
}
@end

void printTrace()
{
    [TheObject printTrace];
}

int main(int argc, char **argv)
{
    printTrace();
    return 0;
}

and it's output:

./test.bin(_printTrace+0x1f) [0x8048e05]
./test.bin() [0x8048e60]
./test.bin() [0x8048e8b]
./test.bin(printTrace+0x34) [0x8048ec5]
./test.bin(main+0xf) [0x8048eda]
/lib/libc.so.6(__libc_start_main+0xe5) [0xb7643bb5]
./test.bin() [0x8048b51]

Is there a way to let the Objective-C symbols appear in this backtrace?

A: 

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.

Tom Harrington
Woah that seems very speculative to me. `backtrace` (and `backtrace_symbols`) are glibc extensions where the latter relies on symbols linked in by the linker `-rdynamic`. I would be surprised if the addresses returned by `backtrace` are good for anything with the Objective-C runtime but I will try it nevertheless.
Tilo Prütz
Okay, I looked up the runtime API. I am using the GNU runtime and I did not found a function for getting the class or the method of a symbol address. In the GNU runtime there is no `object_getClass()` but an `object_get_class()` which gets an object. But I am very sure, that the addresses of `backtrace` are not pointing to objects.
Tilo Prütz
And now I'm sure: the addresses of `backtrace` are __neither__ objects __nor__ classes.
Tilo Prütz
If I had known you were using the GNU runtime I wouldn't have answered, because I have no idea what is or is not available there. You should have made this clear from the start. I could make other suggestions but not ones that would be relevant to your environment.
Tom Harrington
Hmm, I don't think that the runtime does matter. Nevertheless I will try the example on Mac today and if it does work there, I will clarify the question.
Tilo Prütz
Okay, finally I got it compiled on MacOS (what a mess…). And it works there. I will clarify the question.
Tilo Prütz
+1  A: 

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

Jeremy W. Sherman
Tilo Prütz
To know why I accepted your answer but did not give you the bounty please read my comment to dreamlax's answer.
Tilo Prütz
+1  A: 
dreamlax
You won the bounty because your answer led me to an implementation for useful stacktraces without high level 3rd party libraries (like GNUStep ;)). But I will accept Jeremy's answer because I made the fault to ask two questions: 1. Why does the stacktrace does not include Obj-C symbols and 2. How can I have a stacktrace with Obj-C symbols. Obviously I started the bounty to get an answer to the second question. But the first one was the headline and so I will accept Jeremy's answer.
Tilo Prütz