ejecutar - gcc linux
¿Agregando guiones bajos principales a los símbolos de ensamblaje con GCC en Win32? (4)
¿Puedes declararlo dos veces?
.global bar
.global _bar
No he escrito ensamblado por un tiempo, pero ¿el identificador .global simplemente actúa como una especie de etiqueta?
Tengo un fragmento de código C que llama a una función definida en el ensamblaje. A modo de ejemplo, supongamos que foo.c contiene:
int bar(int x); /* returns 2x */
int main(int argc, char *argv[]) { return bar(7); }
Y bar.s contiene la implementación de bar () en el conjunto x86:
.global bar
bar: movl 4(%esp), %eax
addl %eax, %eax
ret
En Linux, puedo compilar y vincular fácilmente estas fuentes con GCC de la siguiente manera:
% gcc -o test foo.c bar.s
% ./test; echo $?
14
En Windows con MinGW, esto falla con un error de "referencia indefinida a` bar ''". Resulta que la causa de esto es que en Windows todos los identificadores de funciones con convención de llamadas C tienen como prefijo un subrayado, pero dado que "barra" está definida en ensamblaje, no obtiene este prefijo y falla el enlace. (Entonces, el mensaje de error se está quejando realmente por perder el símbolo _bar, no la barra.)
Para resumir:
% 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
La pregunta ahora es: ¿cómo puedo resolver esto bien? Si estuviera escribiendo solo para Windows, podría agregar el guión bajo al identificador en bar.s, pero luego el código se rompe en Linux. He analizado las -fleading-underscore
gcc''s -fleading-underscore
y -fno-leading-underscore
, pero ninguna parece hacer nada (al menos en Windows).
La única alternativa que veo ahora es pasar el archivo de ensamblaje a través del preprocesador de C y redefinir manualmente todos los símbolos declarados si se define WIN32, pero tampoco es muy bonito.
¿Alguien tiene una solución limpia para esto? Tal vez una opción de compilador que supervisé? ¿Tal vez el ensamblador de GNU admite una manera específica de que este símbolo particular se refiera a una función que usa la convención de llamadas C y debería ser mutilado como tal? ¿Alguna otra idea?
Los compiladores para el objetivo ELF no agregan caracteres de subrayado iniciales de manera predeterminada. Puede agregar -fleading-underscore
al compilar en formato ELF (bajo Linux). Use un condicional en el archivo MAKE.
Referencia: http://opencores.org/openrisc,gnu_toolchain (hacer una búsqueda en la página de "dejar sin cambios los nombres globales")
Puede usar el preprocesador C para preprocesar su ensamblaje y usar una macro para agregar los guiones bajos que faltan en Windows. En primer lugar, debe cambiar el nombre de su archivo de ensamblaje de bar.s a bar.S (''S'' mayúscula). Esto le dice a gcc que use cpp para preprocesar el archivo.
Para agregar los guiones bajos que faltan, puede definir una macro "cdecl" como esta:
#if defined(__WIN32__)
# define cdecl(s) _##s
#else
# define cdecl(s) s
#endif
Entonces úsalo así:
.global cdecl(bar)
cdecl(bar):
movl 4(%esp), %eax
addl %eax, %eax
ret
Tenga en cuenta que Mac OSX también requiere subrayados iniciales, por lo que puede actualizar la primera línea de la macro de esta manera:
#if defined(__WIN32__) || defined(__APPLE__)
Una opción, aunque peligrosa, es convencer a GCC para que omita el guión bajo requerido por ABI.
Esta opción y su contraparte,
-fno-leading-underscore
, cambian forzosamente la forma en que se representan los símbolos C en el archivo de objeto. Un uso es ayudar a vincular con el código de ensamblaje heredado.Advertencia: el
-fleading-underscore
hace que GCC genere código que no es binario compatible con el código generado sin ese interruptor. Úselo para ajustarse a una interfaz binaria de aplicación no predeterminada. No todos los objetivos brindan soporte completo para este cambio.
Otra opción más segura es indicar explícitamente a GCC el nombre que se debe usar.
5.39 Control de nombres usados en el código ensamblador
Puede especificar el nombre que se utilizará en el código ensamblador para una función o variable C escribiendo la palabra clave
asm
(o__asm__
) después del declarador de la siguiente manera:
int foo asm ("myfoo") = 2;
Esto especifica que el nombre que se utilizará para la variable
foo
en el código del ensamblador debe ser `` myfoo'' rather than the usual /``_foo
''.En los sistemas en los que normalmente se antepone un guion bajo al nombre de una función o variable C, esta característica le permite definir nombres para el enlazador que no comienzan con un guión bajo.
No tiene sentido utilizar esta característica con una variable local no estática ya que tales variables no tienen nombres de ensamblador. Si está tratando de poner la variable en un registro particular, vea Registros explícitos . Actualmente, GCC acepta dicho código con una advertencia, pero probablemente se modifique para emitir un error, en lugar de una advertencia, en el futuro.
No puede usar
asm
de esta manera en una definición de función; pero puede obtener el mismo efecto escribiendo una declaración para la función antes de su definición y poniendoasm
allí, así:
extern func () asm ("FUNC"); func (x, y) int x, y; /* ... */
Depende de usted asegurarse de que los nombres de ensamblador que elija no entren en conflicto con ningún otro símbolo de ensamblador. Además, no debes usar un nombre de registro; eso produciría un código ensamblador completamente inválido. GCC aún no tiene la capacidad de almacenar variables estáticas en los registros. Quizás eso se agregará.
En tu caso,
extern int bar(int x) asm("bar");
debería decirle a GCC que " bar
usa nombre asm` `bar` '', aunque es una función de llamada".