linux 1.0 内核注解 linux/kernel/sys_call.S
时间:2009-03-03 来源:taozhijiangscu
/********************************************
*Created By: 陶治江
*Date: 2009-3-1
********************************************/
/* Stack layout in 'ret_from_system_call':
* 可以看出的是:这里的系统调用是涉及到了特权级的转换的,
* 因为从堆栈中可以分析出对原先的ss esp进行了保存了
* 0(%esp) - %ebx
* 4(%esp) - %ecx
* 8(%esp) - %edx
* C(%esp) - %esi
* 10(%esp) - %edi
* 14(%esp) - %ebp
* 18(%esp) - %eax
* 1C(%esp) - %ds
* 20(%esp) - %es
* 24(%esp) - %fs
* 28(%esp) - %gs
* 2C(%esp) - orig_eax
* 30(%esp) - %eip
* 34(%esp) - %cs
* 38(%esp) - %eflags
* 3C(%esp) - %oldesp
* 40(%esp) - %oldss
*/ #include <linux/segment.h> EBX = 0x00
ECX = 0x04
EDX = 0x08
ESI = 0x0C
EDI = 0x10
EBP = 0x14
EAX = 0x18
DS = 0x1C
ES = 0x20
FS = 0x24
GS = 0x28
ORIG_EAX = 0x2C
EIP = 0x30
CS = 0x34
EFLAGS = 0x38
OLDESP = 0x3C
OLDSS = 0x40 //这里是处理器EFLAGS中的各个标志的位图
CF_MASK = 0x00000001
IF_MASK = 0x00000200
NT_MASK = 0x00004000
VM_MASK = 0x00020000 /*these are offsets into the task-struct.(以字节为单位的偏移)
*task_struct被定义在了linux/include/sched.h中,很长很长*/
state = 0
counter = 4
priority = 8
signal = 12
blocked = 16
flags = 20
errno = 24
dbgreg6 = 52
dbgreg7 = 56 ENOSYS = 38 //unsigned long intr_count = 0; .globl _system_call,_lcall7
.globl _device_not_available, _coprocessor_error
.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,_reserved
.globl _alignment_check,_page_fault
.globl ret_from_sys_call //对寄存器进行顺序保存,同时将ds es加载为内核的数据段
//而fs设置为用户的数据段
#define SAVE_ALL \
cld; \
push %gs; \
push %fs; \
push %es; \
push %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
//相应的段寄存器的加载
movl $(KERNEL_DS),%edx; \
mov %dx,%ds; \
mov %dx,%es; \
movl $(USER_DS),%edx; \
mov %dx,%fs; #define RESTORE_ALL \
cmpw $(KERNEL_CS),CS(%esp); \ //应该是查看当前是否是内核态
je 1f; \
movl _current,%eax; \ //应该是当前任务
movl dbgreg7(%eax),%ebx; \ //Hardware debugging registers的参数
movl %ebx,%db7; \ //???
1: popl %ebx; \
popl %ecx; \
popl %edx; \
popl %esi; \
popl %edi; \
popl %ebp; \
popl %eax; \
pop %ds; \
pop %es; \
pop %fs; \
pop %gs; \
addl $4,%esp; \
iret //这个是中断返回,会弹出堆栈中的eflags寄存器的
//值,并同时弹出返回地址的 /*//struct pt_regs被定义在linux/include/linux/ptrace.h头文件中
struct pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
unsigned short ds, __dsu;
unsigned short es, __esu;
unsigned short fs, __fsu;
unsigned short gs, __gsu; <- //SAVE_ALL宏可以保存的参数
long orig_eax;
long eip;
unsigned short cs, __csu;
long eflags;
long esp;
unsigned short ss, __ssu;
};*/ .align 4
_lcall7:
pushfl # We get a different stack layout with call gates,
pushl %eax # which has to be cleaned up later..
//从堆栈的分布来看,这个pushl %eax可能就是通过RESTORE_ALL的
//addl $4,%esp;来保存的,这个%eax就是pt_regs中的orig_eax了
SAVE_ALL
//下面的6步好像是对堆栈的数据进行调整
//目前来看是可以了解的,因为在上面的SAVE_ALL调用之前是
//push %eax,pushfl 而同上面的pt_regs结构来比较可以发现
//至少应该是eip的位置变成了eflags
//其他pt_regs的参数确定也只有在具体调用的地方确认了 :-(
movl EIP(%esp),%eax # due to call gates, this is eflags, not eip..
movl CS(%esp),%edx # this is eip..
movl EFLAGS(%esp),%ecx # and this is cs..
movl %eax,EFLAGS(%esp) #
movl %edx,EIP(%esp) # Now we move them to their "normal" places
movl %ecx,CS(%esp) #
movl %esp,%eax
pushl %eax
call _iABI_emulate // linux/ibcs/emulate.c
popl %eax //至少现在的堆栈满足上面的条件的,但是pushl %eax
//在call中什么用?
//发现自己真的是混球了,%esp就是堆栈的地址啊,而
//_iABI_emulate的参数就是堆栈指针啊 ~~~
jmp ret_from_sys_call .align 4
handle_bottom_half:
pushfl
incl _intr_count
sti //打开中断
call _do_bottom_half
popfl
decl _intr_count
jmp 9f
.align 4
reschedule:
pushl $ret_from_sys_call
jmp _schedule //linux/kernel/sched.c,返回还是到ret_from_sys_call了
.align 4 //应该这里是所有的系统调用的入口点吧,起码这里
//对中断向量的值进行了检测了
//对,是的
_system_call:
pushl %eax # save orig_eax
SAVE_ALL
movl $-ENOSYS,EAX(%esp) //应该这些错误一般都是作为返回值的,下面也提到了
cmpl _NR_syscalls,%eax //int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
//定义的挺好的哈
jae ret_from_sys_call
movl _current,%ebx
andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors
//关于调试的真的不太懂哎
movl $0,errno(%ebx)
movl %db6,%edx
movl %edx,dbgreg6(%ebx) # save current hardware debugging status
testb $0x20,flags(%ebx) # PF_TRACESYS
jne 1f
call _sys_call_table(,%eax,4)
movl %eax,EAX(%esp) # save the return value
//这里说了是返回值了,所以呢,前面是默认将错误的
//返回值压入,这里正确了就进行覆盖
movl errno(%ebx),%edx //0,如果成功的话
negl %edx
je ret_from_sys_call //这里表示正常,下面是异常,就将错误放在eax中返回,
//同时设置CF标志
movl %edx,EAX(%esp)
orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error
jmp ret_from_sys_call
.align 4 //这里可能是调试调用的历程了
1: call _syscall_trace
movl ORIG_EAX(%esp),%eax
call _sys_call_table(,%eax,4)
movl %eax,EAX(%esp) # save the return value
movl _current,%eax
movl errno(%eax),%edx
negl %edx
je 1f
movl %edx,EAX(%esp)
orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error
1: call _syscall_trace
//现在感觉这是一个很重要重要的标号了!!!
.align 4,0x90
ret_from_sys_call:
cmpl $0,_intr_count
jne 2f
//这里的intr_count是0,然后检查是否还有需要
//处理的中断,如果有就跳到handle_bottom_half处理
//关于handle_bottom_half一次完整的执行是inc dec平衡的
//如果intr_count==0就表示handle_bottom_half没有执行
//就可以进行检测进行新的处理的
//而handle_bottom_half的返回是直接返回到后面的9标号的
movl _bh_mask,%eax
andl _bh_active,%eax
jne handle_bottom_half
9: movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are
testl $(VM_MASK),%eax # different then
jne 1f //虚拟8086方式??
cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ?
je 2f
1: sti
orl $(IF_MASK),%eax # these just try to make sure
andl $~NT_MASK,%eax # the program doesn't do anything
movl %eax,EFLAGS(%esp) # stupid
//下面就涉及到了任务的重新调度了
cmpl $0,_need_resched //具体哪里设置这个标记得以后看了
jne reschedule
movl _current,%eax
cmpl _task,%eax # task[0] cannot have signals
je 2f //task[0]是最悠闲的了
//由于中断是不嵌套的,所以这里从中断返回的时候
//如果当前的任务不是可执行的,或者时间被利用完
//了,就需要进行重新调度了,否则如果没有信号要处理
//就可以直接返回到中断进入的环境进行继续执行了
cmpl $0,state(%eax) # state TASK_RUNNING=0
jne reschedule
cmpl $0,counter(%eax) # counter //no time
je reschedule
movl blocked(%eax),%ecx
movl %ecx,%ebx # save blocked in %ebx for signal handling
notl %ecx
andl signal(%eax),%ecx //如果当前信号中除了可屏蔽的信号还有其他的信号
//就进行do_signal的调用
jne signal_return
2: RESTORE_ALL
.align 4
signal_return:
movl %esp,%ecx
pushl %ecx
testl $(VM_MASK),EFLAGS(%ecx)
jne v86_signal_return //当是虚拟8086的时候需要对部分数据进行保存操作
pushl %ebx //被屏蔽的信号量
call _do_signal //asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs);
//调用参数是需要被屏蔽的信号量的
popl %ebx
popl %ebx //平衡堆栈
RESTORE_ALL
.align 4
v86_signal_return:
call _save_v86_state //linux/kernel/sys.c
//上面的函数返回的是esp的值
movl %eax,%esp
pushl %eax //这里作为第二个参数来给do_signal调用
pushl %ebx
call _do_signal
popl %ebx
popl %ebx
RESTORE_ALL //下面的基本都是先压入错误码,
//(感觉可能是如果调用函数没有压入错误码,这里就压入0占位,如果有
//错误码就会事先压入的,这里就不另外压入了)然后压入处理历程的函数,然后
//再调用error_code历程,这里全部是系统调用的实际操作了,
//从 error_code之后调用jmp ret_from_sys_call进行后续的处理 //对照看
/* Stack layout in 'ret_from_system_call':
* 可以看出的是:这里的系统调用是涉及到了特权级的转换的,
* 因为从堆栈中可以分析出对原先的ss esp进行了保存了
* 0(%esp) - %ebx
* 4(%esp) - %ecx
* 8(%esp) - %edx
* C(%esp) - %esi
* 10(%esp) - %edi
* 14(%esp) - %ebp
* 18(%esp) - %eax
* 1C(%esp) - %ds
* 20(%esp) - %es
* 24(%esp) - %fs
* 28(%esp) - %gs
* 2C(%esp) - orig_eax
* 30(%esp) - %eip
* 34(%esp) - %cs
* 38(%esp) - %eflags
* 3C(%esp) - %oldesp
* 40(%esp) - %oldss */
.align 4
_divide_error:
pushl $0 # no error code ->%eax
pushl $_do_divide_error ->处理历程的地址,这个位置将会被后来的%gs所替换
.align 4,0x90
error_code: //有错误号的处理方式
push %fs
push %es
push %ds
pushl %eax
pushl %ebp
pushl %edi
pushl %esi
pushl %edx
pushl %ecx
pushl %ebx
movl $0,%eax
movl %eax,%db7 # disable hardware debugging...
cld
movl $-1, %eax
xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. )
xorl %ebx,%ebx # zero ebx
mov %gs,%bx # get the lower order bits of gs
xchgl %ebx, GS(%esp) # get the address and save gs.
pushl %eax # push the error code
//虽然错误码压入的比较早,但是被交换到了比较近的地方了
lea 4(%esp),%edx
pushl %edx
//内核态
movl $(KERNEL_DS),%edx
mov %dx,%ds
mov %dx,%es
movl $(USER_DS),%edx
mov %dx,%fs
//这里可能是关于调试的部分,不是很懂
pushl %eax
movl _current,%eax
movl %db6,%edx
movl %edx,dbgreg6(%eax) # save current hardware debugging status
popl %eax
call *%ebx //交换出来的调用地址
addl $8,%esp //计算后堆栈平衡了
jmp ret_from_sys_call .align 4
_coprocessor_error:
pushl $0
pushl $_do_coprocessor_error
jmp error_code .align 4
//可能也是关于协处理器的
_device_not_available:
pushl $-1 # mark this as an int
SAVE_ALL
pushl $ret_from_sys_call
movl %cr0,%eax
testl $0x4,%eax # EM (math emulation bit)
je _math_state_restore //模拟协处理器,其实应该是如果协处理不存在
//都会有这个调用的,但是如果设置了模拟位就跳出去了
//否则就math_emulate,这里的实现就是崩溃了
pushl $0 # temporary storage for ORIG_EIP
call _math_emulate //目前的实现是do_exit(),可能内核就傻掉了,所以不应该允许出现这个错误的
addl $4,%esp
ret //注意这里不是iret,估计弹出的就是ret_from_sys_call了 .align 4
_debug:
pushl $0
pushl $_do_debug
jmp error_code .align 4
_nmi:
pushl $0
pushl $_do_nmi
jmp error_code .align 4
_int3:
pushl $0
pushl $_do_int3
jmp error_code .align 4
_overflow:
pushl $0
pushl $_do_overflow
jmp error_code .align 4
_bounds:
pushl $0
pushl $_do_bounds
jmp error_code .align 4
_invalid_op:
pushl $0
pushl $_do_invalid_op
jmp error_code .align 4
_coprocessor_segment_overrun:
pushl $0
pushl $_do_coprocessor_segment_overrun
jmp error_code .align 4
_reserved:
pushl $0
pushl $_do_reserved
jmp error_code .align 4
_double_fault:
pushl $_do_double_fault
jmp error_code .align 4
_invalid_TSS:
pushl $_do_invalid_TSS
jmp error_code .align 4
_segment_not_present:
pushl $_do_segment_not_present
jmp error_code .align 4
_stack_segment:
pushl $_do_stack_segment
jmp error_code .align 4
_general_protection:
pushl $_do_general_protection
jmp error_code .align 4
_alignment_check:
pushl $_do_alignment_check
jmp error_code .align 4
_page_fault:
pushl $_do_page_fault
jmp error_code
文档地址:http://blogimg.chinaunix.net/blog/upfile2/090303173104.pdf
*Created By: 陶治江
*Date: 2009-3-1
********************************************/
/* Stack layout in 'ret_from_system_call':
* 可以看出的是:这里的系统调用是涉及到了特权级的转换的,
* 因为从堆栈中可以分析出对原先的ss esp进行了保存了
* 0(%esp) - %ebx
* 4(%esp) - %ecx
* 8(%esp) - %edx
* C(%esp) - %esi
* 10(%esp) - %edi
* 14(%esp) - %ebp
* 18(%esp) - %eax
* 1C(%esp) - %ds
* 20(%esp) - %es
* 24(%esp) - %fs
* 28(%esp) - %gs
* 2C(%esp) - orig_eax
* 30(%esp) - %eip
* 34(%esp) - %cs
* 38(%esp) - %eflags
* 3C(%esp) - %oldesp
* 40(%esp) - %oldss
*/ #include <linux/segment.h> EBX = 0x00
ECX = 0x04
EDX = 0x08
ESI = 0x0C
EDI = 0x10
EBP = 0x14
EAX = 0x18
DS = 0x1C
ES = 0x20
FS = 0x24
GS = 0x28
ORIG_EAX = 0x2C
EIP = 0x30
CS = 0x34
EFLAGS = 0x38
OLDESP = 0x3C
OLDSS = 0x40 //这里是处理器EFLAGS中的各个标志的位图
CF_MASK = 0x00000001
IF_MASK = 0x00000200
NT_MASK = 0x00004000
VM_MASK = 0x00020000 /*these are offsets into the task-struct.(以字节为单位的偏移)
*task_struct被定义在了linux/include/sched.h中,很长很长*/
state = 0
counter = 4
priority = 8
signal = 12
blocked = 16
flags = 20
errno = 24
dbgreg6 = 52
dbgreg7 = 56 ENOSYS = 38 //unsigned long intr_count = 0; .globl _system_call,_lcall7
.globl _device_not_available, _coprocessor_error
.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,_reserved
.globl _alignment_check,_page_fault
.globl ret_from_sys_call //对寄存器进行顺序保存,同时将ds es加载为内核的数据段
//而fs设置为用户的数据段
#define SAVE_ALL \
cld; \
push %gs; \
push %fs; \
push %es; \
push %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
//相应的段寄存器的加载
movl $(KERNEL_DS),%edx; \
mov %dx,%ds; \
mov %dx,%es; \
movl $(USER_DS),%edx; \
mov %dx,%fs; #define RESTORE_ALL \
cmpw $(KERNEL_CS),CS(%esp); \ //应该是查看当前是否是内核态
je 1f; \
movl _current,%eax; \ //应该是当前任务
movl dbgreg7(%eax),%ebx; \ //Hardware debugging registers的参数
movl %ebx,%db7; \ //???
1: popl %ebx; \
popl %ecx; \
popl %edx; \
popl %esi; \
popl %edi; \
popl %ebp; \
popl %eax; \
pop %ds; \
pop %es; \
pop %fs; \
pop %gs; \
addl $4,%esp; \
iret //这个是中断返回,会弹出堆栈中的eflags寄存器的
//值,并同时弹出返回地址的 /*//struct pt_regs被定义在linux/include/linux/ptrace.h头文件中
struct pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
unsigned short ds, __dsu;
unsigned short es, __esu;
unsigned short fs, __fsu;
unsigned short gs, __gsu; <- //SAVE_ALL宏可以保存的参数
long orig_eax;
long eip;
unsigned short cs, __csu;
long eflags;
long esp;
unsigned short ss, __ssu;
};*/ .align 4
_lcall7:
pushfl # We get a different stack layout with call gates,
pushl %eax # which has to be cleaned up later..
//从堆栈的分布来看,这个pushl %eax可能就是通过RESTORE_ALL的
//addl $4,%esp;来保存的,这个%eax就是pt_regs中的orig_eax了
SAVE_ALL
//下面的6步好像是对堆栈的数据进行调整
//目前来看是可以了解的,因为在上面的SAVE_ALL调用之前是
//push %eax,pushfl 而同上面的pt_regs结构来比较可以发现
//至少应该是eip的位置变成了eflags
//其他pt_regs的参数确定也只有在具体调用的地方确认了 :-(
movl EIP(%esp),%eax # due to call gates, this is eflags, not eip..
movl CS(%esp),%edx # this is eip..
movl EFLAGS(%esp),%ecx # and this is cs..
movl %eax,EFLAGS(%esp) #
movl %edx,EIP(%esp) # Now we move them to their "normal" places
movl %ecx,CS(%esp) #
movl %esp,%eax
pushl %eax
call _iABI_emulate // linux/ibcs/emulate.c
popl %eax //至少现在的堆栈满足上面的条件的,但是pushl %eax
//在call中什么用?
//发现自己真的是混球了,%esp就是堆栈的地址啊,而
//_iABI_emulate的参数就是堆栈指针啊 ~~~
jmp ret_from_sys_call .align 4
handle_bottom_half:
pushfl
incl _intr_count
sti //打开中断
call _do_bottom_half
popfl
decl _intr_count
jmp 9f
.align 4
reschedule:
pushl $ret_from_sys_call
jmp _schedule //linux/kernel/sched.c,返回还是到ret_from_sys_call了
.align 4 //应该这里是所有的系统调用的入口点吧,起码这里
//对中断向量的值进行了检测了
//对,是的
_system_call:
pushl %eax # save orig_eax
SAVE_ALL
movl $-ENOSYS,EAX(%esp) //应该这些错误一般都是作为返回值的,下面也提到了
cmpl _NR_syscalls,%eax //int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
//定义的挺好的哈
jae ret_from_sys_call
movl _current,%ebx
andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors
//关于调试的真的不太懂哎
movl $0,errno(%ebx)
movl %db6,%edx
movl %edx,dbgreg6(%ebx) # save current hardware debugging status
testb $0x20,flags(%ebx) # PF_TRACESYS
jne 1f
call _sys_call_table(,%eax,4)
movl %eax,EAX(%esp) # save the return value
//这里说了是返回值了,所以呢,前面是默认将错误的
//返回值压入,这里正确了就进行覆盖
movl errno(%ebx),%edx //0,如果成功的话
negl %edx
je ret_from_sys_call //这里表示正常,下面是异常,就将错误放在eax中返回,
//同时设置CF标志
movl %edx,EAX(%esp)
orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error
jmp ret_from_sys_call
.align 4 //这里可能是调试调用的历程了
1: call _syscall_trace
movl ORIG_EAX(%esp),%eax
call _sys_call_table(,%eax,4)
movl %eax,EAX(%esp) # save the return value
movl _current,%eax
movl errno(%eax),%edx
negl %edx
je 1f
movl %edx,EAX(%esp)
orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error
1: call _syscall_trace
//现在感觉这是一个很重要重要的标号了!!!
.align 4,0x90
ret_from_sys_call:
cmpl $0,_intr_count
jne 2f
//这里的intr_count是0,然后检查是否还有需要
//处理的中断,如果有就跳到handle_bottom_half处理
//关于handle_bottom_half一次完整的执行是inc dec平衡的
//如果intr_count==0就表示handle_bottom_half没有执行
//就可以进行检测进行新的处理的
//而handle_bottom_half的返回是直接返回到后面的9标号的
movl _bh_mask,%eax
andl _bh_active,%eax
jne handle_bottom_half
9: movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are
testl $(VM_MASK),%eax # different then
jne 1f //虚拟8086方式??
cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ?
je 2f
1: sti
orl $(IF_MASK),%eax # these just try to make sure
andl $~NT_MASK,%eax # the program doesn't do anything
movl %eax,EFLAGS(%esp) # stupid
//下面就涉及到了任务的重新调度了
cmpl $0,_need_resched //具体哪里设置这个标记得以后看了
jne reschedule
movl _current,%eax
cmpl _task,%eax # task[0] cannot have signals
je 2f //task[0]是最悠闲的了
//由于中断是不嵌套的,所以这里从中断返回的时候
//如果当前的任务不是可执行的,或者时间被利用完
//了,就需要进行重新调度了,否则如果没有信号要处理
//就可以直接返回到中断进入的环境进行继续执行了
cmpl $0,state(%eax) # state TASK_RUNNING=0
jne reschedule
cmpl $0,counter(%eax) # counter //no time
je reschedule
movl blocked(%eax),%ecx
movl %ecx,%ebx # save blocked in %ebx for signal handling
notl %ecx
andl signal(%eax),%ecx //如果当前信号中除了可屏蔽的信号还有其他的信号
//就进行do_signal的调用
jne signal_return
2: RESTORE_ALL
.align 4
signal_return:
movl %esp,%ecx
pushl %ecx
testl $(VM_MASK),EFLAGS(%ecx)
jne v86_signal_return //当是虚拟8086的时候需要对部分数据进行保存操作
pushl %ebx //被屏蔽的信号量
call _do_signal //asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs);
//调用参数是需要被屏蔽的信号量的
popl %ebx
popl %ebx //平衡堆栈
RESTORE_ALL
.align 4
v86_signal_return:
call _save_v86_state //linux/kernel/sys.c
//上面的函数返回的是esp的值
movl %eax,%esp
pushl %eax //这里作为第二个参数来给do_signal调用
pushl %ebx
call _do_signal
popl %ebx
popl %ebx
RESTORE_ALL //下面的基本都是先压入错误码,
//(感觉可能是如果调用函数没有压入错误码,这里就压入0占位,如果有
//错误码就会事先压入的,这里就不另外压入了)然后压入处理历程的函数,然后
//再调用error_code历程,这里全部是系统调用的实际操作了,
//从 error_code之后调用jmp ret_from_sys_call进行后续的处理 //对照看
/* Stack layout in 'ret_from_system_call':
* 可以看出的是:这里的系统调用是涉及到了特权级的转换的,
* 因为从堆栈中可以分析出对原先的ss esp进行了保存了
* 0(%esp) - %ebx
* 4(%esp) - %ecx
* 8(%esp) - %edx
* C(%esp) - %esi
* 10(%esp) - %edi
* 14(%esp) - %ebp
* 18(%esp) - %eax
* 1C(%esp) - %ds
* 20(%esp) - %es
* 24(%esp) - %fs
* 28(%esp) - %gs
* 2C(%esp) - orig_eax
* 30(%esp) - %eip
* 34(%esp) - %cs
* 38(%esp) - %eflags
* 3C(%esp) - %oldesp
* 40(%esp) - %oldss */
.align 4
_divide_error:
pushl $0 # no error code ->%eax
pushl $_do_divide_error ->处理历程的地址,这个位置将会被后来的%gs所替换
.align 4,0x90
error_code: //有错误号的处理方式
push %fs
push %es
push %ds
pushl %eax
pushl %ebp
pushl %edi
pushl %esi
pushl %edx
pushl %ecx
pushl %ebx
movl $0,%eax
movl %eax,%db7 # disable hardware debugging...
cld
movl $-1, %eax
xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. )
xorl %ebx,%ebx # zero ebx
mov %gs,%bx # get the lower order bits of gs
xchgl %ebx, GS(%esp) # get the address and save gs.
pushl %eax # push the error code
//虽然错误码压入的比较早,但是被交换到了比较近的地方了
lea 4(%esp),%edx
pushl %edx
//内核态
movl $(KERNEL_DS),%edx
mov %dx,%ds
mov %dx,%es
movl $(USER_DS),%edx
mov %dx,%fs
//这里可能是关于调试的部分,不是很懂
pushl %eax
movl _current,%eax
movl %db6,%edx
movl %edx,dbgreg6(%eax) # save current hardware debugging status
popl %eax
call *%ebx //交换出来的调用地址
addl $8,%esp //计算后堆栈平衡了
jmp ret_from_sys_call .align 4
_coprocessor_error:
pushl $0
pushl $_do_coprocessor_error
jmp error_code .align 4
//可能也是关于协处理器的
_device_not_available:
pushl $-1 # mark this as an int
SAVE_ALL
pushl $ret_from_sys_call
movl %cr0,%eax
testl $0x4,%eax # EM (math emulation bit)
je _math_state_restore //模拟协处理器,其实应该是如果协处理不存在
//都会有这个调用的,但是如果设置了模拟位就跳出去了
//否则就math_emulate,这里的实现就是崩溃了
pushl $0 # temporary storage for ORIG_EIP
call _math_emulate //目前的实现是do_exit(),可能内核就傻掉了,所以不应该允许出现这个错误的
addl $4,%esp
ret //注意这里不是iret,估计弹出的就是ret_from_sys_call了 .align 4
_debug:
pushl $0
pushl $_do_debug
jmp error_code .align 4
_nmi:
pushl $0
pushl $_do_nmi
jmp error_code .align 4
_int3:
pushl $0
pushl $_do_int3
jmp error_code .align 4
_overflow:
pushl $0
pushl $_do_overflow
jmp error_code .align 4
_bounds:
pushl $0
pushl $_do_bounds
jmp error_code .align 4
_invalid_op:
pushl $0
pushl $_do_invalid_op
jmp error_code .align 4
_coprocessor_segment_overrun:
pushl $0
pushl $_do_coprocessor_segment_overrun
jmp error_code .align 4
_reserved:
pushl $0
pushl $_do_reserved
jmp error_code .align 4
_double_fault:
pushl $_do_double_fault
jmp error_code .align 4
_invalid_TSS:
pushl $_do_invalid_TSS
jmp error_code .align 4
_segment_not_present:
pushl $_do_segment_not_present
jmp error_code .align 4
_stack_segment:
pushl $_do_stack_segment
jmp error_code .align 4
_general_protection:
pushl $_do_general_protection
jmp error_code .align 4
_alignment_check:
pushl $_do_alignment_check
jmp error_code .align 4
_page_fault:
pushl $_do_page_fault
jmp error_code
文档地址:http://blogimg.chinaunix.net/blog/upfile2/090303173104.pdf
相关阅读 更多 +