3

更新:我正在使用 Linux 上的 GNU 运行时。在具有 Apple 运行时的 MacOS 上不会出现此问题。

更新 2:我在 MacOS 上编译了 GNU 运行时并用它构建了示例。在具有 GNU 运行时的 MacOS 上不会发生该错误。我会说问题出在 glibc(因为backtrace并且backtrace_symbols是 glibc 扩展)。

backtrace在使用and在 GCC 编译的 Objective-C 应用程序中打印回溯时backtrace_symbols,我没有得到任何 Objective-C 符号。仅显示文件名、地址和 C 符号。

我编译-g并与-rdynamic.

我的测试应用程序:

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;
}

它的输出:

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

有没有办法让 Objective-C 符号出现在这个回溯中?

4

3 回答 3

3

dladdr()只报告全局和弱符号。但是所有 Objective-C 函数符号都是本地的:

$ 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
…

您可以通过自己查看GNU libc 源代码来验证永远不会返回本地符号。backtrace_symbols()在 sysdeps/generic/elf/backtracesyms.c 中定义。它依赖于_dl_addr()在 elf/dl-addr.c 中定义的 为它提供符号名称。那最终调用determine_info()。如果可以,它会使用GNU hash table,它在设计上不包括本地符号:

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

如果 GNU 哈希表不存在,它会退回到标准哈希表。这包括所有符号,但determine_info()代码过滤掉了除全局符号和弱符号之外的所有符号:

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

要符号化 Objective-C 函数地址,您必须自己执行查找,而不是过滤掉本地函数符号。此外,您必须对 Objective-C 函数符号进行解构才能恢复_c_TheObject___printTrace+[TheObject _printTrace].

于 2010-10-18T00:50:30.930 回答
1

GNUstep 的NSException实现不使用backtrace,而是使用libbfd(二进制文件描述符)。我认为实际执行工作的函数被调用static void find_address,您可以在此处查看。使用这个简单的例子,我得到了以下结果。

#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;
}

输出(使用调试符号编译时):

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

你也许能挑开GSStackTrace。它是一个“私有”类(这就是我需要使用的原因objc_getClass,您还会收到很多无法识别的选择器警告),但它似乎包含读取 Objective-C 类名所需的所有代码。

在配置了 GNUstep 的 Ubuntu 9.04 上进行了测试--enable-debug(因此GSFunctionInfo包含在构建中)。

于 2010-10-18T03:16:37.967 回答
-1

我希望您需要向 ObjC 运行时询问地址以获取符号信息。例如,从 backtrace() 返回的地址可能会传递给 object_getClass() 之类的东西来获取类。我没有尝试过任何这些,但在这种情况下,这是我接下来要看的地方。

于 2010-09-30T16:01:38.567 回答