在x86中,一般函数通过"call"指令调用,"ret"指令返回,但是中断函数不同,它在中断或者异常发生时自动切入(或者使用"int"指令),此时cpu会向栈中压入一堆寄存器来保护现场,如果是异常,还会多压一个错误代码,因此编写中断函数返回时要使用"iret"指令,但是gcc默认是无法编译出iret指令的。当然,有一些IDE自定义了一些东西可以用来编译中断函数,也有一些人会直接用内联汇编解决,但用内联不是一个好办法。通过查询gcc手册,我发现gcc-7.5增加了对x86架构中断函数的支持,可以用来写中断程序。但是要注意,编译时要添加("-mgeneral-regs-only "),gcc才可以编译中断函数。
gcc手册网址:https://gcc.gnu.org/onlinedocs
位置:https://gcc.gnu.org/onlinedocs/gcc-7.5.0/gcc/x86-Function-Attributes.html#x86-Function-Attributes
/*uword_t 类型是用来读取异常压入的错误代码的,gcc手册很贴心的给出了32位和64位系统对于该类型的定义*/
#ifdef __x86_64__
typedef unsigned long long int uword_t;
#else
typedef unsigned int uword_t;
#endif
/*这个结构用来读取中断发生时cpu压栈的数据,但是因为平台不同,cpu的操作也可能不同,所以需要自己根据自己的平台定义*/
struct interrupt_frame;
__attribute__ ((interrupt)) /*这就是gcc增加的用来描述中断函数的符号,只有gcc-7.5及7.5以上支持*/
void div_error(struct interrupt_frame* frame,uword_t error_code)
/*这是个除法错误异常(除以0造成的),因为异常和中断不同,异常会多压入一个错误代码,因此上面的参数多了一个uword_t error_code*/
{
/*code*/
return;
}
__attribute__ ((interrupt))
void timer_int(struct interrupt_frame* frame)
/*这是时钟中断,间隔一定时间会向cpu发送一次中断,windows下是100Hz。中断不会压入错误代码,因此也就没有uword_t error_code*/
{
/*code*/
return;
}
使用以下代码编译测试:
typedef unsigned int uint32;
struct interrupt_frame{
uint32 eip;
uint32 cs;
uint32 eflags;
};
__attribute__ ((interrupt))
void timer_int(struct interrupt_frame* frame)
{
return;
}
gcc命令:gcc main1.c -mgeneral-regs-only -S
生成的汇编文件如下:
.file "main1.c"
.text
.globl _timer_int
.def _timer_int; .scl 2; .type 32; .endef
_timer_int:
LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %eax
subl $4, %esp
.cfi_offset 0, -12
leal 4(%ebp), %eax
movl %eax, -8(%ebp)
nop
addl $4, %esp
popl %eax
.cfi_restore 0
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
iret
.cfi_endproc
LFE0:
.ident "GCC: (i686-win32-dwarf-rev0, Built by MinGW-W64 project) 8.1.0"
可以看到,返回确实被编译为了"iret"。
下面是手册中对此的说明(x86架构)