2011-02-27 43 views
8

Estoy tratando de usar el ensamblaje en línea ... He leído esta página http://www.codeproject.com/KB/cpp/edujini_inline_asm.aspx pero no puedo entender los parámetros que pasan a mi función.Uso del ensamblaje en línea en C/C++

Estoy escribiendo un ejemplo de escritura C .. este es mi cabecera de la función:

write2(char *str, int len){ 
} 

Y este es mi código de montaje:

global write2 
write2: 
    push ebp 
    mov ebp, esp 
    mov eax, 4  ;sys_write 
    mov ebx, 1  ;stdout 
    mov ecx, [ebp+8] ;string pointer 
    mov edx, [ebp+12] ;string size 
    int 0x80  ;syscall 
    leave 
    ret 

¿Qué tengo que hacer pasar ese código a la función C ... que estoy haciendo algo como esto:

write2(char *str, int len){ 
    asm ("movl 4, %%eax;" 
      "movl 1, %%ebx;" 
      "mov %1, %%ecx;" 
      //"mov %2, %%edx;" 
      "int 0x80;" 
      : 
      : "a" (str), "b" (len) 
    ); 
} 

Eso es porque no tengo una variable de salida, así que ¿cómo hacer yo ¿maneja eso? Además, con este código:

global main 
main: 
    mov ebx, 5866  ;PID 
    mov ecx, 9  ;SIGKILL 
    mov eax, 37  ;sys_kill 
    int 0x80  ;interruption 
    ret 

¿Cómo puedo poner ese código en línea en mi código .. por lo que puedo pedir el pid al usuario .. como esta .. Este es mi código previo

void killp(int pid){ 
    asm ("mov %1, %%ebx;" 
      "mov 9, %%ecx;" 
      "mov 37, %%eax;" 
      : 
      : "a" (pid)   /* optional */ 
    ); 
} 
+0

¿Desea pasar un parámetro de salida, junto con str, len? O ¿quiere decir que desea enviar un descriptor de archivo al que desea escribir el str? – Zimbabao

+0

Solo quiero pasar mi puntero de cadena y mi longitud de cadena ... exactamente como se ve en el código de ensamblaje ... De modo que, utilizando solo syscall, puedo imprimir mi cadena a la salida estándar. – RodrigoCR

Respuesta

10

Bueno, no dices específicamente, pero por tu publicación, parece que estás usando gcc y su ASM en línea con sintaxis de restricciones (otros compiladores de C tienen una sintaxis en línea muy diferente). Dicho esto, probablemente necesite utilizar AT & sintaxis de ensamblador T en lugar de Intel, ya que eso es lo que se usa con gcc.

Así que con lo anterior, vamos a ver su función write2. En primer lugar, no desea crear un marco de pila, ya que gcc lo creará, por lo que si crea uno en el código de asm, terminará con dos marcos, y las cosas probablemente se confundan. En segundo lugar, dado que gcc está diseñando el marco de pila, no puede acceder a vars con el anuncio "[ebp + offset]", no sabe cómo se distribuye. Para eso están las restricciones: usted dice qué tipo de lugar desea que gcc ponga el valor (cualquier registro, memoria, registro específico) y el uso de "% X" en el código asm. Finalmente, si utiliza registros explícitos en el código asm, debe enumerarlos en la 3ª sección (después de las restricciones de entrada) para que gcc sepa que los está utilizando. De lo contrario, podría poner un valor importante en uno de esos registros, y tú reducirías ese valor.

Así que con todo eso, su función parece write2:

void write2(char *str, int len) { 
    __asm__ volatile (
     "movl $4, %%eax;" 
     "movl $1, %%ebx;" 
     "movl %0, %%ecx;" 
     "movl %1, %%edx;" 
     "int $0x80" 
     :: "g" (str), "g" (len) 
     : "eax", "ebx", "ecx", "edx"); 
} 

Nota la sintaxis AT & T - src, en lugar de dest dest, src y % antes del nombre de registro.

Ahora esto funcionará, pero es ineficiente ya que contendrá muchos movs adicionales. En general, NUNCA deberías usar instrucciones mov o registros explícitos en el código asm, ya que es mucho mejor utilizar restricciones para decir dónde quieres que las cosas y que el compilador se asegure de que estén allí. De esta manera, el optimizador probablemente puede deshacerse de la mayoría de los movs, particularmente si se enlista la función (lo que hará si especifica -O3).Convenientemente, el modelo de máquina i386 tiene limitaciones para registros específicos, por lo que en su lugar puede hacer:

void write2(char *str, int len) { 
    __asm__ volatile (
     "movl $4, %%eax;" 
     "movl $1, %%ebx;" 
     "int $0x80" 
     :: "c" (str), /* c constraint tells the compiler to put str in ecx */ 
      "d" (len) /* d constraint tells the compiler to put len in edx */ 
     : "eax", "ebx"); 
} 

o incluso mejor

void write2(char *str, int len) { 
    __asm__ volatile ("int $0x80" 
     :: "a" (4), "b" (1), "c" (str), "d" (len)); 
} 

Tenga en cuenta también el uso de volatile que se necesita para decirle al compilador que esta no se puede eliminar como muerto aunque sus salidas (de las cuales no hay) no se utilicen.

edición

Una nota final - esta función está haciendo una llamada al sistema de escritura, lo que devuelve un valor en eax - ya sea el número de bytes escritos o un código de error. Así que usted puede conseguir que con una restricción de salida:

int write2(char *str, int len) { 
    __asm__ ("int $0x80" : "=a" (len) : "a" (4), "b" (1), "c" (str), "d" (len)); 
    return len; 
} 

Con una producción real, que puede o no quiere que el volátil - no tener que permitirá que el compilador de código muerto eliminar la escritura si el valor de retorno no se usa Pero siempre revisas el valor de retorno de los errores, ¿verdad?

+1

Se perdió una cosa al convertir a AT & T: las constantes necesitan un $ en frente de ellas. De lo contrario, son referencias de memoria, y estoy bastante seguro de que no desea realizar la interrupción que sea en la dirección 0x80. – ughoavgfhw

+0

Muchas gracias por la gran respuesta útil. Me di cuenta del AT & T sintax, así que modifiqué mi código ... Pero ya era demasiado tarde para que lo veas: P, ahora lo entiendo sobre las optimizaciones ... Entonces, tengo que poner int $ 0x80 ¿verdad? – RodrigoCR

+0

void write2 (char * str, int len) { __asm__ volátil ("int $ 0x80" :: "a" (4), "b" (1), "c" (str), "d" (len)); } Nota: ¡Esa es la respuesta correcta! intercambiar valores y usar $. ¡Gracias a los dos! – RodrigoCR

Cuestiones relacionadas