如何讓gcc編譯中斷函數

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章