Hinzufügen von führenden Unterstrichen zu Assemblysymbolen mit GCC unter Win32?
Lesezeit: 7 Minuten
Maks Verver
Ich habe ein Stück C-Code, der eine in Assembly definierte Funktion aufruft. Nehmen wir als Beispiel an, dass foo.c Folgendes enthält:
int bar(int x); /* returns 2x */
int main(int argc, char *argv[]) { return bar(7); }
Und bar.s enthält die Implementierung von bar() in der x86-Assembly:
.global bar
bar: movl 4(%esp), %eax
addl %eax, %eax
ret
Unter Linux kann ich diese Quellen mit GCC wie folgt einfach kompilieren und verknüpfen:
% gcc -o test foo.c bar.s
% ./test; echo $?
14
Unter Windows mit MinGW schlägt dies mit dem Fehler “undefinierter Verweis auf `bar'” fehl. Es stellt sich heraus, dass die Ursache dafür darin besteht, dass unter Windows allen Bezeichnern von Funktionen mit C-Aufrufkonvention ein Unterstrich vorangestellt ist, aber da “bar” in Assembly definiert ist, erhält es dieses Präfix nicht und die Verknüpfung schlägt fehl. (Die Fehlermeldung beschwert sich also tatsächlich über das Fehlen des Symbols _bar, nicht bar.)
Zusammenfassen:
% gcc -c foo.c bar.s
% nm foo.o bar.o
foo.o:
00000000 b .bss
00000000 d .data
00000000 t .text
U ___main
U _bar
00000000 T _main
bar.o:
00000000 b .bss
00000000 d .data
00000000 t .text
00000000 T bar
Die Frage ist jetzt: Wie kann ich das schön lösen? Wenn ich nur für Windows schreiben würde, könnte ich einfach den Unterstrich zum Bezeichner in bar.s hinzufügen, aber dann bricht der Code unter Linux ab. Ich habe mir gcc angeschaut -fleading-underscore und -fno-leading-underscore Optionen, aber keine scheint etwas zu tun (zumindest unter Windows).
Die einzige Alternative, die ich jetzt sehe, besteht darin, die Assembly-Datei durch den C-Präprozessor zu leiten und alle deklarierten Symbole manuell neu zu definieren, wenn WIN32 definiert ist, aber das ist auch nicht sehr schön.
Hat jemand eine saubere Lösung dafür? Vielleicht eine Compiler-Option, die ich beaufsichtigt habe? Vielleicht unterstützt der GNU-Assembler eine Möglichkeit zu spezifizieren, dass sich dieses bestimmte Symbol auf eine Funktion bezieht, die die C-Aufrufkonvention verwendet, und als solche entstellt werden sollte? Irgendwelche anderen Ideen?
Eine Option, obwohl gefährlich, besteht darin, GCC davon zu überzeugen, den ABI-erforderlichen führenden Unterstrich wegzulassen.
Diese Option und ihr Gegenstück, -fno-leading-underscore, ändern Sie zwangsweise die Art und Weise, wie C-Symbole in der Objektdatei dargestellt werden. Eine Verwendung besteht darin, bei der Verknüpfung mit Legacy-Assembly-Code zu helfen.
Warnung: das -fleading-underscore switch bewirkt, dass GCC Code generiert, der nicht binärkompatibel mit Code ist, der ohne diesen Schalter generiert wurde. Verwenden Sie es, um sich an eine nicht standardmäßige binäre Anwendungsschnittstelle anzupassen. Nicht alle Ziele bieten vollständige Unterstützung für diesen Schalter.
Eine andere, sicherere Option besteht darin, GCC den zu verwendenden Namen explizit mitzuteilen.
Sie können den Namen angeben, der im Assembler-Code für eine C-Funktion oder -Variable verwendet werden soll, indem Sie schreiben asm (oder __asm__) Schlüsselwort nach dem Deklarator wie folgt:
int foo asm ("myfoo") = 2;
Dies gibt den Namen an, der für die Variable verwendet werden soll foo im Assemblercode sollte “myfoo' rather than the usual \``_foo‘.
Auf Systemen, auf denen dem Namen einer C-Funktion oder -Variable normalerweise ein Unterstrich vorangestellt wird, können Sie mit dieser Funktion Namen für den Linker definieren, die nicht mit einem Unterstrich beginnen.
Es ist nicht sinnvoll, diese Funktion mit einer nicht statischen lokalen Variablen zu verwenden, da solche Variablen keine Assembler-Namen haben. Wenn Sie versuchen, die Variable in ein bestimmtes Register zu schreiben, siehe Explizite Reg-Variablen. GCC akzeptiert solchen Code derzeit mit einer Warnung, wird aber wahrscheinlich geändert, um in Zukunft einen Fehler statt einer Warnung auszugeben.
Sie können nicht verwenden asm auf diese Weise in einer Funktion Definition; Sie können jedoch den gleichen Effekt erzielen, indem Sie eine Deklaration für die Funktion vor ihrer Definition und Platzierung schreiben asm dort so:
Sie müssen sicherstellen, dass die von Ihnen gewählten Assembler-Namen nicht mit anderen Assembler-Symbolen in Konflikt stehen. Außerdem dürfen Sie keinen Registernamen verwenden; das würde völlig ungültigen Assembler-Code erzeugen. GCC verfügt noch nicht über die Fähigkeit, statische Variablen in Registern zu speichern. Vielleicht kommt das noch hinzu.
In Ihrem Fall,
extern int bar(int x) asm("bar");
sollte GCC sagen, dass “bar verwendet den asm-Namen “bar“, obwohl es sich um eine ccall-Funktion handelt”.
Bedeutet dies, dass das Standardverhalten von GCC unter Linux darin bestehen sollte, dass die “C”-Namen einen führenden Unterstrich haben? Wenn ja, gibt es etwas in der Build-Umgebung des OP, das es ausschaltet?
– Michael Burr
24. Juni 2009 um 0:35 Uhr
Unter Linux werden Funktionen, die der standardmäßigen C-Aufrufkonvention folgen (ccall, cdecl, wie auch immer Sie es nennen möchten), nicht dekoriert. Unter Windows ist stdcall die “Standard”-Aufrufkonvention, und Funktionen, die irgendetwas anderem folgen (wie die standardmäßige C-Aufrufkonvention), werden dekoriert.
– vergänglich
24. Juni 2009 um 0:46 Uhr
Wie gesagt, die Optionen -fleading-underscore und -fno-leading-underscore schienen nichts zu bewirken (sie entfernen weder die Unterstriche in den C-Funktionen noch fügen sie sie für die Assembly-Symbole hinzu); Wenn Sie ein bisschen googeln, werden Sie sehen, dass andere die gleiche Erfahrung gemacht haben, also habe ich den Eindruck, dass diese Optionen ziemlich nutzlos sind. Der Vorschlag von asm() ist gut; Das kann ich am Ende verwenden. Der einzige Nachteil ist, dass das Symbol selbst immer noch nicht den richtigen Namen hat (was unter Windows _bar wäre), aber zumindest kann ich ohne weitere Quellcodeänderungen auf beiden Plattformen verlinken.
Du brauchst nicht extern im Prototyp. godbolt.org/z/kpFShC zeigt, dass void start(void) asm("_mystart"); funktioniert. Außerdem möchten Sie in Ihrem Beispiel wahrscheinlich einen Typnamen, anstatt den alten C-Standardwert zu verwenden.int.
– Peter Cordes
20. Februar 2020 um 3:39 Uhr
Sie können den C-Präprozessor verwenden, um Ihre Assembly vorzuverarbeiten, und ein Makro verwenden, um die fehlenden Unterstriche unter Windows hinzuzufügen. Zuerst müssen Sie Ihre Assembly-Datei von bar.s in bar.S (großes ‘S’) umbenennen. Dies weist gcc an, cpp zu verwenden, um die Datei vorzuverarbeiten.
Um die fehlenden Unterstriche hinzuzufügen, können Sie ein Makro “cdecl” wie folgt definieren:
.global cdecl(bar)
cdecl(bar):
movl 4(%esp), %eax
addl %eax, %eax
ret
Beachten Sie, dass Mac OSX auch führende Unterstriche erfordert, sodass Sie die erste Zeile des Makros wie folgt aktualisieren können:
#if defined(__WIN32__) || defined(__APPLE__)
kannst du es zweimal deklarieren?
.global bar
.global _bar
Ich habe seit einiger Zeit keine Assembler mehr geschrieben, aber verhält sich die .global-Kennung einfach wie eine Art Label?
Die .global-Direktive gibt nur an, dass sich dieser Bezeichner auf ein globales Symbol bezieht, sodass dies funktionieren könnte, wenn ich auch zwei Labels für die Funktion definiere, z. B.: .global bar .global _bar bar: _bar: Neben dem Durch die Vervielfältigung erhalte ich auch ein nutzloses „Balken“-Symbol unter Windows und ein nutzloses „_bar“-Symbol unter Linux. Ich hatte auf etwas Saubereres gehofft, aber das funktioniert, also danke ich Ihnen für den Vorschlag.
– Maks Verver
24. Juni 2009 um 14:11 Uhr
Compiler für das ELF-Ziel fügen standardmäßig keine führenden Unterstriche hinzu. Sie könnten hinzufügen -fleading-underscore beim Kompilieren ins ELF-Format (unter Linux). Verwenden Sie eine Bedingung im Makefile.
Hatte dieses Problem mit JNI, wo zB die Java Runtime 1.5/32Bit führende Unterstriche für externe C-Funktionen erwartet. “-fleading-underscore” macht den Job nicht!
Die Lösung bestand hier darin, die Stub-Header des Java-Compilers auf deklarierte Funktionen zu parsen und Substitutionsdefinitionen für diese mit dem Muster ´#define bar _bar´ abzuleiten.
13848100cookie-checkHinzufügen von führenden Unterstrichen zu Assemblysymbolen mit GCC unter Win32?yes