tutorial español assembler assembly x86 nasm

assembly - español - nasm tutorial



Asamblea x86, obteniendo falla de segmentación (2)

La subrutina print_string está modificando el puntero de pila sin restaurarlo. La subrutina main tiene el siguiente diseño:

push ebp ;save the stack frame of the caller mov ebp, esp ;save the stack pointer <code for subroutine> mov esp, ebp ;restore the stack pointer pop ebp ;restore the caller''s stack frame ret ;return to address pushed onto the stack by call

De forma similar, la subrutina print_string debe tener el mismo diseño, guardando el puntero de pila y luego restaurándolo antes de ret . Cualquier subrutina que use la pila debe guardar y restaurar el puntero de la pila.

push ebp mov ebp, esp pusha push msg call printf ;should print "Hello" add esp, 4 popa mov esp, ebp pop ebp ret

Sin guardar el puntero de pila y restaurarlo, la subrutina llamada modifica el stackpointer, donde la instrucción de call guardó la dirección de retorno. En consecuencia, cuando se encuentra ret , la ejecución salta a la dirección de devolución incorrecta, por lo tanto segfaulting. Obtenga más información sobre las convenciones y funciones de llamada en ensamblaje.

section .data msg: db "hello!", 10, 0 ;my message section .text extern printf ;C printf function global main main: push ebp mov ebp, esp call print_string mov esp, ebp pop ebp ret ;end of program print_string: pusha push msg call printf ;should print "Hello" popa ret ;return back to main

Cuando ejecuto este código obtengo:
¡Hola!
Fallo de segmentación (núcleo volcado)

¿Qué está mal con el código?


printf espera un puntero empujado en la pila para un argumento, pero bajo la convención de llamadas C es su tarea eliminar este argumento de la pila.

Usted omitió esto y entonces la instrucción popa pone los valores incorrectos en todos los GPRS y la instrucción ret utiliza el valor original de EAX como una dirección de destino desencadenando así un error de segmentación.

Solución 1 limpiando manualmente

print_string: pusha push msg call printf ;should print "Hello" add esp, 4 ; <-- Clean-up popa ret ;return back to main

Solución 2 usando prolog / epilog en print_string

print_string: push ebp mov ebp, esp push msg call printf ;should print "Hello" mov esp, ebp ; <-- Clean-up pop ebp ret ;return back to main

La solución 2 solo es posible porque printf es una función que se comporta bien y que preserva el registro EBP. Al mover EBP a ESP, cada elemento extra que se empujó entre el prólogo y el epílogo desaparece . La solución 2 puede salvarlo de muchos de los que add esp, 4 instrucciones add esp, 4 (cuando las rutinas se vuelven más largas).