Mucho ha cambiado entre i386 y x86_64, incluidas las instrucciones que se usan para ingresar al kernel y los registros que se usan para llevar los argumentos de las llamadas al sistema. Aquí hay un código equivalente al tuyo:
.section .data
.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rdi
syscall
Citando de esta respuesta a una pregunta relacionada:
Los números de llamada al sistema están en el código fuente de Linux en arch/x86/include/asm/unistd_64.h. El número de llamada al sistema se pasa en el registro rax. Los parámetros están en rdi, rsi, rdx, r10, r8, r9. La llamada se invoca con la instrucción "syscall". La llamada al sistema sobrescribe el registro rcx. La devolución es en rax.
Se encuentra con una sorprendente diferencia entre i386 y x86_64:no utilizan el mismo mecanismo de llamada al sistema. El código correcto es:
movq $60, %rax
movq $2, %rdi ; not %rbx!
syscall
Interrumpir 0x80
siempre invoca llamadas al sistema de 32 bits. Se utiliza para permitir que las aplicaciones de 32 bits se ejecuten en sistemas de 64 bits.
Con el fin de aprender, probablemente debería intentar seguir el tutorial exactamente, en lugar de traducir sobre la marcha a 64 bits; hay algunas otras diferencias de comportamiento significativas con las que es probable que se encuentre. Una vez que esté familiarizado con i386, luego puedes recoger x86_64 por separado.
por favor lea esto ¿Cuáles son las convenciones de llamada para las llamadas del sistema UNIX y Linux en x86-64?
y tenga en cuenta que usar int 0x80
para syscall en sistemas x64 es una capa de compatibilidad antigua. deberías usar syscall
instrucción en sistemas x64.
aún puede usar este método antiguo, pero necesita compilar sus archivos binarios en un modo x86, consulte el manual del compilador/ensamblador para obtener más detalles.
La respuesta de duskwuff señala correctamente que el mecanismo para las llamadas al sistema es diferente para Linux x86 de 64 bits frente a Linux de 32 bits.
Sin embargo, esta respuesta es incompleta y engañosa por un par de razones:
- En realidad, el cambio se introdujo antes de que los sistemas de 64 bits se hicieran populares , motivado por la observación de que
int 0x80
era muy lento en Pentium 4. Linus Torvalds codificó una solución usando elSYSENTER
/SYSEXIT
instrucciones (que habían sido introducidas por Intel alrededor de la era Pentium Pro, pero que tenían errores y no brindaban ningún beneficio práctico). Así que los sistemas Linux modernos de 32 bits en realidad usanSYSENTER
, noint 0x80
. - Los núcleos de Linux x86 de 64 bits en realidad no usan
SYSENTER
ySYSEXIT
. De hecho, usan elSYSCALL
muy similar /SYSRET
instrucciones.
Como se señaló en los comentarios, SYSENTER
en realidad no funciona en muchos sistemas Linux de 64 bits —es decir, AMD de 64 bits sistemas.
Es una situación ciertamente confusa. Los detalles sangrientos están aquí, pero todo se reduce a esto:
Para un kernel de 32 bits, SYSENTER/SYSEXIT son el único par compatible [entre CPU AMD e Intel]
Solo para un kernel de 64 bits en modo largo... SYSCALL/SYSRET son el único par compatible [entre CPU AMD e Intel]
Parece que en un Intel CPU en modo de 64 bits, puede salirse con la suya usando SYSENTER
porque hace lo mismo que SYSCALL
, sin embargo, este no es el caso de los sistemas AMD.
En pocas palabras:siempre use SYSCALL
en Linux en sistemas x86 de 64 bits . Es lo que realmente especifica la ABI x86-64. (Consulte esta excelente respuesta wiki para obtener aún más detalles).