为什么回溯不包含Objective-C符号而不考虑-rdynamic?
更新:我正在Linux上使用GNU运行时。苹果运行时的MacOS上不会出现问题 更新2:我在MacOS上编译了GNU运行时,并用它构建了示例。该错误在带有GNU运行时的MacOS上不会发生。我认为问题在于glibc(因为为什么回溯不包含Objective-C符号而不考虑-rdynamic?,objective-c,linux,gcc,stack-trace,glibc,Objective C,Linux,Gcc,Stack Trace,Glibc,更新:我正在Linux上使用GNU运行时。苹果运行时的MacOS上不会出现问题 更新2:我在MacOS上编译了GNU运行时,并用它构建了示例。该错误在带有GNU运行时的MacOS上不会发生。我认为问题在于glibc(因为backtrace和backtrace\u符号都是glibc扩展) 在GCC编译的Objective-C应用程序中使用backtrace和backtrace\u符号打印回溯时,我没有得到任何Objective-C符号。仅显示文件名、地址和C符号 我用-g编译,并用-rdynami
backtrace
和backtrace\u符号
都是glibc扩展)
在GCC编译的Objective-C应用程序中使用backtrace
和backtrace\u符号
打印回溯时,我没有得到任何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;
}
有没有办法让Objective-C符号出现在此回溯中?我希望您需要询问ObjC运行时有关地址的信息,以获取符号信息。例如,从backtrace()返回的地址可能会被传递到类似object_getClass()的对象以获取类。我没有尝试过这些方法,但在本例中,我将在下一步进行研究。
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
…
您可以通过查看本地符号来验证是否从未返回本地符号backtrace_symbols()
在sysdeps/generic/elf/backtracesyms.c中定义。它依赖于elf/dl addr.c中定义的\u dl\u addr()
,为其提供符号名称。最终调用determinate\u info()
。如果可以,它将使用,其中不包括设计上的本地符号:
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\u info()
code会过滤掉除全局符号和弱符号以外的所有符号:
90 if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
91 || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)
要对Objective-C函数地址进行符号化,您必须自己执行查找,而不是过滤掉本地函数符号。此外,您必须要求Objective-C函数符号将
\u C\u TheObject\u\u printTrace
恢复到+[TheObject\u printTrace]
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;
}
您可能能够分离GSStackTrace
。它是一个“私有”类(这就是为什么我需要使用objc_getClass
,您还将收到许多无法识别的选择器警告),但它似乎包含读取Objective-C类名所需的所有代码
在Ubuntu9.04上测试,GNUstep配置了
——启用调试(因此GSFunctionInfo
包含在构建中)。Woah这对我来说似乎是非常推测性的backtrace
(和backtrace\u符号
)是glibc扩展,后者依赖于链接器-rdynamic
链接的符号。如果backtrace
返回的地址适用于Objective-C运行时,我会感到惊讶,但我还是会尝试一下。好的,我查阅了运行时API。我正在使用GNU运行时,但没有找到用于获取符号地址的类或方法的函数。在GNU运行时中,没有object\u getClass()
,而是一个object\u get\u class()
来获取一个对象。但是我非常确定,backtrace
的地址不是指向对象的。现在我确定:backtrace
的地址既不是对象也不是类。如果我知道你在使用GNU运行时,我就不会回答了,因为我不知道那里有什么可用或不可用。你应该从一开始就说清楚。我可以提出其他建议,但不是那些与您的环境相关的建议。嗯,我认为运行时并不重要。尽管如此,我今天还是会在Mac上尝试这个例子,如果它真的在Mac上起作用,我会澄清这个问题。答案很好,但我的应用程序必须使用标准的glibc运行,我无法复制和更改glibc代码,因为一些细节(如链接地图
结构的成员是私有的)。要知道为什么我接受了你的答案但没有给你奖金,请阅读我对dreamlax答案的评论。你赢得奖金是因为你的答案让我实现了一个有用的StackTrace,而没有高级第三方库(如GNUStep;))。但我会接受杰里米的回答,因为我犯了错误,提出了两个问题:1。为什么stacktrace不包括Obj-C符号和2。如何使用Obj-C符号创建stacktrace。显然,我开始悬赏是为了得到第二个问题的答案。但是第一个是标题,所以我接受杰里米的回答。
#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