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类:
带返回错误码的中断调用
不带返回错误码的中断调用
将所有的寄存器值入栈
SS:存放栈顶的段地址
SP:存放栈顶的偏移地址
一个栈也就是一块内存区域,我们必须要有基地址(也就是段地址左移4位)和偏移地址。
要在一个栈中寻址的话,也需要段地址和偏移地址。
——————————————————————————————————————-
CS:代码段寄存器
IP:指令段寄存器
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中断原理。
来源:freebuf.com 2021-06-02 16:59:24 by: 星云博创科技有限公司
请登录后发表评论
注册