题
有人能解释一下下面的汇编代码的作用吗?
int 0x80
解决方案
它将控制传递给中断向量0x80
请参阅 http://en.wikipedia.org/wiki/Interrupt_vector
在Linux上,看一下这个:它用于处理 system_call 代码>。当然,在另一个操作系统上,这可能意味着完全不同的东西。
其他提示
int
表示中断,编号 0x80
是中断号。
中断将程序流传输给处理该中断的任何人,在这种情况下是中断 0x80
。
在Linux中, 0x80
中断处理程序是内核,用于通过其他程序对内核进行系统调用。
通过检查寄存器%eax
(气体语法和Intel语法中的EAX)中的值,通知内核程序想要进行哪个系统调用。每个系统调用对其他寄存器的使用有不同的要求。例如,%eax
中的 1
值表示 exit()
的系统调用,%ebx <中的值/ code>保存
exit()
的状态代码值。
请记住,0x80 = 80h = 128
您可以在此处看到 INT 是只是x86指令集中存在的许多指令之一(实际上是汇编语言表示(或者我应该说'助记符'))。您还可以在英特尔自己的手册中找到有关此说明的更多信息这里。
总结PDF:
INT n / INTO / INT 3&#8212;调用中断程序
INT n指令生成对中断或异常的调用 使用目标操作数指定的处理程序。目的地 操作数指定0到255之间的向量,编码为8位无符号 中间价值。 INT n指令是for的通用助记符 执行软件生成的调用中断处理程序。
您可以在问题中看到 0x80 是目标操作数。此时CPU知道它应该执行一些驻留在内核中的代码,但是代码是什么?这是由Linux中的中断向量决定的。
最有用的DOS软件中断之一是中断0x21。通过在寄存器中调用不同的参数(主要是ah和al),您可以访问各种IO操作,字符串输出等。
大多数Unix系统和衍生产品不使用软件中断,但中断0x80除外,用于进行系统调用。这是通过在处理器的EAX寄存器中输入对应于内核函数的 32位值然后执行INT 0x80来实现的。
请看看中断处理程序表中其他可用值的显示位置:
如您所见,该表指向CPU执行系统调用。您可以在此处找到Linux系统调用表。
因此,通过将值0x1移动到EAX寄存器并在程序中调用INT 0x80,可以使进程执行内核中的代码,该代码将停止(退出)当前正在运行的进程(在Linux,x86 Intel CPU上)
硬件中断不得与软件中断混淆。 这里非常这方面的答案很好。
这个也是很好的来源。
你可以在行动中看到 int 80h 此处。
int 0x80是汇编语言 用于调用的指令 在x86上的Linux系统调用(即, 与英特尔兼容的处理器。
最小可运行 Linux 系统调用示例
Linux 设置中断处理程序 0x80
这样它就实现了系统调用,这是用户态程序与内核通信的一种方式。
.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
编译并运行:
as -o main.o main.S
ld -o main.out main.o
./main.out
结果:程序打印到标准输出:
hello world
并干净地退出。
您不能直接从用户态设置自己的中断处理程序,因为您只有 Ring 3 和 Linux 阻止你这样做.
GitHub 上游. 。在 Ubuntu 16.04 上测试。
更好的选择
int 0x80
已被更好的系统调用替代方案所取代:第一的 sysenter
, ,然后是VDSO。
x86_64 有 一个新的 syscall
操作说明.
也可以看看: “int 0x80”或“syscall”哪个更好?
最小 16 位示例
首先学习如何创建最小的引导加载程序操作系统并在 QEMU 和真实硬件上运行它,正如我在这里解释的: https://stackoverflow.com/a/32483545/895245
现在您可以在 16 位实模式下运行:
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
这将按顺序进行:
Do 0.
Do 1.
hlt
:停止执行
注意处理器如何在地址处查找第一个处理程序 0
, ,第二个在 4
:这是一个处理程序表,称为 IVT, ,每个条目有 4 个字节。
执行一些 IO 的最小示例 使处理程序可见。
最小保护模式示例
现代操作系统在所谓的保护模式下运行。
该模式下的操控有更多的选择,因此也更复杂,但精神是一样的。
关键步骤是使用 LGDT 和 LIDT 指令,它们指向描述处理程序的内存数据结构(中断描述符表)的地址。
“int”指令导致中断。
什么是中断?
简单答案:简单地说,中断是一个中断CPU并告诉它运行特定任务的事件。
详细解答:
CPU有一个存储在内存中的中断服务程序(或ISR)表。在Real(16位)模式下,它存储为 IVT ,或 I nterrupt V ector T 能够。 IVT通常位于 0x0000:0x0000
(物理地址 0x00000
),它是一系列指向ISR的段偏移地址。操作系统可以用自己的ISR替换已存在的IVT条目。
(注意:IVT的大小固定为1024(0x400)字节。)
在受保护(32位)模式下,CPU使用IDT。 IDT是一个可变长度的结构,由描述符(也称为门)组成,它告诉CPU有关中断处理程序的信息。这些描述符的结构比IVT的简单段偏移条目复杂得多;这是:
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
* IDT可能是可变大小的,但它必须是顺序的,即如果你声明你的IDT是从0x00到0x50,你必须有从0x00到0x50的每个中断。操作系统不一定全部使用,因此当前位允许CPU正确处理操作系统无意处理的中断。
当发生中断时(通过IRQ中的外部触发器(例如硬件设备)或程序中的 int
指令),CPU按下EFLAGS,然后按CS,然后按EIP。 (这些由 iret
,中断返回指令自动恢复。)OS通常存储有关机器状态的更多信息,处理中断,恢复机器状态,并继续。
在许多* NIX操作系统(包括Linux)中,系统调用都是基于中断的。程序将参数放入寄存器(EAX,EBX,ECX,EDX等)中的系统调用,并调用中断0x80。内核已经将IDT设置为在0x80上包含一个中断处理程序,当它接收到中断0x80时会被调用。然后内核读取参数并相应地调用内核函数。它可以在EAX / EBX中存储一个回报。系统调用已基本上被 sysenter
和 sysexit
(或AMD上的 syscall
和 sysret
)指令所取代,允许更快地进入环0。
此中断在不同的操作系统中可能有不同的含义。请务必查看其文档。
如上所述,它会导致控制跳转到中断向量0x80。在实践中,这意味着(至少在Linux下)是调用系统调用;确切的系统调用和参数由寄存器的内容定义。例如,可以通过将%eax设置为1,然后设置为“int 0x80”来调用exit()。
它告诉cpu激活中断向量0x80,它在Linux操作系统上是系统调用中断,用于调用系统函数,如 open()
用于文件等。