2010-08-12 7 views
8

Si desea llamar a una función de C/C++ de ensamblado en línea, usted puede hacer algo como esto:llamada directa mediante ensamblado en línea de GCC

void callee() {} 
void caller() 
{ 
    asm("call *%0" : : "r"(callee)); 
} 

GCC entonces emitir código que se parece a esto:

movl $callee, %eax 
call *%eax 

Esto puede ser problemático ya que la llamada indirecta destruirá la tubería en las CPU más antiguas.

Dado que la dirección de callee es eventualmente una constante, se puede imaginar que sería posible usar la restricción i. Citando del CCG en línea docs:

`i'

An immediate integer operand (one with constant value) is allowed. This includes symbolic constants whose values will be known only at assembly time or later.

si trato de utilizar de esta manera:

asm("call %0" : : "i"(callee)); 

me sale el siguiente error desde el ensamblador:

Error: suffix or operands invalid for `call'

Esto se debe a GCC emite el código

call $callee 

En lugar de

call callee 

Así que mi pregunta es si es posible hacer que la salida del CCG correcta call.

+1

¿Estás seguro de que la llamada indirecta destruye la tubería? ¿Has realizado una evaluación comparativa? Tengo entendido que en los viejos días de x86 (pre-i686), las llamadas indirectas eran muy malas (recuerdo que eran 10-100 veces más lentas en mi K6), pero hoy en día las cpus son más inteligentes y pueden manejarlas bien . ¡Entonces haz algunas pruebas antes de saltar a conclusiones! –

+0

@R ..: Tiene razón: si comparo esto con una CPU real, no hace ninguna diferencia. Estoy ejecutando mi código en qemu, sin embargo, y parece hacer una diferencia allí (alrededor de un 20% más ciclos/llamada). – Job

+0

Entonces, simplemente me quedaré con la forma en que lo haces, con la llamada indirecta. Esto permitirá a gcc generar el código correcto para las bibliotecas/ejecutables PIC/PIE sin tener que insertar hacks especiales para manejar estas cosas. –

Respuesta

10

me dieron el answer de la lista de correo de GCC:

asm("call %P0" : : "i"(callee)); 

Ahora sólo tengo que averiguar lo que realmente significa %P0, ya que parece ser una característica no documentada ...

Editar: Después de mirar el código fuente de GCC, no está exactamente claro qué significa el código P frente a una restricción. Pero, entre otras cosas, evita que GCC ponga un $ frente a valores constantes. Que es exactamente lo que necesito en este caso.

+0

Ver también https://stackoverflow.com/questions/37639993/is-this-assembly-function-call-safe-complete for ** los peligros de usar 'call' desde inline asm **. Básicamente, es imposible hacerlo de manera confiable para el Sistema V x86-64 (Linux), porque no se puede decir al compilador que se quiere vencer a la zona roja. En general, es una mala idea. Consulte https://stackoverflow.com/questions/7984209/calling-a-function-in-gcc-inline-assembly. –

0

Qué tal.

asm volatile ("call $callee"); 

ya que conoce el nombre del símbolo, de todos modos.

Editar: El punto que quería hacer es que no tiene que pasar por las convenciones de gcc para los argumentos asm aquí, sino que solo tiene que hacer el procesamiento de texto para hacer el trabajo.

+0

Bueno, ese sería mi último recurso (debería ser 'asm (" call callee ");', though). El problema es que necesitarías el nombre del símbolo mutilado. Este no es un gran problema en C, sino un PITA en C++. – Job

+0

Esto debería ser 'asm (" call callee ");', pero en C++ esto sería algo así como 'asm (" call _ZL4callv ");' –

+4

Esto no funciona, por varias razones. Uno, diferentes sistemas operativos (en el mismo hardware) tienen diferentes convenciones sobre cómo se asignan los nombres de símbolos (la rareza más común es anteponer un guión bajo). Y dos, a menos que se le diga a gcc de alguna manera que el asm usa la función, muy bien podría nunca omitir una versión invocable independiente de la función. Por ejemplo, puede alinearlo en cualquier otro lugar que se use, o puede omitirlo por completo si nunca se lo llama desde el código C. Tres, probablemente haya problemas con el código PIC y PIE ... –

0

El truco es la concatenación literal de cadenas. Antes de que GCC empiece a tratar de obtener un significado real de su código, concatenará literales adyacentes, de modo que aunque las cadenas de ensamblaje no sean las mismas que las utilizadas en su programa, deben concatenarse si lo hace:

#define ASM_CALL(X) asm("\t call " X "\n") 


int main(void) { 
    ASM_CALL("my_function"); 
    return 0; 
} 

dado que está utilizando GCC también se podría hacer

#define ASM_CALL(X) asm("\t call " #X "\n") 

int main(void) { 
    ASM_CALL(my_function); 
    return 0; 
} 

Si no lo sabe ya que debe ser consciente de que llamar a las cosas de ensamblado en línea es muy complicado. Cuando el compilador genera sus propias llamadas a otras funciones, incluye código para configurar y restaurar cosas antes y después de la llamada. Sin embargo, no sabe que debería hacer algo de esto por su llamada. Tendrá que incluirlo usted mismo (es muy difícil hacerlo bien y puede romper con una actualización del compilador o indicadores de compilación) o asegurarse de que su función esté escrita de tal manera que no parezca haber cambiado ningún registro o condición del pila (o variable en ella).

editar esto solo funcionará para nombres de funciones C, no C++, ya que están destrozados.

+1

No, esto no funciona en absoluto.Primero, el '$' es totalmente incorrecto para la llamada, segundo, no quiere "mi_función" en la cadena asm, sino el ** nombre destrozado que genera C++ **. –

+0

No vi que esto fuera para C++ también. En ese caso, es probable que esto no funcione si el nombre de la función está sobrecargado sin muchos aros. Acabo de copiar el "$" de otra publicación ya que no recordaba la sintaxis del asma x86. Reparar eso ... – nategoose

0

Si va a generar código de 32 bits (por ejemplo, la opción -m32 gcc), la siguiente línea asm emite una llamada directa:

asm ("call %0" :: "m" (callee)); 
1

Tal vez me estoy perdiendo algo aquí, pero

extern "C" void callee(void) 
{ 

} 

void caller(void) 
{ 
    asm("call callee\n"); 
} 

debería funcionar bien. Necesita una "C" externa para que el nombre no se decore en función de las reglas de creación de nombres de C++.

Cuestiones relacionadas