在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架構)