Lectura adicional para cualquiera de los temas aquí: La guía definitiva para las llamadas al sistema Linux
Verifiqué esto usando GNU Assembler (gas) en Linux.
Interfaz de kernel
x86-32 también conocido como i386 Linux System Call Convention:
En x86-32, los parámetros para la llamada al sistema Linux se pasan mediante registros. %eax
para syscall_number. % ebx,% ecx,% edx,% esi,% edi,% ebp se utilizan para pasar 6 parámetros a las llamadas del sistema.
El valor de retorno está en %eax
. Todos los demás registros (incluidos los EFLAGS) se conservan en todo el int $0x80
.
Tomé el siguiente fragmento del Tutorial de ensamblaje de Linux, pero tengo dudas al respecto. Si alguien puede mostrar un ejemplo, sería genial.
Si hay más de seis argumentos,
%ebx
debe contener la ubicación de la memoria donde se almacena la lista de argumentos, pero no se preocupe por esto porque es poco probable que use una llamada al sistema con más de seis argumentos.
Para ver un ejemplo y un poco más de lectura, consulte http://www.int80h.org/bsdasm/#alternate-calling-convention . Otro ejemplo de Hello World para i386 Linux usando int 0x80
: Hola, ¿mundo en lenguaje ensamblador con llamadas al sistema Linux?
Hay una forma más rápida de hacer llamadas al sistema de 32 bits: usando sysenter
. El núcleo mapea una página de memoria en cada proceso (el vDSO), con el lado del espacio de usuario de la sysenter
danza, que tiene que cooperar con el núcleo para que pueda encontrar la dirección de retorno. Arg para registrar la asignación es lo mismo que para int $0x80
. Normalmente debería llamar al vDSO en lugar de usarlo sysenter
directamente. (Consulte la Guía definitiva para las llamadas al sistema Linux para obtener información sobre cómo vincular y llamar al vDSO, y para obtener más información sysenter
y todo lo demás relacionado con las llamadas al sistema).
x86-32 [Gratis | Abrir | Red | DragonFly] Convención del sistema BSD UNIX:
Los parámetros se pasan en la pila. Empuje los parámetros (último parámetro empujado primero) en la pila. Luego inserte una información ficticia adicional de 32 bits (en realidad no son datos ficticios. Consulte el siguiente enlace para obtener más información) y luego brinde una instrucción de llamada al sistemaint $0x80
http://www.int80h.org/bsdasm/#default-calling-convention
x86-64 Convención de llamada al sistema Linux:
x86-64 Mac OS X es similar pero diferente . TODO: comprueba lo que * BSD hace.
Consulte la sección: "A.2 Convenciones del kernel de Linux AMD64 " de la interfaz binaria del sistema V Suplemento del procesador de arquitectura AMD64 . Las últimas versiones de las psABI i386 y x86-64 System V se pueden encontrar enlazadas desde esta página en el repositorio del mantenedor de ABI . (Ver también elx86 etiqueta wiki para enlaces ABI actualizados y muchas otras cosas buenas sobre x86 asm.)
Aquí está el fragmento de esta sección:
- Las aplicaciones de nivel de usuario se utilizan como registros enteros para pasar la secuencia% rdi,% rsi,% rdx,% rcx,% r8 y% r9. La interfaz del kernel utiliza% rdi,% rsi,% rdx,% r10,% r8 y% r9.
- Una llamada al sistema se realiza a través de las
syscall
instrucciones . Este clobbers% rcx y% r11 , así como el valor de retorno% rax, pero se conservan otros registros.
- El número de la llamada al sistema debe pasarse en el registro% rax.
- Las llamadas al sistema están limitadas a seis argumentos, no se pasa ningún argumento directamente en la pila.
- Al regresar de la llamada al sistema, el registro% rax contiene el resultado de la llamada al sistema. Un valor en el rango entre -4095 y -1 indica un error, lo es
-errno
.
- Solo los valores de la clase INTEGER o de la clase MEMORY se pasan al kernel.
Recuerde que esto es del apéndice específico de Linux al ABI, e incluso para Linux es informativo, no normativo. (Pero de hecho es exacto).
Este int $0x80
ABI de 32 bits se puede usar en código de 64 bits (pero no se recomienda). ¿Qué sucede si usa la ABI de Linux int 0x80 de 32 bits en código de 64 bits? Todavía trunca sus entradas a 32 bits, por lo que no es adecuado para punteros, y pone a cero r8-r11.
Interfaz de usuario: función de llamada
Convención de llamada a funciones x86-32:
En x86-32 los parámetros se pasaron en la pila. El último parámetro se introdujo primero en la pila hasta que se hayan completado todos los parámetros y luego call
se haya ejecutado la instrucción. Esto se usa para llamar a las funciones de la biblioteca C (libc) en Linux desde el ensamblado.
Las versiones modernas del i386 System V ABI (utilizado en Linux) requieren una alineación de 16 bytes %esp
antes de a call
, como siempre ha requerido el x86-64 System V ABI. Los Callees pueden asumir eso y usar cargas / almacenes SSE de 16 bytes que fallan sin alinearse. Pero históricamente, Linux solo requería una alineación de pila de 4 bytes, por lo que se requería un trabajo adicional para reservar espacio alineado naturalmente incluso para un byte de 8 bytes double
o algo así.
Algunos otros sistemas modernos de 32 bits aún no requieren una alineación de pila de más de 4 bytes.
x86-64 Convención de llamada a la función de espacio de usuario del sistema V:
x86-64 System V pasa args en registros, que es más eficiente que la convención de args de pila de i386 System V. Evita la latencia y las instrucciones adicionales de almacenar los argumentos en la memoria (caché) y luego volver a cargarlos en la persona que llama. Esto funciona bien porque hay más registros disponibles y es mejor para las CPU modernas de alto rendimiento donde la latencia y la ejecución fuera de orden son importantes. (El i386 ABI es muy antiguo).
En este nuevo mecanismo: Primero, los parámetros se dividen en clases. La clase de cada parámetro determina la manera en que se pasa a la función llamada.
Para obtener información completa, consulte: "3.2 Secuencia de llamada de función" de la interfaz binaria de la aplicación System V AMD64 Architecture Processor Supplement que lee, en parte:
Una vez que se clasifican los argumentos, los registros se asignan (en orden de izquierda a derecha) para pasar de la siguiente manera:
- Si la clase es MEMORY, pase el argumento en la pila.
- Si la clase es INTEGER, se utiliza el siguiente registro disponible de la secuencia% rdi,% rsi,% rdx,% rcx,% r8 y% r9
También lo %rdi, %rsi, %rdx, %rcx, %r8 and %r9
son los registros en orden utilizados para pasar parámetros de entero / puntero (es decir, clase INTEGER) a cualquier función libc desde el ensamblado. % rdi se usa para el primer parámetro INTEGER. % rsi para el segundo,% rdx para el tercero y así sucesivamente. Entonces se call
deben dar instrucciones. La pila ( %rsp
) debe estar alineada con 16B cuando se call
ejecuta.
Si hay más de 6 parámetros INTEGER, el séptimo parámetro INTEGER y más tarde se pasan a la pila. (La persona que llama aparece, igual que x86-32.)
Los primeros 8 argumentos de coma flotante se pasan en% xmm0-7, más tarde en la pila. No hay registros de vector de llamada preservada. (Una función con una combinación de FP y argumentos enteros puede tener más de 8 argumentos de registro total).
Las funciones variables ( comoprintf
) siempre necesitan %al
= el número de argumentos de registro FP.
Hay reglas sobre cuándo empacar estructuras en registros ( rdx:rax
al regreso) vs. en memoria. Consulte la ABI para obtener detalles y verifique la salida del compilador para asegurarse de que su código concuerde con los compiladores sobre cómo se debe pasar / devolver algo.
Tenga en cuenta que la convención de llamada de función x64 de Windows tiene múltiples diferencias significativas con respecto al Sistema x86-64 V, como el espacio de sombra que debe ser reservado por la persona que llama (en lugar de una zona roja) y xmm6-xmm15 con llamada preservada. Y reglas muy diferentes para qué argumento va en qué registro.