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):
und hier ist die Definition für l_objc_msgSend_fixup_alloc
:
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:
Für L_OBJC_CLASSLIST_REFERENCES_$_
:
OBJC_CLASS_$_MyClass
ist ein Zeiger auf die MyClass
struct-Definition, die ebenfalls vom Compiler generiert wurde und auch im Assembly-Code enthalten ist.
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:
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:
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:
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:
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
...)
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:
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?
Tags und Links objective-c assembly segmentation-fault objective-c-runtime