Título: Duda ensamblador, conversion de x86 a x64 (push y pop)
Publicado por: Peaverin en 7 Febrero 2017, 03:34 am
Hola. Primero de todo decir que soy nuevo en este foro y tengo muchísimas cosas por leer y aprender, así que agradeceré a todos los que tengáis la paciencia necesaria para ayudarme. Estoy empezando a aprender ensamblador, leyendo un libro que me recomendaron. El libro se titulo Programming From the Ground Up, es un libro muy conocido. Primero que todo decir que soy absolutamente nuevo en linux también, y practicamente en programación, así como totalmente nuevo en ensamblador. El problema que tengo es que el libro lo explica todo para sistemas x86, cuyos registros tienen 32 bits de memoria, y mi ordenador tiene el procesador x64 de 64bits, así como la gran mayoria de nuevos ordenadores, por lo que hay incompatibilidad entre los códigos que explican y los que yo puedo utilizar. He estado leyendo por internet, y según dice, puedo utilizar normalmente las partes de 32 bits de los registros, ignorando los 32 restantes, escribiendo, por ejemplo, "%eax" en vez de "%rax". Hasta aquí todo bien, no he tenido ningún problema. Pero el problema viene a la hora de guardar variables en el stack. Para ello no sirve utilizar, por ejemplo, "pushl %eax", sino que hay que utilizar "pushq %rax". Dicho esto, necesito algo de ayuda. En el libro, se crea la función power (para elevar un numero a un exponente), y se utiliza esta. El ejemplo dado es para 32bits, por lo que se tendrían que hacer algunos cambios. Lo he intentado, pero algo hago mal, puesto que el programa se linkea correctamente, pero cuando lo ejecuto, me dice "violación del segmento". Aquí pongo el codigo original del libro y mi intento de "convertir" a 64 bits. Código original 32 bits: #PURPOSE: Program to illustrate how functions work # This program will compute the value of # 2^3 + 5^2 # #Everything in the main program is stored in registers, #so the data section doesn’t have anything. .section .data .section .text .globl _start _start: pushl $3 #push second argument pushl $2 #push first argument call power #call the function addl $8, %esp #move the stack pointer back pushl %eax #save the first answer before #calling the next function pushl $2 #push second argument pushl $5 #push first argument call power #call the function addl $8, %esp #move the stack pointer back popl %ebx #The second answer is already #in %eax. We saved the #first answer onto the stack, #so now we can just pop it #out into %ebx addl %eax, %ebx #add them together #the result is in %ebx movl $1, %eax #exit (%ebx is returned) int $0x80 #PURPOSE: This function is used to compute # the value of a number raised to # a power. # #INPUT: First argument - the base number # Second argument - the power to # raise it to # #OUTPUT: Will give the result as a return value # #NOTES: The power must be 1 or greater # #VARIABLES: # %ebx - holds the base number # %ecx - holds the power # # -4(%ebp) - holds the current result # # %eax is used for temporary storage # .type power, @function power: pushl %ebp #save old base pointer movl %esp, %ebp #make stack pointer the base pointer subl $4, %esp #get room for our local storage movl 8(%ebp), %ebx #put first argument in %eax movl 12(%ebp), %ecx #put second argument in %ecx movl %ebx, -4(%ebp) #store current result power_loop_start: cmpl $1, %ecx #if the power is 1, we are done je end_power movl -4(%ebp), %eax #move the current result into %eax imull %ebx, %eax #multiply the current result by #the base number movl %eax, -4(%ebp) #store the current result decl %ecx #decrease the power jmp power_loop_start #run for the next power end_power: movl -4(%ebp), %eax #return value goes in %eax movl %ebp, %esp #restore the stack pointer popl %ebp #restore the base pointer ret Mi intento en 64bits , que da el error "violación de segmento" (imagino que es porque intenta acceder a algún lugar de la memoria no reservado para el prgroama, desconozco el motivo) cuando, después de linquear, escribo "./power" en la consola de linux (hay algunos comentarios que he puesto tan solo para aclararme yo): .section .data
.section .text
.globl _start _start: #ponemos en el stack al numero 3(será el segundo argumento) y al 2(primer #argumento) pushq $3 #push second argument pushq $2 #push first argument call power #call the function ##Devolvemos el stack pointer a donde estaba antes (antes de los argumentos) addl $8, %esp #move the stack pointer back pushq %rax #save the first answer before #calling the next function pushq $2 #push second argument pushq $5 #push first argument call power #call the function addl $8, %esp #move the stack pointer back popq %rbx #The second answer is already #in %eax. We saved the #first answer onto the stack, #so now we can just pop it #out into %ebx addl %eax, %ebx #add them together #the result is in %ebx movl $1, %eax #exit (%ebx is returned) int $0x80
#PURPOSE: This function is used to compute the value of a number raised to a power.
#INPUT #First argument : the base number #Second argument : the power to raise it to
#OUTPUT # Will give the result as a return value
#THE POWER MUST BE 1 OR GREATER
# %ebx: base number # %ecx: power
#Local variable: -4(%epb) current result #%eax: temporary storage
.type power, @function power:
##Pone el base pointer actual en el stack (el sack pointer %esp se cambia solo) push %rbp ##Pone el valor del stack pointer en el base pointer movl %esp, %ebp ##Le quita un word al stack pointer (para guardar una local variable) subl $4, %esp
#8 es el first argument (porque es el segundo que metemos), 12 es el second (es el primero que metemos en el stack) movl 8(%ebp), %ebx movl 12(%ebp), %ecx #Guardamos la base en la local storage movl %ebx, -4(%ebp)
##Loop de hacer potencia:
potencia_loop: ##Si el exponente es 1, ya hemos acabado cmpl $1, %ecx je exit_loop
##Movemos el resultado actual a %eax movl -4(%ebp), %eax ##Multiplicamos el resultado actual por la base 1 vez (se guarda en la derecha): imull %ebx, %eax
##Guardamos el resultado actual en la local storage: movl %eax, -4(%ebp)
##restamos 1 al exponente: decl %ecx jmp potencia_loop
exit_loop: ##mueve la local variable a %eax, que es el parametro de retorno movl -4(%ebp), %eax ##restablece el stack pointer movl %ebp, %esp ##pop quita el último valor del stack y lo pone en el registro indicado, es decir el antiguo %epb, y lo ponemos en %ebp (recuperamos el antiguo %ebp) pop %rbp ##Retornamos a donde nos encontramos antes (adress guardada en el stack), y suma 4 al stack (se deshace de la return adress) ret
Basicamente mi intento se basa en sustiuir todos los "pushl %exx" por "pushq rxx" y lo mismo con los pop... Obviamente no ha funcionado. Agradecería que alguien que sepa del tema me pudiera ayudar y resolver esta duda, puesto que hasta que no consiga resolverlo no me quedo tranquilo y no puedo continuar con el libro. Y cualquier recomendación de cualquier libro/recurso que me pueda ayudar a iniciarme en esto, también será muy agradecido. Gracias!
Título: Re: Duda ensamblador, conversion de x86 a x64 (push y pop)
Publicado por: xv0 en 7 Febrero 2017, 22:04 pm
A la hora de trabajar con ebp, esp lo haces en 32 bits, si tu sistema esta corriendo en 64 bits, recuerda que las direcciones seran un quad, tienes que usar los de 64 bit, rsp, rbp.
Si estas en linux puedes hacer un strace, para saber donde esta la violacion, pero vamos que es por lo que te dije.
Un saludo.
Título: Re: Duda ensamblador, conversion de x86 a x64 (push y pop)
Publicado por: Peaverin en 8 Febrero 2017, 02:35 am
Gracias por la respuesta, pero eso mismo es lo que yo ya sabía y explico en el mensaje inicial, no obstante y aún haciendo los cambios de esp, ebp por rsp, rbp, me da el error de violación de segmento. Aquí pongo un codigo de lo que he intentado: .section .data
.section .text
.globl _start _start: #ponemos en el stack al numero 3(será el segundo argumento) y al 2(primer #argumento) pushq $3 #push second argument pushq $2 #push first argument call power #call the function ##Devolvemos el stack pointer a donde estaba antes (antes de los argumentos) addl $8, %esp #move the stack pointer back pushq %rax #save the first answer before #calling the next function pushq $2 #push second argument pushq $5 #push first argument call power #call the function addq $8, %rsp #move the stack pointer back popq %rbx #The second answer is already #in %eax. We saved the #first answer onto the stack, #so now we can just pop it #out into %ebx addl %eax, %ebx #add them together #the result is in %ebx movl $1, %eax #exit (%ebx is returned) int $0x80
#PURPOSE: This function is used to compute the value of a number raised to a power.
#INPUT #First argument : the base number #Second argument : the power to raise it to
#OUTPUT # Will give the result as a return value
#THE POWER MUST BE 1 OR GREATER
# %ebx: base number # %ecx: power
#Local variable: -4(%epb) current result #%eax: temporary storage
.type power, @function power:
##Pone el base pointer actual en el stack (el sack pointer %esp se cambia solo) push %rbp ##Pone el valor del stack pointer en el base pointer movq %rsp, %rbp ##Le quita un word al stack pointer (para guardar una local variable) subq $4, %rsp
#8 es el first argument (porque es el segundo que metemos), 12 es el second (es el primero que metemos en el stack) movl 8(%rbp), %ebx movl 12(%rbp), %ecx #Guardamos la base en la local storage movl %ebx, -4(%rbp)
##Loop de hacer potencia:
potencia_loop: ##Si el exponente es 1, ya hemos acabado cmpl $1, %ecx je exit_loop
##Movemos el resultado actual a %eax movl -4(%rbp), %eax ##Multiplicamos el resultado actual por la base 1 vez (se guarda en la derecha): imull %ebx, %eax
##Guardamos el resultado actual en la local storage: movl %eax, -4(%rbp)
##restamos 1 al exponente: decl %ecx jmp potencia_loop
exit_loop: ##mueve la local variable a %eax, que es el parametro de retorno movl -4(%rbp), %eax ##restablece el stack pointer movq %rbp, %rsp ##pop quita el último valor del stack y lo pone en el registro indicado, es decir el antiguo %epb, y lo ponemos en %ebp (recuperamos el antiguo %ebp) pop %rbp ##Retornamos a donde nos encontramos antes (adress guardada en el stack), y suma 4 al stack (se deshace de la return adress) ret
Título: Re: Duda ensamblador, conversion de x86 a x64 (push y pop)
Publicado por: xv0 en 8 Febrero 2017, 21:41 pm
Hay errores, estas sumando 8 a esp en la cuarta instruccion, ademas de otras sumas, si estas en 64 bits, tienes que usar syscall no int 0x80, se me olvido decirtelo. Los parametros no se suben al stack, si no se pasan a los registros.
http://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-x86-64 (http://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-x86-64)
Mirate eso.
Un saludo.
Título: Re: Duda ensamblador, conversion de x86 a x64 (push y pop)
Publicado por: Peaverin en 9 Febrero 2017, 01:53 am
Hola, ya he conseguido compilar y ejecutar el programa sin ningún error.
Gracias por la ayuda.
Resuelto.
|