2011-12-14 66 views
14

Actualmente estoy aprendiendo el lenguaje ensamblador en Linux. He estado usando el libro 'Programming From the Ground Up' y todos los ejemplos son de 32 bits. Mi sistema operativo es de 64 bits y he estado tratando de hacer todos los ejemplos en 64 bits. Sin embargo, tengo problemas:x86_64 Ensamblaje Sistema Linux Confusión de llamadas

.section .data 

.section .text 
.global _start 
_start: 
movq $60, %rax 
movq $2, %rbx 
int $0x80 

Esto simplemente llama a la llamada al Sistema de salida de Linux o debería. En su lugar, causa un SEG FALLO y cuando en su lugar hago esto

.section .data 

.section .text 
.global _start 
_start: 
movq $1, %rax 
movq $2, %rbx 
int $0x80 

funciona. Claramente, el problema es el valor que muevo a% rax. El valor de $ 1 que uso en el segundo ejemplo es lo que se dice 'Programación desde cero', pero varias fuentes en Internet han dicho que el número de llamada del sistema de 64 bits es de $ 60. Reference ¿Qué estoy haciendo mal? Además, ¿qué otros problemas debo tener en cuenta y qué debo usar para una referencia? En caso de que necesite saber, estoy en el Capítulo 5 en Programación desde la base.

Respuesta

15

Te encuentras con una sorprendente diferencia entre i386 y x86_64: no usan el mismo mecanismo de llamada al sistema. El código correcto es:

movq $60, %rax 
movq $2, %rdi ; not %rbx! 
syscall 

interrupción 0x80 siempre invoca llamadas de sistema de 32 bits. Se usa para permitir que las aplicaciones de 32 bits se ejecuten en sistemas de 64 bits.

Para fines de aprendizaje, probablemente deberías tratar de seguir el tutorial exactamente, en lugar de traducir sobre la marcha a 64 bits; hay otras pocas diferencias de comportamiento significativas con las que probablemente te encuentres. Una vez que esté familiarizado con i386, y luego, puede recoger x86_64 por separado.

+0

Probablemente voy a terminar haciendo eso. Gracias por su respuesta. –

+0

Uno debe usar '% rdi' para el primer argumento de llamada del sistema, no'% rbx'. –

+0

Gracias por atrapar eso - arreglado. – duskwuff

10

lea este What are the calling conventions for UNIX & Linux system calls on x86-64

y tenga en cuenta que el uso de int 0x80 para la llamada al sistema en sistemas x64 es una capa de compatibilidad de edad. debe usar la instrucción syscall en los sistemas x64.

puede seguir utilizando este método anterior, pero debe compilar sus binarios en modo x86; consulte el manual del compilador/ensamblador para obtener más información.

+0

Me alegro de ver a alguien señalando que x86_64 Linux realmente usa 'syscall' not' sysenter'! Escribí una [respuesta más larga para explicar la confusión entre los dos] (// stackoverflow.com/a/31510342/20789). –

4

Muchos han cambiado entre i386 y x86_64, incluidas las instrucciones utilizadas para acceder al kernel y los registros utilizados para transportar argumentos de llamadas al sistema. Aquí es código equivalente a la suya:

.section .data 

.section .text 
.global _start 
_start: 
movq $60, %rax 
movq $2, %rdi 
syscall 

Citando this answer a una pregunta relacionada:

La llamada al sistema números están en el código fuente de Linux bajo arco/86/include/asm/unistd_64.h. El número de syscall se pasa en el registro de rax. Los parámetros están en rdi, rsi, rdx, r10, r8, r9. La llamada se invoca con la instrucción "syscall". El syscall sobrescribe el registro de rcx. El regreso está en rax.

+0

También estoy haciendo las cosas en el manual del shellcoder 2. Sin embargo, mi sistema operativo también es de 64 bits. Me reúno con nasm-option -f elf32 y me enlace también con -melf_i386.Yo uso gdb y también evans depurador (sin embargo, está mal compilado para 64 y aparece después de cargar un archivo binario, pero dentro de edb, dentro de gdb y en ejecución, el error es el mismo): segfault después de una instrucción DESPUÉS int 0x80.Y Solo quiero SALIR. He visto que el registro (r) ax debe contener 0x3c en lugar de 0x01 (porque se usa el archivo syscall para 64 bits), pero no me puedo imaginar por qué se llama a la instrucción int 0x80. Cualquiera que conozca , por qué ? – icbytes

2

Si marca /usr/include/asm/unistd_32.h salida corresponde a 1 pero en /usr/include/asm/unistd_64.h salida corresponde a 60.

4

duskwuffanswer señala correctamente que el mecanismo para las llamadas al sistema es diferente para Linux de 64 bits frente a Linux de 32 bits.

Sin embargo, esta respuesta es incompleta y engañosa por un par de razones:

Como pointed out in the comments, SYSENTERno funciona realmente en muchos sistemas Linux de 64 bits -a saber de 64 bits de AMD sistemas.

Es una situación ciertamente confusa. El gory details are here, pero lo que se reduce a lo siguiente:

para un kernel de 32 bits, SYSENTER/SYSEXIT son la única pareja compatible [entre AMD e Intel CPUs]

Para un núcleo de 64 bits de largo modo sólo ... SYSCALL/SYSRET son la única pareja compatible [entre AMD e Intel CPUs]

parece ser que en una Intel CPU en modo de 64 bits, puede obtener awa y con el uso de SYSENTER porque hace lo mismo que SYSCALL, sin embargo, este no es el caso para los sistemas AMD.

En pocas palabras: utilice siempre SYSCALL en Linux en sistemas x86 de 64 bits. Es lo que el x86-64 ABI realmente especifica. (Vea este gran wiki answer para aún más detalles.)

Cuestiones relacionadas