我正在处理objective运行时,试图编译objective代码,而不将其与libobjc链接,并且程序出现了一些分段错误问题,因此我从它生成了一个汇编文件。我认为没有必要显示整个程序集文件。在我的main函数的某一点上,我得到了以下行(顺便说一句,这是我得到seg错误的行):
callq *l_objc_msgSend_fixup_alloc以下是l_objc_msgSend_fixup_alloc的定义
.hidden l_objc_msgSend_fixup_alloc # @"\01l_objc_msgSend_fixup_alloc"
.type l_objc_msgSend_fixup_alloc,@object
.section "__DATA, __objc_msgrefs, coalesced","aw",@progbits
.weak l_objc_msgSend_fixup_alloc
.align 16
l_objc_msgSend_fixup_alloc:
.quad objc_msgSend_fixup
.quad L_OBJC_METH_VAR_NAME_
.size l_objc_msgSend_fixup_alloc, 16我已经将objc_msgSend_fixup重新实现为一个函数(id objc_msgSend_fixup(id self, SEL op, ...)),它返回nil (只是为了查看发生了什么),但是这个函数甚至没有被调用(程序在调用它之前崩溃)。
所以,我的问题是,callq *l_objc_msgSend_fixup_alloc应该做什么,objc_msgSend_fixup (l_objc_msgSend_fixup_alloc:之后)应该是什么(函数还是对象)?
编辑
为了更好地解释,我不会将源文件链接到objc库。我想做的是实现libray的一些部分,看看它是如何工作的。下面是我所做的事情的一个方法:
#include <stdio.h>
#include <objc/runtime.h>
@interface MyClass {
}
+(id) alloc;
@end
@implementation MyClass
+(id) alloc {
// alloc the object
return nil;
}
@end
id objc_msgSend_fixup(id self, SEL op, ...) {
printf("Calling objc_msgSend_fixup()...\n");
// looks for the method implementation for SEL in self's method list
return nil; // Since this is just a test, this function doesn't need to do that
}
int main(int argc, char *argv[]) {
MyClass *m;
m = [MyClass alloc]; // At this point, according to the assembly code generated
// objc_msgSend_fixup should be called. So, the program should, at least, print
// "Calling objc_msgSend_fixup()..." on the screen, but it crashes before
// objc_msgSend_fixup() is called...
return 0;
}如果运行时需要访问对象的vtable或obect类的方法列表以找到要调用的正确方法,那么实际执行此操作的函数是什么?我认为这是objc_msgSend_fixup,在这种情况下。因此,当调用objc_msgSend_fixup时,它接收一个对象作为其参数之一,如果该对象尚未初始化,则该函数将失败。
因此,我已经实现了我自己版本的objc_msgSend_fixup。根据上面的组装源,应该调用它。函数是否真的在寻找作为参数传递的选择器的实现并不重要。我只想叫objc_msgSend_lookup。但是,它没有被调用,也就是说,查找对象数据的函数甚至没有被调用,而不是被调用并导致错误(因为它返回一个nil (顺便说一句,这并不重要)。程序seg在调用objc_msgSend_lookup之前失败.
编辑2
一个更完整的程序集片段:
.globl main
.align 16, 0x90
.type main,@function
main: # @main
.Ltmp20:
.cfi_startproc
# BB#0:
pushq %rbp
.Ltmp21:
.cfi_def_cfa_offset 16
.Ltmp22:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Ltmp23:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movl $0, %eax
leaq l_objc_msgSend_fixup_alloc, %rcx
movl $0, -4(%rbp)
movl %edi, -8(%rbp)
movq %rsi, -16(%rbp)
movq L_OBJC_CLASSLIST_REFERENCES_$_, %rsi
movq %rsi, %rdi
movq %rcx, %rsi
movl %eax, -28(%rbp) # 4-byte Spill
callq *l_objc_msgSend_fixup_alloc
movq %rax, -24(%rbp)
movl -28(%rbp), %eax # 4-byte Reload
addq $32, %rsp
popq %rbp
ret对于l_objc_msgSend_fixup_alloc,我们有:
.hidden l_objc_msgSend_fixup_alloc # @"\01l_objc_msgSend_fixup_alloc"
.type l_objc_msgSend_fixup_alloc,@object
.section "__DATA, __objc_msgrefs, coalesced","aw",@progbits
.weak l_objc_msgSend_fixup_alloc
.align 16
l_objc_msgSend_fixup_alloc:
.quad objc_msgSend_fixup
.quad L_OBJC_METH_VAR_NAME_
.size l_objc_msgSend_fixup_alloc, 16对于L_OBJC_CLASSLIST_REFERENCES_$_
.type L_OBJC_CLASSLIST_REFERENCES_$_,@object # @"\01L_OBJC_CLASSLIST_REFERENCES_$_"
.section "__DATA, __objc_classrefs, regular, no_dead_strip","aw",@progbits
.align 8
L_OBJC_CLASSLIST_REFERENCES_$_:
.quad OBJC_CLASS_$_MyClass
.size L_OBJC_CLASSLIST_REFERENCES_$_, 8OBJC_CLASS_$_MyClass是指向MyClass结构定义的指针,编译器也生成了该定义,它也存在于程序集代码中。
发布于 2013-03-09 17:01:18
要理解objc_msgSend_fixup是什么以及它做了什么,就必须确切地知道如何在Objective中执行消息发送。有一天,所有ObjC程序员都听说编译器将[obj message]语句转换为objc_msgSend(obj, sel_registerName("message"))调用。然而,这并不完全准确。
为了更好地理解我的解释,请考虑以下ObjC片段:
[obj mesgA];
[obj mesgB];
[obj mesgA];
[obj mesgB];在这个片段中,向obj发送两条消息,每条消息发送两次。因此,您可以想象生成了以下代码:
objc_msgSend(obj, sel_registerName("mesgA"));
objc_msgSend(obj, sel_registerName("mesgB"));
objc_msgSend(obj, sel_registerName("mesgA"));
objc_msgSend(obj, sel_registerName("mesgB"));然而,sel_registerName可能太昂贵,每当调用特定方法时都调用它,这不是明智的做法。然后,编译器为要发送的每条消息生成如下结构:
typedef struct message_ref {
id (*trampoline) (id obj, struct message_ref *ref, ...);
union {
const char *str;
SEL sel;
};
} message_ref;因此,在上面的示例中,当程序启动时,我们有如下所示:
message_ref l_objc_msgSend_fixup_mesgA = { &objc_msgSend_fixup, "mesgA" };
message_ref l_objc_msgSend_fixup_mesgB = { &objc_msgSend_fixup, "mesgB" };当需要将这些消息发送到obj时,编译器将生成相当于以下内容的代码:
l_objc_msgSend_fixup_mesgA.trampoline(obj, &l_objc_msgSend_fixup_mesgA, ...); // [obj mesgA];
l_objc_msgSend_fixup_mesgB.trampoline(obj, &l_objc_msgSend_fixup_mesgB, ...); // [obj mesgB];在程序启动时,消息引用蹦床是指向objc_msgSend_fixup函数的指针。对于每个message_ref,当第一次调用其trampoline指针时,objc_msgSend_fixup将被调用,接收消息必须发送到的obj和调用消息的message_ref结构。因此,objc_msgSend_fixup必须做的是获取要调用的消息的选择器。因为,对于每个消息引用,这只需执行一次,因此objc_msgSend_fixup还必须将ref的trampoline字段替换为指向未修复消息选择器的另一个函数的指针。这个函数称为objc_msgSend_fixedup (选择器已经安装)。既然已经设置了消息选择器,并且不需要再次这样做,objc_msgSend_fixup就只调用objc_msgSend_fixedup,而只调用objc_msgSend。在此之后,如果消息引用的trampoline再次被调用,它的选择器已经被修复,而objc_msgSend_fixedup是被调用的。
简而言之,我们可以这样编写objc_msgSend_fixup和objc_msgSend_fixedup:
id objc_msgSend_fixup(id obj, struct message_ref *ref, ...) {
ref->sel = sel_registerName(ref->str);
ref->trampoline = &objc_msgSend_fixedup;
objc_msgSend_fixedup(obj, ref, ...);
}
id objc_msgSend_fixedup(id obj, struct message_ref *ref, ...) {
objc_msgSend(obj, ref->sel, ...);
}这使得消息发送速度快得多,因为只有在第一次调用消息时(通过objc_msgSend_fixup)才会发现合适的选择器。在以后的调用中,已经找到了选择器,并直接用objc_msgSend (由objc_msgSend_fixedup调用)调用消息。
在问题的汇编代码中,l_objc_msgSend_fixup_alloc是alloc方法的message_ref结构,分割错误可能是由于第一个字段中的问题引起的(可能不是指向objc_msgSend_fixup.)
发布于 2012-09-01 14:25:24
好的,你的代码是目标C,而不是C。
编辑/关于objc_msgSend_fixup
objc_msgSend_fixup是内部目标-C运行时内容,用于使用C++样式方法vtable管理调用。
您可以在这里阅读一些有关这方面的文章:
编辑/结束
关于你的节段错误。
Objective使用运行时进行消息传递、分配等。
消息传递(方法调用)通常由objc_msgSend函数完成。
当你这样做的时候,你就会用到这个:
[ someObject someFunction: someArg ];它被翻译成:
objc_msgSend( someObject, @selector( someFunction ), someArg );因此,如果在这样的运行时函数(如objc_msgSend_fixup_alloc、)中存在分段错误,这当然意味着您对未初始化的指针(如果不使用)或对已释放的对象调用方法。
类似于:
NSObject * o;
[ o retain ]; // Will segfault somewhere in the Obj-C runtime in non ARC, as 'o' may point to anything.或者:
NSObject * o;
o = [ [ NSObject alloc ] init ];
[ o release ];
[ o retain ]; // Will segfault somewhere in the Obj-C runtime as 'o' is no longer a valid object address.因此,即使分段错误定位在运行时,这也是一个基本目标--C内存管理问题,在您自己的代码中。。
尝试启用NSZombie,它应该会有所帮助。
也可以试试静态分析器。
编辑2
它在运行时崩溃,因为运行时需要访问对象的vtable以找到要调用的正确方法。
由于对象无效,vtable查找将导致无效指针的取消引用。
这就是为什么分段断层位于这里。
编辑3
你说你和objc库没有链接。
你怎么称呼objc库?
我这样问是因为,正如我们在代码中所看到的,您确实使用了Objective编译器。
例如,您可能不会链接到提供基本对象的基金会框架,但是由于您使用的是objects编译器,所以libobjc库(提供运行时)仍然将被隐式链接。
你确定不是这样的吗?对生成的二进制文件尝试一个简单的nm。
编辑4
如果是这样的话,objc_msgSend_fixup并不是第一个用于重新创建运行时的函数。
在定义类时,运行时需要了解它,因此需要编写诸如objc_allocateClassPair和朋友之类的代码。
您还需要确保编译器不会使用快捷方式。
我在您的代码中看到了这样的东西:L_OBJC_CLASSLIST_REFERENCES_$_。
这个符号在你自己的版本中存在吗?
https://stackoverflow.com/questions/12228350
复制相似问题