Was genau ist 'objc_msgSend_fixup'?

8

Ich mache mich mit der Objective-C-Laufzeit herum, versuche, objective-c-Code zu kompilieren, ohne ihn mit libobjc zu verbinden, und ich habe einige Segmentierungsfehler mit einem Programm, deshalb habe ich eine Assemblydatei erzeugt es. Ich denke, es ist nicht notwendig, die gesamte Assembly-Datei anzuzeigen. An einer Stelle meiner Funktion main habe ich die folgende Zeile (die übrigens die Zeile ist, nach der ich den Seg-Fehler erhalte):

%Vor%

und hier ist die Definition für l_objc_msgSend_fixup_alloc :

%Vor%

Ich habe objc_msgSend_fixup als eine Funktion ( id objc_msgSend_fixup(id self, SEL op, ...) ) neu implementiert, die nil zurückgibt (nur um zu sehen, was passiert), aber diese Funktion wird nicht einmal aufgerufen (das Programm stürzt ab, bevor es aufgerufen wird) / p>

Also, meine Frage ist, was soll callq *l_objc_msgSend_fixup_alloc tun und was soll objc_msgSend_fixup (nach l_objc_msgSend_fixup_alloc: ) sein (eine Funktion oder ein Objekt)?

Bearbeiten

Um das besser zu erklären, verbinde ich meine Quelldatei nicht mit der objc-Bibliothek. Was ich versuche zu tun, ist einige Teile der Bibliothek zu implementieren, nur um zu sehen, wie es funktioniert. Hier ist ein Ansatz von dem, was ich getan habe:

%Vor%

Wenn die Laufzeitumgebung auf die vtable des Objekts oder die Methodenliste der Klasse des Objekts zugreifen muss, um die richtige Aufrufmethode zu finden, welche Funktion hat das dann? Ich denke es ist objc_msgSend_fixup , in diesem Fall. Wenn also objc_msgSend_fixup aufgerufen wird, empfängt es ein Objekt als einen seiner Parameter, und wenn dieses Objekt nicht initialisiert wurde, schlägt die Funktion fehl.

Also, ich habe meine eigene Version von objc_msgSend_fixup implementiert. Laut der obigen Quelle sollte es aufgerufen werden. Es spielt keine Rolle, ob die Funktion tatsächlich nach der Implementierung des als Parameter übergebenen Selektors sucht. Ich möchte nur objc_msgSend_lookup aufgerufen werden. Aber es wird nicht aufgerufen, das heißt, die Funktion, die nach den Daten des Objekts sucht, wird nicht einmal aufgerufen, anstatt aufgerufen zu werden und einen Fehler zu verursachen (weil sie nil zurückgibt (was übrigens nicht der Fall ist) Angelegenheit)). Das Programm segelt fehl, bevor objc_msgSend_lookup aufgerufen wird ...

Bearbeiten 2

Ein vollständigerer Zusammenstellungsausschnitt:

%Vor%

Für l_objc_msgSend_fixup_alloc haben wir:

%Vor%

Für L_OBJC_CLASSLIST_REFERENCES_$_ :

%Vor%

OBJC_CLASS_$_MyClass ist ein Zeiger auf die MyClass struct-Definition, die ebenfalls vom Compiler generiert wurde und auch im Assembly-Code enthalten ist.

    
LuisABOL 01.09.2012, 13:55
quelle

2 Antworten

8

Um zu verstehen, was objc_msgSend_fixup ist und was es tut, müssen Sie genau wissen, wie Nachrichten in Objective-C gesendet werden. Alle ObjC-Programmierer haben eines Tages gehört, dass der Compiler [obj message] -Anweisungen in objc_msgSend(obj, sel_registerName("message")) -Anrufe umwandelt. Das stimmt jedoch nicht ganz.

Um meine Erklärung besser zu verstehen, betrachten Sie das folgende ObjC-Snippet:

%Vor%

In diesem Snippet werden zwei Nachrichten an obj gesendet, von denen jede zweimal gesendet wird. Sie können sich also vorstellen, dass der folgende Code generiert wird:

%Vor%

Allerdings kann sel_registerName zu teuer sein und es immer dann aufrufen, wenn eine bestimmte Methode aufgerufen wird. Dann generiert der Compiler für jede zu sendende Nachricht Strukturen wie diese:

%Vor%

Also, im obigen Beispiel, wenn das Programm startet, haben wir etwas wie folgt:

%Vor%

Wenn diese Nachrichten an obj gesendet werden müssen, generiert der Compiler folgenden Code:

%Vor%

Beim Programmstart sind die Nachrichtenreferenztrampoline Zeiger auf die Funktion objc_msgSend_fixup . Für jede message_ref , wenn ihr trampoline -Zeiger zum ersten Mal aufgerufen wird, wird objc_msgSend_fixup aufgerufen und erhält die obj , an die die Nachricht gesendet werden soll und die message_ref -Struktur, von der sie aufgerufen wurde. Also, was objc_msgSend_fixup tun muss, ist der Selektor für die Nachricht, die aufgerufen werden soll. Da dies für jede Nachrichtenreferenz nur einmal ausgeführt werden muss, muss objc_msgSend_fixup auch das Feld trampoline des Verweises durch einen Zeiger auf eine andere Funktion ersetzen, die den Selektor der Nachricht nicht korrigiert. Diese Funktion heißt objc_msgSend_fixedup (der Selektor wurde repariert). Nun, da der Nachrichtenselektor eingestellt wurde und dies nicht erneut durchgeführt werden muss, ruft objc_msgSend_fixup nur objc_msgSend_fixedup auf und ruft nur objc_msgSend auf. Wenn danach ein% code% der Nachrichtenreferenz erneut aufgerufen wird, ist sein Selektor bereits festgelegt, und trampoline ist derjenige, der aufgerufen wird.

Kurz gesagt, könnten wir objc_msgSend_fixedup und objc_msgSend_fixup folgendermaßen schreiben:

%Vor%

Dies macht das Senden von Nachrichten sehr viel schneller, da der entsprechende Selektor nur beim ersten Aufruf der Nachricht entdeckt wird (von objc_msgSend_fixedup ). Bei späteren Aufrufen wurde der Selektor bereits gefunden und die Nachricht wird direkt mit objc_msgSend_fixup (by objc_msgSend ) aufgerufen.

Im Assemblycode der Frage ist objc_msgSend_fixedup die l_objc_msgSend_fixup_alloc -Struktur der alloc -Methode, und der Segmentationsfehler wurde möglicherweise durch ein Problem in seinem ersten Feld verursacht (vielleicht zeigt es nicht auf message_ref ...)

    
LuisABOL 09.03.2013, 17:01
quelle
7

Ok, dein Code ist Objective-C, nicht C.

Bearbeiten / Über objc_msgSend_fixup

objc_msgSend_fixup ist eine interne Objective-C-Laufzeitkomponente, die zum Verwalten von Aufrufen mit einer C ++ - Methode vtable verwendet wird.

Sie können hier einige Artikel lesen:

Bearbeiten / Beenden

Nun zu Ihrem segfault.

Objective-C verwendet eine Laufzeit für Nachrichtenübergabe, Zuweisungen usw.

Die Nachrichtenübergabe (Methodenaufruf) wird normalerweise von der Funktion objc_msgSend ausgeführt.
Das ist, was Sie tun, wenn Sie das tun:

%Vor%

Es wurde übersetzt in:

%Vor%

Wenn Sie also in einer solchen Laufzeitfunktion einen segfault haben, wie objc_msgSend_fixup_alloc , , bedeutet das sicherlich, dass Sie eine Methode für einen nicht initialisierten Zeiger (wenn Sie ARC nicht verwenden) oder für ein freigegebenes Objekt aufrufen.

Etwas wie:

%Vor%

Oder:

%Vor%

Selbst wenn sich der segfault-Speicherort in der Laufzeit befindet, ist dies sicherlich ein grundlegendes Ziel-C-Speicherverwaltungsproblem in Ihrem eigenen Code.

Versuchen Sie, NSZombie zu aktivieren, es sollte helfen.
Versuchen Sie auch den statischen Analysator.

Bearbeiten 2

Es stürzt in der Laufzeit ab, weil die Laufzeit auf die vtable des Objekts zugreifen muss, um die richtige Aufrufmethode zu finden.

Da das Objekt ungültig ist, führt die Vtable-Suche zur Dereferenzierung eines ungültigen Zeigers.

Deshalb befindet sich der segfault hier.

Bearbeiten Sie 3

Sie sagen, dass Sie nicht mit der objc-Bibliothek verknüpft sind.
Wie nennen Sie die «objc library»?

Ich frage das, weil Sie, wie wir in Ihrem Code sehen, definitiv einen Objective-C-Compiler verwenden.

Sie dürfen beispielsweise nicht mit dem Framework "Foundation" verlinken, das die Basisobjekte bereitstellt, aber da Sie einen Objective-C-Compiler verwenden, ist die libobjc -Bibliothek (die die Laufzeit bereitstellt) wird immer noch implizit verknüpft sein.

Sind Sie sicher, dass das nicht der Fall ist? Probieren Sie eine einfache nm auf Ihre resultierende Binärdatei.

Bearbeiten 4

Wenn dies wirklich der Fall ist, ist objc_msgSend_fixup nicht die erste Funktion, um die Laufzeit neu zu erstellen.

Wenn Sie eine Klasse definieren, muss die Runtime darüber Bescheid wissen, also müssen Sie Dinge wie objc_allocateClassPair und Freunde codieren.

Sie müssen auch sicherstellen, dass der Compiler keine Verknüpfungen verwendet.

Ich habe in dir Code-Sachen gesehen wie: L_OBJC_CLASSLIST_REFERENCES_$_ .

Existiert dieses Symbol in Ihrer eigenen Version?

    
Macmade 01.09.2012 14:25
quelle