2011-11-08 50 views
5

Quiero usar un naked function en mi programa C++ utilizando g ++. Desafortunadamente, g ++, a diferencia de VC++, no es compatible con funciones simples y la única forma de administrar esto es escribir su propio código ensamblador en un archivo separado y vincularlo con sus archivos C++. Traté de encontrar un buen tutorial para x86 para mezclar el ensamblado y los archivos C/C++ pero no pude encontrar ninguno bueno.Mezcla C y archivos de ensamblaje

Por favor, hágamelo saber si conoce alguna. Tenga en cuenta que no estoy preguntando acerca del ensamblaje en línea, sino que vincula archivos C y ensamblados y maneras de declarar variables externas de C en ensamblaje y viceversa además de usarlas en C o ensamblaje, y también formas de vincular los archivos C y asm usando Makefile .

+0

Probar y hacer esto más específico - es bastante amplio y vago tal como está: de lo contrario, probablemente se cerrará como "no es una pregunta real". También recuerde el consejo anterior en sus preguntas anteriores sobre el uso de 'gcc -S ...' para producir plantillas asm, etc. –

+0

gcc en la misma línea tiene '__tributo__ ((desnudo))' pero no para x86 :( –

Respuesta

16

En archivo C++:

extern "C" void foo(); // Stop name mangling games 

int main() { 
    foo(); 
} 

en el archivo asm "desnudo", para x86:

# modified from http://asm.sourceforge.net/howto/hello.html 

.text     # section declaration 
    .global foo 

foo: 

# write our string to stdout 

    movl $len,%edx # third argument: message length 
    movl $msg,%ecx # second argument: pointer to message to write 
    movl $1,%ebx  # first argument: file handle (stdout) 
    movl $4,%eax  # system call number (sys_write) 
    int $0x80  # call kernel 

# and exit 

    movl $0,%ebx  # first argument: exit code 
    movl $1,%eax  # system call number (sys_exit) 
    int $0x80  # call kernel 

.data     # section declaration 

msg: 
    .ascii "Hello, world!\n" # our dear string 
    len = . - msg   # length of our dear string 

compilar, ensamblar y enlace (con g ++ en lugar de LD porque es mucho más fácil hacerlo de esa manera para C++) y ejecute:

[email protected]:/tmp > g++ -Wall -Wextra test.cc -c -o test.o 
[email protected]:/tmp > as -o asm.o asm.S 
[email protected]:/tmp > g++ test.o asm.o 
[email protected]:/tmp > ./a.out 
Hello, world! 

Es evidente que si quiere pasar argumentos a su función o devolver nada y Debes respetar las convenciones de llamadas.

1

Solo quiero agregar una cosa a la publicación anterior. Imagine que quiere una función, que aceptan argumentos: (algo así como

int add(int,int);

prototipo)

segment .text 
global add 

    add: 
    enter 0,0 
    mov eax,[ebp+8] ; first argument 
    mov ebx,[ebp+12] ; second argument 
    add eax,ebx 
    leave 
    ret 
+1

sí, excepto no use la instrucción lenta 'enter'. http://agner.org/optimize/.No hay ninguna razón para construir un marco de pila aquí en absoluto. Solo 'mov eax, [esp + 4]'/'agregar eax, [esp + 8]'/'ret'. Además, no golpee 'ebx'; es un registro de llamada preservada en todas las convenciones de llamadas habituales. Bajando la votación de un ejemplo peligroso (e ineficiente), y que no mostró cómo construir + vincular esto con NASM. –

5

He aquí un ejemplo de un truco para conseguir el efecto "función desnudo".

#include <stdio.h> 

extern "C" int naked_func(); 

static void 
dummy() 
{ 
    __asm__ __volatile__ 
    (
    " .global naked_func\n" 
    "naked_func:\n" 
    " movq $3141569, %rax\n" 
    " ret\n" 
    ); 
} 

int 
main() 
{ 
    printf ("%d\n", naked_func()); 
    return 0; 
} 
+2

Ni siquiera necesita la función ficticia como envoltorio, ya que el ensamblaje en línea básico se puede colocar en un ámbito global. –

0

Esta es mi manera de definir una función en el montaje, esto no necesita tener un ensamblador-archivo separado, ni se necesita para escapar de cada nueva línea. Puede copiar el contenido de los archivos de ensamblaje en el literal de cadena. Nota: El raw multiline string literal es una característica de C++ 11 (también etiquetó C++). Esto es útil, si desea compilar todo en un solo .c -/.cpp -file.

extern"C" int rand_byte(void); 
asm (R"(
    .globl rand_byte 
rand_byte: 
    call rand 
    and eax, 255 
    ret 
)"); 

Solo se puede usar una instrucción básica de ensamblaje sin parámetros adicionales en el alcance global. Al usar GCC o Clang y un procesador de brazo, puede usar [[gnu::naked]]/__attribute__((naked)).

[[gnu::naked]] 
int rand_byte(void) { 
    asm volatile (R"(
     push {lr} 
     bl rand 
     and r0, #255 
     pop {pc} 
    )"); 
}; 

La primera manera siempre permite definir las funciones desnudas. Esto también ayuda a crear un código más portátil.

extern"C" int _cdecl rand_byte(void); 
    #if defined __x86__ 
     // NOTE: the names are decorated with a '_' on windows 32-bit 
     // You can use extern"C" int _cdecl rand_byte() asm("rand_byte"); 
     // to make the asm symbol name always be rand_byte, without an _ 
     asm volatile (R"(
      .globl _rand_byte 
     _rand_byte: 
      call rand 
      and eax, 255 
      ret 
     )"); 
    #elif defined __x86_64__ 
     asm volatile (R"(
      .globl rand_byte 
     rand_byte: 
      call rand 
      and rax, 255 # eax works here, too. x86-32 and -64 could share the same source. 
      ret 
     )"); 
    #elif defined __arm__ 
     asm (R"(
      .global rand_byte 
     rand_byte: 
      push {lr} 
      bl rand 
      and r0, #255 
      pop {pc} 
     )"); 
    #else 
     #error There is no code for your platform yet... 
    #endif 
+0

Puede usar una instrucción ASM básica en el alcance global en GCC/G ++. No puede usar el ensamblaje extendido en línea en el alcance global. Una instrucción de ensamblaje básica siempre es implícitamente volátil según la documentación. –

+0

Su ejemplo x86 contra x86-64 es un poco tonto. 'y eax, 255' hace lo que quiere en ambos x86-32 y x86-64, y es un byte más corto que' y rax, 255'. Además, 'movzx eax, al' es ligeramente mejor en ambos, porque es más corto. ¿Estás seguro de que esto incluso se reúne? La directiva de gases es '.globl' o' .global', no 'global'. El uso de 'gcc -masm = intel' todavía usa directivas GAS, no NASM. (Compilará bien, pero no se ensamblará. En godbolt, use el modo "binario" para asegurarse de que se ensambla y compila). Además, ARM no tiene 'eax', tiene' r0'. –

+0

Por supuesto, asm para 'rand_byte' es tonto de todos modos. Mucho mejor hacer '' uint8_t rand_byte' un alias débil para 'rand' cuando la convención de llamadas permite que los valores de retorno estrechos tengan una gran cantidad de basura (como en x86 pero no en ARM) y que la persona que llama inline la extensión cero si lo necesita . Es casi imposible encontrar ejemplos para el asm en línea donde no sería mejor simplemente https://gcc.gnu.org/wiki/DontUseInlineAsm, pero es posible que desee mencionarlo al menos. (Excepto las instrucciones privilegiadas que no tienen envoltorios integrados/intrínsecos). –