作者:好人森森_195 | 来源:互联网 | 2023-02-07 10:13
考虑以下功能:
void foo(){
//do something
}
在组装时,它看起来像这样(不准确):
push something
;do stuff
pop something
ret
但是我不想要这个生成的代码(RET,PUSH,POP ...)。我只想在代码块上加上标签,所以我必须返回自己:
void bar(){
//do something
asm("iret") //i want to use this function as a ISR
}
并在组装中看起来像这样:
; do something
iret
没有PUSH,POP或RET。是否有任何预处理器指令或关键字可以帮助我完成此任务?
我正在Windows下使用GCC和NASM,并且试图生成自己的中断服务例程(ISR)。
1> Michael Petc..:
尚不清楚您要完成什么。似乎您想要一个中断处理程序,该中断处理程序iret
默认情况下没有其他推送和弹出操作。
海湾合作委员会
使用GCC(不使用NASM)可能是这样的:
/* Make C extern declarations of the ISR entry points */
extern void isr_test1(void);
extern void isr_test2(void);
/* Define a do nothing ISR stub */
__asm__(".global isr_test1\n"
"isr_test1:\n\t"
/* Other stuff here */
"iret");
/* Define an ISR stub that makes a call to a C function */
__asm__(".global isr_test2\n"
"isr_test2:\n\t"
"cld\n\t" /* Set direction flag forward for C functions */
"pusha\n\t" /* Save all the registers */
/* Other stuff here */
"call isr_test2_handler\n\t"
"popa\n\t" /* Restore all the registers */
"iret");
void isr_test2_handler(void)
{
return;
}
GCC中的基本__asm__
语句可以放在函数之外。我们为我们的中断服务程序(ISR)定义标签,并使它们在外部可见(您可能不需要全局可见性,但无论如何我都会显示出来)。.globl
我创建了几个示例中断服务例程。一个只做一个iret
,而另一个做一个对C处理程序的函数调用。我们保存所有寄存器,然后将其还原。C函数要求将方向标志设置为正向,因此在调用C函数之前需要CLD。此示例代码适用于32位目标。64位可以通过保存寄存器单独而不是使用来完成PUSHA和POPA。
注意:如果使用GCC在Windows函数名称内组装块可能会需要有预先考虑_
(下划线)。它看起来像:
/* Make C extern declarations of the ISR entry points */
extern void isr_test1(void);
extern void isr_test2(void);
/* Define a do nothing ISR stub */
__asm__(".global _isr_test1\n"
"_isr_test1:\n\t"
/* Other stuff here */
"iret");
/* Define an ISR stub that makes a call to a C function */
__asm__(".global _isr_test2\n"
"_isr_test2:\n\t"
"cld\n\t" /* Set direction flag forward for C functions */
"pusha\n\t" /* Save all the registers */
/* Other stuff here */
"call _isr_test2_handler\n\t"
"popa\n\t" /* Restore all the registers */
"iret");
void isr_test2_handler(void)
{
return;
}
MSVC / MSVC ++
Microsoft的C / C ++编译器支持函数的裸属性。他们将此属性描述为:
裸存储类属性是Microsoft对C语言的特定扩展。对于使用裸存储类属性声明的函数,编译器将生成不含序言和结语代码的代码。您可以使用此功能通过内联汇编代码编写自己的序言/结尾代码序列。裸函数在编写虚拟设备驱动程序时特别有用。
一个示例中断服务程序可以这样完成:
__declspec(naked) int isr_test(void)
{
/* Function body */
__asm { iret };
}
您将需要处理保存和恢复寄存器的问题,以与上述GCC示例类似的方式自行设置方向标志。
GCC 7.x +在x86 / x86-64目标上引入了中断属性
现在,您可以在GCC 7.0+上使用__attribute__((interrupt))
函数了。仅在x86和x86-64目标上最近才支持此属性:
打断
使用此属性指示指定的函数是中断处理程序还是异常处理程序(取决于传递给该函数的参数,将进一步说明)。当存在此属性时,编译器会生成适合在中断处理程序中使用的函数进入和退出序列。IRET指令而不是RET指令用于从中断处理程序中返回。除由IRET指令恢复的EFLAGS寄存器外,所有寄存器均由编译器保留。由于GCC不会保留MPX,SSE,MMX或x87状态,因此应使用GCC选项-mgeneral-regs-only来编译中断和异常处理程序。
该方法仍然存在缺陷。如果您希望C代码访问中断时出现的寄存器内容,那么目前尚无可靠的方法来使用此机制。如果您正在编写软件中断并需要访问寄存器以确定要执行的操作(即:int 0x80
在Linux上),这将很方便。另一个示例是允许中断将所有寄存器的内容转储到显示器以进行调试。