What is `objc_msgSend_fixup`, exactly?

I was messing around with Objective-C trying to compile Objective-C code without linking it to libobjc , and I am having problems with segmentation problems with the program, so I generated the build file from This. I think it is not necessary to show the entire assembly file. At some point in my main function, I have the following line (which, incidentally, is the line after which I get a seg error):

 callq *l_objc_msgSend_fixup_alloc 

and here is the definition for 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 

I redefined objc_msgSend_fixup as a function ( id objc_msgSend_fixup(id self, SEL op, ...) ) that returns nil (just to find out what is happening), but this function is not even called (the program crashes before the call).

So my question is what is the callq *l_objc_msgSend_fixup_alloc and what objc_msgSend_fixup (after l_objc_msgSend_fixup_alloc: should be (function or object)?

Edit

To better explain, I am not linking my source file to the objc library. What I'm trying to do is implement some parts of libray, just to see how it works. Here is the approach to what I did:

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

If the runtime needs to access the vtable object or the list of methods of the obect class to find the correct method to call, what is the function that actually does this? I think in this case it is objc_msgSend_fixup . So, when objc_msgSend_fixup is objc_msgSend_fixup , it receives the object as one of its parameters, and if this object has not been initialized, the function does not work.

So, I implemented my own version of objc_msgSend_fixup . According to the build source above, it should be called. It does not matter if the function is really looking for an implementation of the selector passed as a parameter. I just want objc_msgSend_lookup to objc_msgSend_lookup called. But it is not called, that is, the function that searches for object data is not even called, but does not cause and causes an error (because it returns nil (which, by the way, is not the case)). The seg program fails before objc_msgSend_lookup is objc_msgSend_lookup ...

Edit 2

A more complete assembly fragment:

 .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 

For l_objc_msgSend_fixup_alloc we have:

 .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 

For 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_$_, 8 

OBJC_CLASS_$_MyClass is a pointer to the MyClass structure definition, which was also generated by the compiler and is also present in the assembly code.

+8
assembly segmentation-fault objective-c objective-c-runtime
source share
2 answers

To understand what objc_msgSend_fixup and what it does, you need to know exactly how the message is sent to Objective-C. All ObjC programmers once heard that the compiler translates the [obj message] statements into objc_msgSend(obj, sel_registerName("message")) calls objc_msgSend(obj, sel_registerName("message")) . However, this is not entirely accurate.

To better explain my explanations, consider the following code snippet:

 [obj mesgA]; [obj mesgB]; [obj mesgA]; [obj mesgB]; 

In this fragment, two messages are sent to obj , each of which is sent twice. So you can imagine that the following code is generated:

 objc_msgSend(obj, sel_registerName("mesgA")); objc_msgSend(obj, sel_registerName("mesgB")); objc_msgSend(obj, sel_registerName("mesgA")); objc_msgSend(obj, sel_registerName("mesgB")); 

However, sel_registerName can be too expensive and calling it whenever a particular method is called is not a reasonable task. Then the compiler creates such structures for each message sent:

 typedef struct message_ref { id (*trampoline) (id obj, struct message_ref *ref, ...); union { const char *str; SEL sel; }; } message_ref; 

So, in the example above, when the program starts, we have something like this:

 message_ref l_objc_msgSend_fixup_mesgA = { &objc_msgSend_fixup, "mesgA" }; message_ref l_objc_msgSend_fixup_mesgB = { &objc_msgSend_fixup, "mesgB" }; 

When these messages need to be sent to obj , the compiler generates code equivalent to the following:

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

When the program starts, the reference message trampolines are pointers to the objc_msgSend_fixup function. For each message_ref , when its trampoline pointer is called for the first time, objc_msgSend_fixup receives a call, receiving obj to which the message was sent, and the message_ref structure from which it was called. So what objc_msgSend_fixup should do is get a selector for the message being called. Since this needs to be done only once for each link to messages, objc_msgSend_fixup should also replace the trampoline ref field with a pointer to another function that does not fix the message selector. This function is called objc_msgSend_fixedup (selector fixed). Now that the message selector is set, and you don’t have to do it again, objc_msgSend_fixup just calls objc_msgSend_fixedup and it just calls objc_msgSend . After that, if the ref trampoline message is called again, its selector is already set, and objc_msgSend_fixedup is the one that is being called.

In short, we could write objc_msgSend_fixup and objc_msgSend_fixedup as follows:

 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, ...); } 

This greatly speeds up the sending of the message, since the corresponding selector opens only when the message is first called (via objc_msgSend_fixup ). On subsequent calls, the selector will already be found, and the message will be called directly using objc_msgSend (via objc_msgSend_fixedup ).

The l_objc_msgSend_fixup_alloc questions build code has a alloc structure and the segmentation error may have been caused by a problem in its first field (maybe this does not indicate objc_msgSend_fixup ...)

+8
source share

Ok, your code is Objective-C, not C.

Edit / About objc_msgSend_fixup

objc_msgSend_fixup is an internal Objective-C runtime material used to manage calls using the C ++ style vtable method.

You can read some articles about this here:

Change / Finish

Now about your segfault.

Objective-C uses a runtime for messaging, distribution, etc.

Message objc_msgSend (method invocation) is usually done using the objc_msgSend function.
This is what is used when you do:

 [ someObject someFunction: someArg ]; 

This is translated into:

 objc_msgSend( someObject, @selector( someFunction ), someArg ); 

So, if you have segfault in such a run-time function, for example objc_msgSend_fixup_alloc , this means that you are calling the uninitialized pointer method (if you are not using ARC) or on the freed object.

Something like:

 NSObject * o; [ o retain ]; // Will segfault somewhere in the Obj-C runtime in non ARC, as 'o' may point to anything. 

Or:

 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. 

Thus, even if the segfault location is at run time, this is by far the main problem of Objective-C memory management in your own code.

Try turning on NSZombie, this should help.
Also try a static analyzer.

Edit 2

Runtime failure because the runtime must access the vtable object in order to find the correct method to call.

Since the object is invalid, a search in vtable dereferens an invalid pointer.

That is why segfault is here.

Edit 3

You say you are not related to the objc library.
What do you call the "objc library"?

I ask about this because, as we see in your code, you are finally using the Objective-C compiler.

You cannot reference the "Foundation" framework, for example, which provides basic objects, but since you use the Objective-C compiler, the libobjc library (providing runtime) will still be implicitly linked.

Are you sure this is not so? Try a simple nm in your binary.

Change 4

If this is true, objc_msgSend_fixup not the first function to recreate the runtime.

As you define a class, the runtime should be aware of this, so you need to code material like objc_allocateClassPair and friends.

You also need to make sure that the compiler will not use shortcuts.

I saw you look like code: L_OBJC_CLASSLIST_REFERENCES_$_ .

Does this symbol exist in your own version?

+7
source share

All Articles