Linux中异常初始化和门设置函数的实现
异常初始化trap_init
void __init trap_init(void)
{
#ifdef CONFIG_EISAif (isa_readl(0x0FFFD9) == 'E'+('I'<<8)+('S'<<16)+('A'<<24)) {EISA_bus = 1;}
#endif#ifdef CONFIG_X86_LOCAL_APICinit_apic_mappings();
#endifset_trap_gate(0,÷_error);set_intr_gate(1,&debug);set_intr_gate(2,&nmi);set_system_intr_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_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);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_intr_gate(14,&page_fault);set_trap_gate(15,&spurious_interrupt_bug);set_trap_gate(16,&coprocessor_error);set_trap_gate(17,&alignment_check);
#ifdef CONFIG_X86_MCEset_trap_gate(18,&machine_check);
#endifset_trap_gate(19,&simd_coprocessor_error);set_system_gate(SYSCALL_VECTOR,&system_call);/** Should be a barrier for any external CPU state.*/cpu_init();trap_init_hook();
}
1. 函数功能
初始化x86架构的异常处理机制,设置中断描述符表(IDT)中的各种异常和陷阱门,为CPU异常处理建立基础框架
2. 代码详细解释
2.1. EISA总线检测
#ifdef CONFIG_EISAif (isa_readl(0x0FFFD9) == 'E'+('I'<<8)+('S'<<16)+('A'<<24)) {EISA_bus = 1;}
#endif
#ifdef CONFIG_EISA
:只有在配置了EISA总线支持时才编译isa_readl(0x0FFFD9)
:从I/O地址0xFFFD9读取32位值'E'+('I'<<8)+('S'<<16)+('A'<<24)
:计算EISA签名"EISA"的数值- ‘E’ = 0x45, ‘I’<<8 = 0x4900, ‘S’<<16 = 0x530000, ‘A’<<24 = 0x41000000
- 总和 = 0x41534945 (小端序为"EISA")
- 如果匹配,设置
EISA_bus = 1
标记系统有EISA总线
2.2. APIC初始化
#ifdef CONFIG_X86_LOCAL_APICinit_apic_mappings();
#endif
#ifdef CONFIG_X86_LOCAL_APIC
:只有在配置了本地APIC支持时才编译init_apic_mappings()
:初始化APIC(高级可编程中断控制器)的内存映射- APIC用于多处理器系统中的中断分发
2.3. 异常0-7设置
set_trap_gate(0,÷_error);set_intr_gate(1,&debug);set_intr_gate(2,&nmi);set_system_intr_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);
异常0:除零错误
set_trap_gate(0,÷_error)
:设置陷阱门,处理除零异常- 当DIV或IDIV指令除数为0时触发
异常1:调试异常
set_intr_gate(1,&debug)
:设置中断门,处理调试异常- 用于硬件调试、单步执行等
异常2:NMI(不可屏蔽中断)
set_intr_gate(2,&nmi)
:设置中断门,处理NMI- 用于硬件严重错误,无法被屏蔽
异常3:断点指令
set_system_intr_gate(3, &int3)
:设置系统中断门- int3-5可以从所有特权级调用
- INT3指令用于软件断点
异常4:溢出异常
set_system_gate(4,&overflow)
:设置系统门- INTO指令在溢出标志设置时触发
异常5:边界检查异常
set_system_gate(5,&bounds)
:设置系统门- BOUND指令在数组越界时触发
异常6:无效操作码
set_trap_gate(6,&invalid_op)
:设置陷阱门- 执行未定义或无效的指令时触发
异常7:设备不可用
set_trap_gate(7,&device_not_available)
:设置陷阱门
2.4. 异常8-13设置
set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);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);
异常8:双重故障
set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS)
:设置任务门- 特殊处理:使用任务门切换到专门的TSS(任务状态段)
- 当处理一个异常时又发生另一个异常时触发
异常9:协处理器段越界
set_trap_gate(9,&coprocessor_segment_overrun)
:设置陷阱门- 协处理器操作超出段界限时触发
异常10:无效TSS
set_trap_gate(10,&invalid_TSS)
:设置陷阱门- 任务状态段无效时触发
异常11:段不存在
set_trap_gate(11,&segment_not_present)
:设置陷阱门- 访问不存在的内存段时触发
异常12:堆栈段异常
set_trap_gate(12,&stack_segment)
:设置陷阱门- 堆栈操作违反段规则时触发
异常13:通用保护异常
set_trap_gate(13,&general_protection)
:设置陷阱门- 各种保护违规时触发,最常见的异常之一
2.5. 异常14-19设置
set_intr_gate(14,&page_fault);set_trap_gate(15,&spurious_interrupt_bug);set_trap_gate(16,&coprocessor_error);set_trap_gate(17,&alignment_check);
#ifdef CONFIG_X86_MCEset_trap_gate(18,&machine_check);
#endifset_trap_gate(19,&simd_coprocessor_error);
异常14:页面故障
set_intr_gate(14,&page_fault)
:设置中断门- 重要:处理缺页异常,虚拟内存管理的核心
异常15:伪中断bug
set_trap_gate(15,&spurious_interrupt_bug)
:设置陷阱门- 处理某些硬件产生的不必要中断
异常16:协处理器错误
set_trap_gate(16,&coprocessor_error)
:设置陷阱门- 数学协处理器错误时触发
异常17:对齐检查
set_trap_gate(17,&alignment_check)
:设置陷阱门- 未对齐的内存访问时触发(如果启用对齐检查)
异常18:机器检查
#ifdef CONFIG_X86_MCE
:机器检查异常支持set_trap_gate(18,&machine_check)
:设置陷阱门- 硬件检测到CPU或总线错误时触发
异常19:SIMD协处理器错误
set_trap_gate(19,&simd_coprocessor_error)
:设置陷阱门- SSE/SSE2指令执行错误时触发
2.6. 系统调用门设置
set_system_gate(SYSCALL_VECTOR,&system_call);
set_system_gate(SYSCALL_VECTOR,&system_call)
:设置系统调用门SYSCALL_VECTOR
:系统调用中断向量号(通常是0x80)&system_call
:系统调用处理函数的地址- 允许用户空间程序通过中断进入内核空间
2.7. CPU初始化和钩子函数
/** Should be a barrier for any external CPU state.*/cpu_init();trap_init_hook();
cpu_init()
:初始化当前CPU的特定状态- 设置任务状态段(TSS)
- 设置本地描述符表(LDT)
- 初始化其他CPU特定资源
trap_init_hook()
:架构特定的陷阱初始化钩子函数- 允许不同x86变体(如x86_64)提供特定实现
3. 门类型区别
门类型 | 特权级检查 | 用途 |
---|---|---|
set_trap_gate | 严格 | 大多数异常,允许调试 |
set_intr_gate | 严格 | 重要异常,禁用中断 |
set_system_gate | 宽松 | 用户空间可调用的异常 |
set_system_intr_gate | 宽松 | 用户空间可调用的中断 |
set_task_gate | 严格 | 任务切换,用于双重故障 |
4. 异常处理的重要性
这个函数建立的异常处理框架是操作系统稳定性的基石:
- 硬件异常到软件处理的桥梁
- 内存管理的基础(页面故障)
- 调试支持(断点、单步执行)
- 系统调用入口
- 错误恢复机制
设置陷阱门set_trap_gate
static void __init set_trap_gate(unsigned int n, void *addr)
{_set_gate(idt_table+n,15,0,addr,__KERNEL_CS);
}
1. 函数功能
在中断描述符表(IDT)中设置一个陷阱门,用于处理CPU异常
2. 代码详细解释
2.1. 函数定义
static void __init set_trap_gate(unsigned int n, void *addr)
static
:函数只在当前文件内可见void __init
:返回值为void,__init
表示该函数只在系统初始化阶段使用,初始化完成后内存可以被释放unsigned int n
:中断向量号(0-255)void *addr
:中断/异常处理函数的地址
2.2. 函数实现
_set_gate(idt_table+n,15,0,addr,__KERNEL_CS);
参数1:idt_table+n
idt_table
:中断描述符表的基地址指针+n
:计算第n个中断描述符的地址
参数2:15
- 这是门描述符的类型和属性字段
- 二进制:
00001111
(15 = 0x0F) - 分解:
00
:DPL (Descriptor Privilege Level) - 特权级0,只有内核可以调用1
:S (System) 位 - 1表示系统段/门描述符111
:Type字段 - 111表示32位陷阱门(Trap Gate)
参数3:0
- 设为0表示不使用IST,使用当前堆栈
参数4:addr
- 中断/异常处理函数的地址
- 这个地址会被拆分成低16位和高16位填入门描述符
参数5:__KERNEL_CS
- 代码段选择子
- 值为
0x60
(在x86架构中) - 表示使用内核代码段
3. 陷阱门的特点
3.1. 执行时行为
// 当通过陷阱门调用时:
1. 保存当前EFLAGS、CS、EIP到堆栈
2. 清除某些EFLAGS位(如NT、TF)
3. 但IF(中断使能)标志保持不变 ← 与中断门的关键区别
4. 切换到目标代码段和偏移地址
3.2. 与中断门的区别
// 中断门 (type=14):
- 进入处理程序时自动禁用中断 (IF=0)
- 用于需要原子性处理的紧急中断// 陷阱门 (type=15):
- 进入处理程序时保持中断状态不变
- 用于异常处理,允许在异常处理期间响应中断
4. 特权级保护机制
由于DPL=0(最高特权级):
- 只能从内核模式(Ring 0)调用
- 如果用户模式(Ring 3)程序尝试通过INT指令触发这些异常,会产生通用保护异常
- 这保护了关键异常处理程序不被用户程序滥用
设置系统门set_system_gate
static void __init set_system_gate(unsigned int n, void *addr)
{_set_gate(idt_table+n,15,3,addr,__KERNEL_CS);
}
1. 函数功能
在中断描述符表(IDT)中设置一个系统门,允许用户空间程序通过中断调用内核功能
2. 代码详细解释
2.1. 函数定义
static void __init set_system_gate(unsigned int n, void *addr)
static
:函数只在当前文件内可见void __init
:返回值为void,__init
表示该函数只在系统初始化阶段使用set_system_gate
:函数名,设置系统门unsigned int n
:中断向量号void *addr
:处理函数的地址
2.2. 函数实现
_set_gate(idt_table+n,15,3,addr,__KERNEL_CS);
参数1:idt_table+n
idt_table
:中断描述符表的基地址+n
:计算第n个中断描述符的位置- 指向要设置的门在IDT中的位置
参数2:15
- 门描述符的类型字段
- 二进制:
00001111
(15 = 0x0F) - 分解:
00
:DPL字段的一部分1
:S (System) 位 - 1表示系统段/门描述符111
:Type字段 - 111表示32位陷阱门
参数3:3
- 关键区别:这是DPL (Descriptor Privilege Level) 字段
- 二进制:
11
= 特权级3 - 表示用户模式(Ring 3)程序也可以调用这个门
参数4:addr
- 处理函数的入口地址
- 当通过这个门调用时,CPU会跳转到这个地址执行
参数5:__KERNEL_CS
- 代码段选择子,值为
0x60
- 表示处理函数在内核代码段中执行
3. 与set_trap_gate的关键区别
对比分析:
// set_trap_gate - 只能内核调用
_set_gate(idt_table+n, 15, 0, addr, __KERNEL_CS);
// ↑ DPL=0 (仅内核)// set_system_gate - 用户也可调用
_set_gate(idt_table+n, 15, 3, addr, __KERNEL_CS);
// ↑ DPL=3 (用户和内核)
4. 实际使用场景
在trap_init()
中的具体使用:
set_system_gate(4,&overflow); // 溢出异常
set_system_gate(5,&bounds); // 边界检查异常
set_system_gate(SYSCALL_VECTOR,&system_call); // 系统调用
5. 安全机制
虽然用户程序可以调用系统门,但有重要限制:
代码段切换
// 即使从用户模式调用:
当前CS = 用户代码段 (特权级3)
目标CS = __KERNEL_CS (特权级0)// 实际执行在内核模式下进行
堆栈切换
// 自动切换到内核堆栈
用户堆栈SS:ESP → 保存到内核堆栈
使用内核堆栈SS0:ESP0执行处理函数
系统调用的特殊角色
SYSCALL_VECTOR
(通常是0x80)是最重要的系统门:
// 传统的Linux系统调用机制
set_system_gate(SYSCALL_VECTOR, &system_call);// 用户程序使用:
mov eax, 1 // 系统调用号
mov ebx, 0 // 参数
int 0x80 // 触发系统调用
设置任务门set_task_gate
static void __init set_task_gate(unsigned int n, unsigned int gdt_entry)
{_set_gate(idt_table+n,5,0,0,(gdt_entry<<3));
}
1. 函数功能
在中断描述符表(IDT)中设置一个任务门,用于在异常处理时自动进行任务切换
2. 代码详细解释
2.1. 函数定义
static void __init set_task_gate(unsigned int n, unsigned int gdt_entry)
static
:函数只在当前文件内可见void __init
:返回值为void,__init
表示该函数只在系统初始化阶段使用set_task_gate
:函数名,设置任务门unsigned int n
:中断向量号unsigned int gdt_entry
:GDT(全局描述符表)中的TSS(任务状态段)条目索引
2.2. 函数实现
_set_gate(idt_table+n,5,0,0,(gdt_entry<<3));
参数1:idt_table+n
idt_table
:中断描述符表的基地址+n
:计算第n个中断描述符的位置- 指向要设置的任务门在IDT中的位置
参数2:5
- 门描述符的类型字段
- 二进制:
00000101
(5 = 0x05) - 分解:
00
:DPL (Descriptor Privilege Level) - 特权级00
:S (System) 位 - 0表示这是系统描述符0101
:Type字段 - 0101表示任务门(Task Gate)
参数3:0
- DPL (Descriptor Privilege Level) 字段
参数4:0
- 偏移量字段,对于任务门必须为0
- 因为任务门不直接指定代码地址,而是通过TSS间接指定
参数5:(gdt_entry<<3)
- 关键参数:TSS段选择子
gdt_entry
:GDT中的TSS描述符索引<<3
:左移3位,将索引转换为段选择子
3. 实际使用场景
在trap_init()
中的具体使用:
set_task_gate(8, GDT_ENTRY_DOUBLEFAULT_TSS);
双重故障异常(8)的特殊处理:
- 当CPU在处理一个异常时又发生另一个异常
- 例如:页面故障处理程序中又发生除零错误
- 使用任务门而不是普通的陷阱门的原因:
- 需要干净的上下文来处理严重错误
- 避免在已损坏的堆栈上继续执行
- 确保系统能够恢复或安全崩溃
4. 任务门与其他门的关键区别
对比分析:
// 陷阱门 - 直接跳转到处理函数
_set_gate(..., 15, 0, addr, __KERNEL_CS);
// 执行流程: 保存上下文 → 跳转到addr// 任务门 - 进行完整的任务切换
_set_gate(..., 5, 0, 0, (gdt_entry<<3));
// 执行流程: 保存当前TSS → 加载新TSS → 全新开始
任务切换的详细过程:
1. 保存当前任务状态到当前TSS- 所有寄存器值- 段选择子- EFLAGS- EIP2. 加载新任务的TSS- 从GDT中指定的TSS描述符加载- 恢复所有寄存器状态- 切换到新任务的地址空间3. 开始在新任务上下文中执行- 使用全新的堆栈- 独立的地址空间- 隔离的执行环境
5. 双重故障处理的重要性
为什么需要特殊处理?
// 普通异常处理可能失败的场景:
1. 页面故障处理时发生堆栈错误
2. 除零异常处理时发生段错误
3. 任何异常处理程序中发生严重硬件错误// 后果:
- 当前堆栈可能已损坏
- 关键数据结构可能不一致
- 继续执行可能导致系统完全崩溃
任务门的优势:
// 通过任务门处理双重故障:
1. 完全隔离:在新的TSS中执行,与损坏环境隔离
2. 干净状态:全新的寄存器组和堆栈
3. 可靠恢复:有机会保存调试信息并优雅关闭
4. 系统保护:防止错误传播导致整个系统崩溃
设置系统中断门set_system_intr_gate
static inline void set_system_intr_gate(unsigned int n, void *addr)
{_set_gate(idt_table+n, 14, 3, addr, __KERNEL_CS);
}
1. 函数功能
在中断描述符表(IDT)中设置一个系统中断门,允许用户空间程序通过中断调用内核的中断处理程序
2. 代码详细解释
2.1. 函数定义
static inline void set_system_intr_gate(unsigned int n, void *addr)
static
:函数只在当前文件内可见inline
:建议编译器进行内联展开,避免函数调用开销void
:函数没有返回值set_system_intr_gate
:函数名,设置系统中断门unsigned int n
:中断向量号void *addr
:中断处理函数的地址
2.2. 函数实现
_set_gate(idt_table+n, 14, 3, addr, __KERNEL_CS);
参数1:idt_table+n
idt_table
:中断描述符表的基地址指针+n
:计算第n个中断描述符的位置- 在x86中,每个中断描述符占8字节,所以这是第n个门的位置
参数2:14
- 门描述符的类型和属性字段
- 二进制:
00001110
(14 = 0x0E) - 分解:
0
:P (Present) 位 - 应该为1表示存在00
:DPL字段的一部分1
:S (System) 位 - 1表示系统段/门描述符1110
:Type字段 - 1110表示32位中断门(Interrupt Gate)
参数3:3
- 关键特性:DPL (Descriptor Privilege Level) 字段
- 二进制:
11
= 特权级3 - 表示用户模式(Ring 3)程序也可以调用这个中断门
参数4:addr
- 中断处理函数的入口地址
- 当通过这个门调用时,CPU会跳转到这个地址执行中断处理
参数5:__KERNEL_CS
- 代码段选择子,值为
0x60
- 表示中断处理函数在内核代码段中执行
3. 与其他门类型的对比
四种门类型的比较:
// 陷阱门 - 保持中断使能
set_trap_gate: _set_gate(..., 15, 0, addr, __KERNEL_CS)// 中断门 - 禁用中断
set_intr_gate: _set_gate(..., 14, 0, addr, __KERNEL_CS)// 系统门 - 陷阱门 + 用户可访问
set_system_gate: _set_gate(..., 15, 3, addr, __KERNEL_CS)// 系统中断门 - 中断门 + 用户可访问
set_system_intr_gate: _set_gate(..., 14, 3, addr, __KERNEL_CS)
4. 安全保护机制
可控的用户访问:
// 虽然用户可调用,但是受控的:
1. 只能调用特定的向量(如INT3)
2. 处理函数在内核模式执行
3. 有完整的上下文保存和恢复
4. 内核可以验证和处理请求的合法性
与普通系统调用的区别:
// 系统调用门 (向量0x80):
- 用于正常的系统服务调用
- 保持中断使能(使用陷阱门)
- 有严格的参数验证机制// 系统中断门 (向量3):
- 用于特定的调试异常
- 禁用中断确保原子性(使用中断门)
- 针对调试场景的特殊处理
5. 调试器集成
GDB等调试器的工作原理:
// 调试器设置断点的过程:
1. 保存目标指令字节
2. 写入INT3指令 (0xCC)
3. 程序执行到断点时触发异常3
4. 通过系统中断门进入内核调试处理
5. 内核通知调试器,调试器接管控制// 系统中断门确保:
- 断点触发时立即响应,不被其他中断打断
- 调试状态的一致性
- 可靠的单步执行和断点管理
设置中断门set_intr_gate
void set_intr_gate(unsigned int n, void *addr)
{_set_gate(idt_table+n,14,0,addr,__KERNEL_CS);
}
1. 函数功能
在中断描述符表(IDT)中设置一个中断门,用于处理硬件中断和关键异常,确保处理过程的原子性
2. 代码详细解释
2.1. 函数定义
void set_intr_gate(unsigned int n, void *addr)
void
:函数没有返回值set_intr_gate
:函数名,设置中断门unsigned int n
:中断向量号void *addr
:中断处理函数的地址- 注意:这个函数没有
static
和__init
,意味着它可以在运行时被调用
2.2. 函数实现
_set_gate(idt_table+n,14,0,addr,__KERNEL_CS);
参数1:idt_table+n
idt_table
:中断描述符表的基地址指针+n
:计算第n个中断描述符的位置- 在x86中,每个中断描述符占8字节,所以这是第n个门的位置
参数2:14
- 门描述符的类型和属性字段
- 二进制:
00001110
(14 = 0x0E) - 分解:
00
:DPL (Descriptor Privilege Level) - 特权级01
:S (System) 位 - 1表示系统段/门描述符1110
:Type字段 - 1110表示32位中断门(Interrupt Gate)
参数3:0
- 关键特性:DPL (Descriptor Privilege Level) 字段
- 二进制:
00
= 特权级0 - 表示只有内核模式(Ring 0)可以调用这个中断门
参数4:addr
- 中断处理函数的入口地址
- 当通过这个门调用时,CPU会跳转到这个地址执行中断处理
参数5:__KERNEL_CS
- 代码段选择子,值为
0x60
- 表示中断处理函数在内核代码段中执行
3. 中断门的关键特性
自动禁用中断
// 当中断门被调用时:
EFLAGS.IF = 0 // 自动清除中断使能标志// 这意味着:
// - 在中断处理期间不会响应其他中断
// - 确保中断处理的原子性
// - 防止中断嵌套导致堆栈溢出或死锁
严格的特权级保护
// 由于DPL=0:
// - 只有内核代码可以触发这些中断
// - 用户程序尝试访问会产生通用保护异常
// - 保护关键中断处理程序不被滥用
4. 与陷阱门的区别
关键行为差异:
// 中断门 (type=14):
- 进入时自动禁用中断 (IF=0)
- 用于需要原子性处理的紧急情况
- 处理程序必须显式启用中断// 陷阱门 (type=15):
- 进入时保持中断状态不变
- 用于普通的异常处理
- 可以在处理期间响应中断
设置门描述符的内联汇编宏_set_gate
#define _set_gate(gate_addr,type,dpl,addr,seg) \
do { \int __d0, __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" ((seg) << 16)); \
} while (0)
1. 宏功能
在中断描述符表(IDT)或全局描述符表(GDT)中设置一个门描述符,用于定义中断、陷阱或调用门的属性
2. 代码详细解释
2.1. 宏定义开始
#define _set_gate(gate_addr,type,dpl,addr,seg) \
- 定义宏
_set_gate
,接受5个参数:gate_addr
:门描述符的内存地址type
:门类型(陷阱门、中断门等)dpl
:描述符特权级addr
:处理函数地址seg
:代码段选择子
2.2. do-while循环结构
do { \
- 使用
do { ... } while (0)
是宏定义的常见技巧 - 确保宏在使用时像单个语句一样工作
- 允许在宏中使用局部变量而不会与外部代码冲突
2.3. 局部变量声明
int __d0, __d1; \
- 声明两个临时整型变量
__d0
和__d1
- 双下划线前缀避免与用户变量名冲突
- 这些变量将在内联汇编中使用
2.4. 内联汇编开始
__asm__ __volatile__ ( \
__asm__
:GCC内联汇编关键字__volatile__
:告诉编译器不要优化这段汇编代码- 确保汇编指令按原样执行
2.5. 汇编指令部分
"movw %%dx,%%ax\n\t" \"movw %4,%%dx\n\t" \"movl %%eax,%0\n\t" \"movl %%edx,%1" \
指令1:"movw %%dx,%%ax\n\t"
- 将DX寄存器的低16位移动到AX寄存器,DX寄存器保存着处理函数地址
- 准备构建描述符的低32位
指令2:"movw %4,%%dx\n\t"
- 将输入操作数%4(属性字)移动到DX寄存器
- %4对应后面的
"i" ((short) (0x8000+(dpl<<13)+(type<<8)))
指令3:"movl %%eax,%0\n\t"
- 将EAX寄存器的值存储到输出操作数%0,EAX寄存器为
段地址 << 16 | addr & 0x0000FFFF
- %0对应门描述符的低32位内存位置
指令4:"movl %%edx,%1"
- 将EDX寄存器的值存储到输出操作数%1,EDX寄存器为
属性字 | addr & 0xFFFF0000
- %1对应门描述符的高32位内存位置
2.6. 输出操作数
:"=m" (*((long *) (gate_addr))), \"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
输出操作数0:"=m" (*((long *) (gate_addr)))
=m
:内存操作数,只写*((long *) (gate_addr))
:将gate_addr
转换为long指针并解引用- 对应门描述符的低32位
输出操作数1:"=m" (*(1+(long *) (gate_addr)))
- 同样内存操作数,只写
*(1+(long *) (gate_addr))
:gate_addr
后4字节的位置- 对应门描述符的高32位
输出操作数2:"=&a" (__d0)
=&a
:早期破坏的EAX寄存器,绑定到__d0
&
表示在输入操作数使用前就修改
输出操作数3:"=&d" (__d1)
=&d
:早期破坏的EDX寄存器,绑定到__d1
2.7. 输入操作数
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \"3" ((char *) (addr)),"2" ((seg) << 16)); \
输入操作数4:"i" ((short) (0x8000+(dpl<<13)+(type<<8)))
"i"
:立即数- 计算属性字:
0x8000
:P位(存在位)= 1dpl<<13
:DPL字段移到正确位置(位13-14)type<<8
:类型字段移到正确位置(位8-11)
输入操作数:"3" ((char *) (addr))
"3"
:匹配第3个操作数(EDX/__d1)- 将处理函数地址转换为char指针
输入操作数:"2" ((seg) << 16)
"2"
:匹配第2个操作数(EAX/__d0)- 将段选择子左移16位,准备放到描述符的高位