Can someone explain what the following assembly code does?
int 0x80
Can someone explain what the following assembly code does?
int 0x80
It passes control to interrupt vector 0x80
See http://en.wikipedia.org/wiki/Interrupt_vector
On Linux, have a look at this: it was used to handle system_call
. Of course on another OS this could mean something totally different.
int 0x80
as a special kind of call
to a function in the kernel (selected by eax
). –
Cecil int
means interrupt, and the number 0x80
is the interrupt number.
An interrupt transfers the program flow to whomever is handling that interrupt, which is interrupt 0x80
in this case.
In Linux, 0x80
interrupt handler is the kernel, and is used to make system calls to the kernel by other programs.
The kernel is notified about which system call the program wants to make, by examining the value in the register %eax
(AT&T syntax, and EAX in Intel syntax). Each system call have different requirements about the use of the other registers. For example, a value of 1
in %eax
means a system call of exit()
, and the value in %ebx
holds the value of the status code for exit()
.
It passes control to interrupt vector 0x80
See http://en.wikipedia.org/wiki/Interrupt_vector
On Linux, have a look at this: it was used to handle system_call
. Of course on another OS this could mean something totally different.
int 0x80
as a special kind of call
to a function in the kernel (selected by eax
). –
Cecil Keep in mind that 0x80
= 80h
= 128
You can see here that INT
is just one of the many instructions (actually the Assembly Language representation (or should I say 'mnemonic') of it) that exists in the x86 instruction set. You can also find more information about this instruction in Intel's own manual found here (page 626).
To summarize from the PDF:
INT n/INTO/INT 3—Call to Interrupt Procedure
The INT n instruction generates a call to the interrupt or exception handler specified with the destination operand. The destination operand specifies a vector from 0 to 255, encoded as an 8-bit unsigned intermediate value. The INT n instruction is the general mnemonic for executing a software-generated call to an interrupt handler.
As you can see 0x80 is the destination operand in your question. At this point the CPU knows that it should execute some code that resides in the Kernel, but what code? That is determined by the Interrupt Vector in Linux.
One of the most useful DOS software interrupts was interrupt 0x21. By calling it with different parameters in the registers (mostly ah and al) you could access various IO operations, string output and more.
Most Unix systems and derivatives do not use software interrupts, with the exception of interrupt 0x80, used to make system calls. This is accomplished by entering a 32-bit value corresponding to a kernel function into the EAX register of the processor and then executing INT 0x80.
Take a look at this please where other available values in the interrupt handler tables are shown:
As you can see the table points the CPU to execute a system call. You can find the Linux System Call table here.
So by moving the value 0x1 to EAX register and calling the INT 0x80 in your program, you can make the process go execute the code in Kernel which will stop (exit) the current running process (on Linux, x86 Intel CPU).
A hardware interrupt must not be confused with a software interrupt. Here is a very good answer on this regard.
This also is good source.
int 0x80
i386 Linux system call ABI is extremely similar to the DOS int 0x21
ABI. Put a call number in a register (AH for DOS, EAX for Linux), and other args in other registers, then run a software-interrupt instruction. The main difference is in what the system calls let you do (access hardware directly in DOS but not Linux), not in how you invoke them. –
Cecil /usr/include/x86_64-linux-gnu/asm/unistd_64.h
–
Conduction Minimal runnable Linux system call example
Linux sets up the interrupt handler for 0x80
such that it implements system calls, a way for userland programs to communicate with the 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
Compile and run with:
as -o main.o main.S
ld -o main.out main.o
./main.out
Outcome: the program prints to stdout:
hello world
and exits cleanly.
You cannot set your own interrupt handlers directly from userland because you only have ring 3 and Linux prevents you from doing so.
GitHub upstream. Tested on Ubuntu 16.04.
Better alternatives
int 0x80
has been superseded by better alternatives for making system calls: first sysenter
, then VDSO.
x86_64 has a new syscall
instruction.
See also: What is better "int 0x80" or "syscall"?
Minimal 16-bit example
First learn how to create a minimal bootloader OS and run it on QEMU and real hardware as I've explained here: https://mcmap.net/q/15004/-how-to-run-a-program-without-an-operating-system
Now you can run in 16-bit real mode:
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
This would do in order:
Do 0.
Do 1.
hlt
: stop executingNote how the processor looks for the first handler at address 0
, and the second one at 4
: that is a table of handlers called the IVT, and each entry has 4 bytes.
Minimal example that does some IO to make handlers visible.
Minimal protected mode example
Modern operating systems run in the so called protected mode.
The handling has more options in this mode, so it is more complex, but the spirit is the same.
The key step is using the LGDT and LIDT instructions, which point the address of an in-memory data structure (the Interrupt Descriptor Table) that describes the handlers.
int 0x80 is the assembly language instruction that is used to invoke system calls in Linux on x86 (i.e., Intel-compatible) processors.
The "int" instruction causes an interrupt.
Simple Answer: An interrupt, put simply, is an event that interrupts the CPU, and tells it to run a specific task.
Detailed Answer:
The CPU has a table of Interrupt Service Routines (or ISRs) stored in memory. In Real (16-bit) Mode, this is stored as the IVT, or Interrupt Vector Table. The IVT is typically located at 0x0000:0x0000
(physical address 0x00000
), and it is a series of segment-offset addresses that point to the ISRs. The OS may replace the pre-existing IVT entries with its own ISRs.
(Note: The IVT's size is fixed at 1024 (0x400) bytes.)
In Protected (32-bit) Mode, the CPU uses an IDT. The IDT is a variable-length structure that consists of descriptors (otherwise known as gates), which tell the CPU about the interrupt handlers. The structure of these descriptors is much more complex than the IVT's simple segment-offset entries; here it is:
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
*The IDT may be of variable size, but it must be sequential, i.e. if you declare your IDT to be from 0x00 to 0x50, you must have every interrupt from 0x00 to 0x50. The OS does not necessarily use all of them, so the Present bit allows the CPU to properly handle interrupts the OS does not intend to handle.
When an interrupt occurs (either by an external trigger (e.g. a hardware device) in an IRQ, or by the int
instruction from a program), the CPU pushes EFLAGS, then CS, and then EIP. (These are automatically restored by iret
, the interrupt return instruction.) The OS usually stores more information about the state of the machine, handles the interrupt, restores the machine state, and continues on.
In many *NIX OSes (including Linux), system calls are interrupt based. The program puts the arguments to the system call in the registers (EAX, EBX, ECX, EDX, etc..), and calls interrupt 0x80. The kernel has already set the IDT to contain an interrupt handler on 0x80, which is called when it receives interrupt 0x80. The kernel then reads the arguments and invokes a kernel function accordingly. It may store a return in EAX/EBX. System calls have largely been replaced by the sysenter
and sysexit
(or syscall
and sysret
on AMD) instructions, which allow for faster entry into ring 0.
This interrupt could have a different meaning in a different OS. Be sure to check its documentation.
eax
is used for the syscall number. asm.sourceforge.net/intro/hello.html –
Cecil It tells the cpu to activate interrupt vector 0x80, which on Linux OSes is the system-call interrupt, used to invoke system functions like open()
for files, et cetera.
As mentioned, it causes control to jump to interrupt vector 0x80. In practice what this means (at least under Linux) is that a system call is invoked; the exact system call and arguments are defined by the contents of the registers. For example, exit() can be invoked by setting %eax to 1 followed by 'int 0x80'.
int is nothing but an interruption i.e the processor will put its current execution to hold.
0x80 is nothing but a system call or the kernel call. i.e the system function will be executed.
To be specific 0x80 represents rt_sigtimedwait/init_module/restart_sys it varies from architecture to architecture.
For more details refer https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md
© 2022 - 2024 — McMap. All rights reserved.