#define _set_gate(gate_addr, type, dpl, addr) \
do { \
int __do, __d1; \
__asm__ __volatile__ ("movw %%dx, %%ax\n\t" \
"movw %4, %%dx\n\t" \
"movl %%eax, %0\n\t" \
"movl %%edx, %1" \
: "=m" (*((long *)(gate_addr))), \
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
:"i" ((short)(0x8000+(dpl<<13)+(type<<8))), \
"3" ((char *)(addr)), "2" (__KERNEL_CS <<16); \
} while(0)
对于输出部和输入部编号
编号 | 代码 | 含义 |
---|---|---|
输出 | ||
%0 | "=m" (*((long *)(gate_addr)) | 表示%0要与 gate_addr 结合 |
%1 | "=m" ((1+(long)(gate_addr))) | %1 与(gate_addr + 1)结合 |
%2 | "=&a" (__d0) | %2与局部变量__d0结合,存放在%%eax 中 |
%3 | "=&d" (__d1) | %3与局部变量__d1 结合,存放在%%edx 中 |
输入 | ||
%4 | "i" ((short)(0x8000+(dpl<<13)+(type<<8))) | 表示%4与操作数(0x8000+(dpl<<13+(type<<8)))结合 |
%5 | "3" ((char*)(addr)) | 表示变量与%3相同,即%%edx,将 addr 存入到 %%edx中 |
%6 | "2" (__KERNEL_CS<<16) | 表示与%2相同,即%%eax, 将__KERNEL_CS << 16存入 |
输入代表在汇编开始的时候进行的赋值,或者与汇编中的占位相对应,在本例中,汇编中只用到%4,于是%4 的地方用((short)(0x8000+(dpl<<13)+(type<<8)))代替。于是执行过程
- 将 addr 的内存值存入 %edx 寄存器,将(__KERNEL_CS<<16)值存入%eax 寄存器。初始化完成
- 第一行汇编,将%edx 的低 16 位移入%eax 的低 16 位(即%dx 移入%ax)。
此时 %eax 的高 16 位便是(__KERNEL_CS),低 16 位即为 addr 的低 16 位。 - 将(0x8000+(dpl<<3)+(type<<8))移入%edx 的低 16 位。(此时%edx 的高 16 位保存 addr 的高 16 位,低 16 位为(0x8000+(dpl<<3)+(type<<8)))
- 将%eax 的值保存到 *gate_addr 中。
- 将%edx中的地保存到*(gate_addr+1)中
- 根据输出部赋值。
--
内联汇编可以分为四个部分
指令部 | : | 输出部 | : | 输入部 | : | 损坏部 |
---|
约束条件:
字母 | 含义 |
---|---|
m / v / o | 表示内存单元 |
r | 表示任何寄存器 |
q | 表示寄存器 eax, ebx, ecx, edx 之一 |
i / h | 表示直接操作数 |
E / F | 表示浮点数 |
g | 表示任意 |
a , b , c , d | 分表表示 eax, ebx, ecx, edx |
S, D | 分别表示 esi, edi |
I | 表示常数(0-31) |
辅助的参考视频,最后一个例子,可以直接拉到 5 分 35 秒
为什么要读懂呢,因为 Linux内核很多地方都采用了 GCC 内联汇编。
以中断向量表为例.
void __init trap_init(void){
set_trap_gate(0,÷_error);
set_trap_gate(0,÷_error);
set_trap_gate(1,&debug);
set_intr_gate(2,&nmi);
set_system_gate(3,&int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_trap_gate(8,&double_fault);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_trap_gate(14,&page_fault);
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
set_trap_gate(18,&machine_check);
set_trap_gate(19,&simd_coprocessor_error);
set_system_gate(SYSCALL_VECTOR,&system_call);
/*
* default LDT is a single-entry callgate to lcall7 for iBCS
* and a callgate to lcall27 for Solaris/x86 binaries
*/
set_call_gate(&default_ldt[0],lcall7);
set_call_gate(&default_ldt[4],lcall27);
}
static set_intr_gate(unsigned int n, void* addr){
-set_gate(idt_table+n, 14, 0, addr);
}
static void __init set_trap_gate(unsigned int n, void* addr){
_set_gate(idt_table+n, 15, 0, addr);
}
static void __init set_system_gate(unsigned int n, void* addr){
_set_gate(idt_table+n, 15, 3, addr);
}
static void __init set_call_gate(void* a, void*addr){
_set_gate(a, 12, 3, addr);
}
其中 idt_table 是就是中断向量表
struct desc_struct{
unsigned long a, b;
};
struct desc_struct id_table[256] __attribute__((__section__(".data.idt"))) = {{0,0},}
于是在结合上面的 GCC 内联汇编代码,中断向量表的每一项 desc_struct,其
a赋值为
高 16 位 | 低 16 位 |
---|---|
(__KERNEL_CS) | addr 的低 16 位 |
b 赋值为
高 16 位 | 低 16 位 |
---|---|
addr 的高 16 位 | (0x8000+(dpl<<13)+(type<<8)) |
再结合各参数在 i386 中,(__KERNEL_CS)宏定义为0x10。
类型 | dpl | type | (0x8000+(dpl<<13)+(type<<8)) |
---|---|---|---|
set_intr_gate | 14 | 0 | 0x24000(100100000000000000) |
set_trap_gate | 15 | 0 | 0x26300(100110000000000000) |
set_system_gate | 15 | 3 | 0x26300(100110000000000000) |
set_call_gate | 12 | 3 | 0x20300(100000001100000000) |