0x00:前言
本篇文章接着内核中断1 来进行源码的分析 文章难度较大 需要一定的汇编基础中断分为
两种有错误码中断和无错误码中断 接下来我们来看正文部分。
0x01:源码
- /* linux/kernel/asm.s */
- .globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op
- .globl _double_fault,_coprocessor_segment_overrun
- .globl _invalid_TSS,_segment_not_present,_stack_segment
- .globl _general_protection,_coprocessor_error,_irq13,_reserved
- _divide_error:
- pushl $_do_divide_error
- no_error_code:
- xchgl %eax,(%esp)
- pushl %ebx
- pushl %ecx
- pushl %edx
- pushl %edi
- pushl %esi
- pushl %ebp
- push %ds
- push %es
- push %fs
- pushl $0 # "error code"
- lea 44(%esp),%edx
- pushl %edx
- movl $0x10,%edx
- mov %dx,%ds
- mov %dx,%es
- mov %dx,%fs
- call *%eax
- addl $8,%esp
- pop %fs
- pop %es
- pop %ds
- popl %ebp
- popl %esi
- popl %edi
- popl %edx
- popl %ecx
- popl %ebx
- popl %eax
- iret
- _debug:
- pushl $_do_int3 # _do_debug
- jmp no_error_code
- _nmi:
- pushl $_do_nmi
- jmp no_error_code
- _int3:
- pushl $_do_int3
- jmp no_error_code
- _overflow:
- pushl $_do_overflow
- jmp no_error_code
- _bounds:
- pushl $_do_bounds
- jmp no_error_code
- _invalid_op:
- pushl $_do_invalid_op
- jmp no_error_code
- _coprocessor_segment_overrun:
- pushl $_do_coprocessor_segment_overrun
- jmp no_error_code
- _reserved:
- pushl $_do_reserved
- jmp no_error_code
- _irq13:
- pushl %eax
- xorb %al,%al
- outb %al,$0xF0
- movb $0x20,%al
- outb %al,$0x20
- jmp 1f
- 1: jmp 1f
- 1: outb %al,$0xA0
- popl %eax
- jmp _coprocessor_error
- _double_fault:
- pushl $_do_double_fault
- error_code:
- xchgl %eax,4(%esp) # error code <-> %eax
- xchgl %ebx,(%esp) # &function <-> %ebx
- pushl %ecx
- pushl %edx
- pushl %edi
- pushl %esi
- pushl %ebp
- push %ds
- push %es
- push %fs
- pushl %eax # error code
- lea 44(%esp),%eax # offset
- pushl %eax
- movl $0x10,%eax
- mov %ax,%ds
- mov %ax,%es
- mov %ax,%fs
- call *%ebx
- addl $8,%esp
- pop %fs
- pop %es
- pop %ds
- popl %ebp
- popl %esi
- popl %edi
- popl %edx
- popl %ecx
- popl %ebx
- popl %eax
- iret
- _invalid_TSS:
- pushl $_do_invalid_TSS
- jmp error_code
- _segment_not_present:
- pushl $_do_segment_not_present
- jmp error_code
- _stack_segment:
- pushl $_do_stack_segment
- jmp error_code
- _general_protection:
- pushl $_do_general_protection
- jmp error_code
复制代码
0x02:分析
该文件主要是定义了CPU异常产生的中断函数的调用。
分成2类:
将所有的寄存器值入栈
一个栈也就是一块内存区域 我们必须要有基地址 也就是段地址左移4位和偏移地址。
要在一个栈中寻址的话 也需要段地址和偏移地址。
---------------------------------------------------------
----------------------------------------------------------
CS:IP 指向可执行程序的起始地址
此后CPU从这个起始地址开始读取内存中的指令 并且执行。
CS:IP指向
1.你想让CPU 执行哪行指令 你就让 CS:IP指向保存有指令的那块内存即可。
2.任何时候 CS:IP 指向的地址中的内容都是CPU当前执行的指令。
-------------------------------------------------------------------
------------------------------
前四步:
===================================================
xchgl %eax,(%esp)
将eax的值保存在栈上 将中断处理函数的地址保存在eax寄存器中
xchg 交换eax 和esp的值
ESP 专门用作堆栈指针 被形象地称为栈顶指针
EAX是累加器AX是算术的主要寄存器
asm.s包含着CPU探测到故障异常的底层代码程序 与traps.c关系密切
调用traps.c的程序打印出错信息 并退出。
对于不带出错号的中断过程 堆栈指针位置变化情况请参照图(a)。
在开始执行相应中断服务程序之前 堆栈指针esp指在中断返回地址一栏(图中 esp0处)。
当把将要调用的C函数do_ _divide_ error()或其他C函数地址入栈后 指针位置是esp1处 此时程序使用
交换指令把该函数的地址放入eax寄存器中 而原来eax的值则被保存到堆栈上。
此后程序在把一些寄存器入栈后 堆栈指针位置处于esp2处。
当正式调用do_ divide_ error()之前, 程序会将开始执行中断程序时的原eip 保存位置(即堆栈指针esp0值)压入
堆栈放到esp3位置处 并在中断返回弹出入栈的寄存器之前指针通过加上8又回到esp2处。
对于CPU会产生错误号的中断过程 堆栈指针位置变化情况请参照图(b)。
在刚开始执行中断服务程序之前 堆栈指针指向图中esp0处。
在把将要调用的C函数do_ double_ fault()或其 他C函数地址入栈后 栈指针位置是esp1处。
此时程序通过使用两个交换指令分别把eax ebx寄存器的值保存在esp0 esp1位置处 而把出错号交换到eax寄
存器中:函数地址交换到了ebx寄存器中。随后的处理过程则和无错误号一样。
----------------------------------------------------------------------------------------
一般寄存器:AX BX CX DX
AX:累积暂存器 BX:基底暂存器 CX:计数暂存器 DX:资料暂存器
索引暂存器:SI DI
SI:来源索引暂存器 DI:目的索引暂存器
堆叠 基底暂存器:SP BP
SP:堆叠指标暂存器 BP:基底指标暂存器
cs是代码段寄存器
ds是数据段寄存器
ss是堆栈段寄存器
es是扩展段寄存器
fs是标志段寄存器
gs是全局段寄存器
----------------------------------------------------------------------------------------
把这些寄存器入栈保护
pushl %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
-----------------------------------------------------------------------------------------------
无错误号的代码:
核心代码:xchg1 %eax,(%esp) 交换ax和sp
push $0 0作为错误号压栈
lea 44(%esp), %edx 把中断的地方压栈
call *%eax 调用中断打印函数
add1 $8 %esp 函数的参数出栈
----------------------------------------------------------------------------------------
有错误号的代码:
error_code:
0x03:小结
此篇文章主要讲解了中断的工作原理 下篇文章我们一起来看8086 PC机的8259A中断原理。 |