¿Alguien puede explicar qué hace el siguiente código ensamblador?
int 0x80
¿Alguien puede explicar qué hace el siguiente código ensamblador?
int 0x80
Respuestas:
Pasa el control para interrumpir el vector 0x80
Ver http://en.wikipedia.org/wiki/Interrupt_vector
En Linux, eche un vistazo a esto : se usó para manejar system_call
. Por supuesto, en otro sistema operativo esto podría significar algo totalmente diferente.
int 0x80
como un tipo especial de call
función en el kernel (seleccionada por eax
).
int
significa interrumpir, y el número 0x80
es el número de interrupción. Una interrupción transfiere el flujo del programa a quien esté manejando esa interrupción, que es una interrupción 0x80
en este caso. En Linux, el 0x80
controlador de interrupciones es el kernel y otros programas lo utilizan para realizar llamadas del sistema al kernel.
Se notifica al kernel sobre qué llamada al sistema desea realizar el programa, examinando el valor en el registro %eax
(sintaxis AT&T y EAX en sintaxis Intel). Cada llamada al sistema tiene diferentes requisitos sobre el uso de los otros registros. Por ejemplo, un valor de 1
in %eax
significa una llamada al sistema de exit()
, y el valor de in %ebx
contiene el valor del código de estado para exit()
.
Tenga en cuenta que 0x80
= 80h
=128
Aquí puede ver que INT
es solo una de las muchas instrucciones (en realidad, la representación en lenguaje ensamblador (o debería decir 'mnemónico')) que existe en el conjunto de instrucciones x86. También puede encontrar más información sobre estas instrucciones en el propio manual de Intel que se encuentra aquí .
Para resumir del PDF:
INT n / INTO / INT 3: procedimiento de llamada para interrumpir
La instrucción INT n genera una llamada al manejador de interrupciones o excepciones especificado con el operando de destino. El operando de destino especifica un vector de 0 a 255, codificado como un valor intermedio sin signo de 8 bits. La instrucción INT n es el mnemónico general para ejecutar una llamada generada por software a un controlador de interrupciones.
Como puede ver, 0x80 es el operando de destino en su pregunta. En este punto, la CPU sabe que debe ejecutar algún código que reside en el Kernel, pero ¿qué código? Eso está determinado por el vector de interrupción en Linux.
Una de las interrupciones de software de DOS más útiles fue la interrupción 0x21. Al llamarlo con diferentes parámetros en los registros (principalmente ah y al), puede acceder a varias operaciones de E / S, salida de cadena y más.
La mayoría de los sistemas Unix y sus derivados no utilizan interrupciones de software, con la excepción de la interrupción 0x80, que se utiliza para realizar llamadas al sistema. Esto se logra ingresando un valor de 32 bits correspondiente a una función del kernel en el registro EAX del procesador y luego ejecutando INT 0x80.
Eche un vistazo a esto, donde se muestran otros valores disponibles en las tablas del manejador de interrupciones:
Como puede ver, la tabla indica que la CPU debe ejecutar una llamada al sistema. Puede encontrar la tabla de llamadas al sistema Linux aquí .
Entonces, al mover el valor 0x1 al registro EAX y llamar al INT 0x80 en su programa, puede hacer que el proceso ejecute el código en el Kernel que detendrá (saldrá) el proceso en ejecución actual (en Linux, CPU Intel x86).
Una interrupción de proceso no debe confundirse con una interrupción de software. Aquí hay una muy buena respuesta a este respecto.
Esta también es una buena fuente.
int 0x80
llamada ABI del sistema i386 Linux es extremadamente similar a la int 0x21
ABI de DOS . Ponga un número de llamada en un registro (AH para DOS, EAX para Linux) y otros argumentos en otros registros, luego ejecute una instrucción de interrupción de software. La principal diferencia está en lo que le permiten hacer las llamadas al sistema (acceder al hardware directamente en DOS pero no en Linux), no en cómo las invoca.
/usr/include/x86_64-linux-gnu/asm/unistd_64.h
Ejemplo de llamada al sistema Linux mínimamente ejecutable
Linux configura el manejador de interrupciones para 0x80
que implemente llamadas al sistema, una forma para que los programas de la zona de usuario se comuniquen con el kernel.
.data
s:
.ascii "hello world\n"
len = . - s
.text
.global _start
_start:
movl $4, %eax /* write system call number */
movl $1, %ebx /* stdout */
movl $s, %ecx /* the data to print */
movl $len, %edx /* length of the buffer */
int $0x80
movl $1, %eax /* exit system call number */
movl $0, %ebx /* exit status */
int $0x80
Compilar y ejecutar con:
as -o main.o main.S
ld -o main.out main.o
./main.out
Resultado: el programa imprime en stdout:
hello world
y sale limpiamente.
No puede configurar sus propios controladores de interrupciones directamente desde el área de usuario porque solo tiene el anillo 3 y Linux le impide hacerlo .
GitHub aguas arriba . Probado en Ubuntu 16.04.
Mejores alternativas
int 0x80
ha sido reemplazado por mejores alternativas para realizar llamadas al sistema: primero sysenter
, luego VDSO.
x86_64 tiene una nueva syscall
instrucción .
Ver también: ¿Qué es mejor "int 0x80" o "syscall"?
Ejemplo mínimo de 16 bits
Primero, aprenda a crear un sistema operativo de cargador de arranque mínimo y ejecútelo en QEMU y hardware real, como he explicado aquí: https://stackoverflow.com/a/32483545/895245
Ahora puede ejecutar en modo real de 16 bits:
movw $handler0, 0x00
mov %cs, 0x02
movw $handler1, 0x04
mov %cs, 0x06
int $0
int $1
hlt
handler0:
/* Do 0. */
iret
handler1:
/* Do 1. */
iret
Esto haría en orden:
Do 0.
Do 1.
hlt
: dejar de ejecutarObserve cómo el procesador busca el primer controlador en la dirección 0
y el segundo en 4
: que es una tabla de controladores llamada IVT , y cada entrada tiene 4 bytes.
Ejemplo mínimo que hace algo de IO para hacer visibles a los controladores.
Ejemplo de modo protegido mínimo
Los sistemas operativos modernos se ejecutan en el llamado modo protegido.
El manejo tiene más opciones en este modo, por lo que es más complejo, pero el espíritu es el mismo.
El paso clave es usar las instrucciones LGDT y LIDT, que señalan la dirección de una estructura de datos en memoria (la tabla de descriptores de interrupciones) que describe los controladores.
int 0x80 es la instrucción en lenguaje ensamblador que se usa para invocar llamadas al sistema en Linux en procesadores x86 (es decir, compatibles con Intel).
La instrucción "int" provoca una interrupción.
Respuesta simple: una interrupción, en pocas palabras, es un evento que interrumpe la CPU y le dice que ejecute una tarea específica.
Respuesta detallada :
La CPU tiene una tabla de Rutinas de servicio de interrupción (o ISR) almacenadas en la memoria. En Real (16-bit) Mode, este se almacena como la IVT , o I nterrupt V ector T capaz. El IVT normalmente se encuentra en 0x0000:0x0000
(dirección física 0x00000
) y es una serie de direcciones de desplazamiento de segmento que apuntan a los ISR. El sistema operativo puede reemplazar las entradas IVT preexistentes con sus propios ISR.
(Nota: el tamaño del IVT se fija en 1024 (0x400) bytes).
En el modo protegido (32 bits), la CPU utiliza un IDT. El IDT es una estructura de longitud variable que consta de descriptores (también conocidos como puertas), que informan a la CPU sobre los manejadores de interrupciones. La estructura de estos descriptores es mucho más compleja que las simples entradas de desplazamiento de segmento del IVT; aquí está:
bytes 0, 1: Lower 16 bits of the ISR's address.
bytes 2, 3: A code segment selector (in the GDT/LDT)
byte 4: Zero.
byte 5: A type field consisting of several bitfields.
bit 0: P (Present): 0 for unused interrupts, 1 for used interrupts.*
bits 1, 2: DPL (Descriptor Privilege Level): The privilege level the descriptor (bytes 2, 3) must have.
bit 3: S (Storage Segment): Is 0 for interrupt and trap gates. Otherwise, is one.
bits 4, 5, 6, 7: GateType:
0101: 32 bit task gate
0110: 16-bit interrupt gate
0111: 16-bit trap gate
1110: 32-bit interrupt gate
1111: 32-bit trap gate
* El IDT puede ser de tamaño variable, pero debe ser secuencial, es decir, si declara que su IDT es de 0x00 a 0x50, debe tener todas las interrupciones de 0x00 a 0x50. El sistema operativo no necesariamente los usa todos, por lo que el bit Presente permite que la CPU maneje adecuadamente las interrupciones que el sistema operativo no pretende manejar.
Cuando ocurre una interrupción (ya sea por un disparador externo (por ejemplo, un dispositivo de hardware) en un IRQ, o por la int
instrucción de un programa), la CPU presiona EFLAGS, luego CS y luego EIP. (Estos son restaurados automáticamente por iret
la instrucción de retorno de interrupción). El sistema operativo generalmente almacena más información sobre el estado de la máquina, maneja la interrupción, restaura el estado de la máquina y continúa.
En muchos sistemas operativos * NIX (incluido Linux), las llamadas al sistema se basan en interrupciones. El programa coloca los argumentos de la llamada al sistema en los registros (EAX, EBX, ECX, EDX, etc.), y llama a la interrupción 0x80. El kernel ya ha configurado el IDT para que contenga un controlador de interrupciones en 0x80, que se llama cuando recibe la interrupción 0x80. El núcleo luego lee los argumentos e invoca una función del núcleo en consecuencia. Puede almacenar una devolución en EAX / EBX. Las llamadas al sistema han sido reemplazadas en gran medida por las instrucciones sysenter
y sysexit
(o syscall
y sysret
en AMD), que permiten una entrada más rápida en el anillo 0.
Esta interrupción podría tener un significado diferente en un sistema operativo diferente. Asegúrese de consultar su documentación.
eax
se utiliza para el número de llamada al sistema. asm.sourceforge.net/intro/hello.html
Como se mencionó, hace que el control salte para interrumpir el vector 0x80. En la práctica, lo que esto significa (al menos en Linux) es que se invoca una llamada al sistema; la llamada y los argumentos exactos del sistema están definidos por el contenido de los registros. Por ejemplo, exit () se puede invocar estableciendo% eax en 1 seguido de 'int 0x80'.
Le dice a la CPU que active el vector de interrupción 0x80, que en los sistemas operativos Linux es la interrupción de la llamada al sistema, que se utiliza para invocar funciones del sistema como open()
archivos, etc.
int no es más que una interrupción, es decir, el procesador pondrá en espera su ejecución actual.
0x80 no es más que una llamada al sistema o al kernel. es decir, se ejecutará la función del sistema.
Para ser específico, 0x80 representa rt_sigtimedwait / init_module / restart_sys y varía de una arquitectura a otra.
Para obtener más detalles, consulte https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md