更新:我正在使用Linux上的GNU-runtime。使用Apple运行时在MacOS上出现不问题。
更新2:我在MacOS上编译了GNU-runtime并用它构建了示例。使用GNU运行时在MacOS上发生不错误。我会说问题是glibc(因为backtrace
和backtrace_symbols
是glibc扩展)。
使用backtrace
和backtrace_symbols
在GCC编译的Objective-C应用程序中打印回溯时,我没有得到任何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符号出现在这个回溯中?
答案 0 :(得分:2)
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 source code来验证是否永远不会返回本地符号。 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]
。
答案 1 :(得分:1)
GNUstep的NSException
实现不使用backtrace
,而是使用libbfd
(二进制文件描述符)。我认为实际完成工作的功能称为static void find_address
,您可以查看here。使用这个简单的例子,我得到了随后的结果。
#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类名所需的所有代码。 / p>
在Ubuntu 9.04上测试,GNUstep配置了--enable-debug
(因此构建中包含GSFunctionInfo
)。
答案 2 :(得分:-1)
我希望您需要向ObjC运行时询问地址以获取符号信息。例如,从backtrace()返回的地址可能会传递给类似object_getClass()的地址以获取类。我没有尝试过这个,但在这种情况下我会看到下一个。