在没有编译器的情况下创建C函数会生成prologue / epilogue&RET指令吗?

考虑这个function:

void foo(){ //do something } 

在assembly中,它看起来像这样(不准确):

 push something ;do stuff pop something ret 

但我不想要这个生成的代码( RETPUSHPOP ……)。 我只想要一个代码块的标签,所以我必须回复自己:

 void bar(){ //do something asm("iret") //i want to use this function as a ISR } 

在汇编中,它看起来像这样:

 ; do something iret 

没有PUSHPOPRET 。 是否有任何预处理器指令或关键字可以让我实现这一目标?

我在Windows下使用GCCNASM ,我正在尝试生成自己的中断服务例程(ISR)。

你想要完成什么并不完全清楚。 看起来你想要一个中断处理程序来执行iret ,默认情况下没有其他推送和弹出。


GCC

使用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一个iret而另一个做一个函数调用C处理程序。 我们保存所有寄存器并在之后恢复它们。 C函数需要向前设置方向标志,因此在调用C函数之前我们需要一个CLD 。 此示例代码适用于32位目标。 64位可以通过单独保存寄存器而不是使用PUSHAPOPA来完成。

注意 :如果在Windows上使用GCC则程序集块中的函数名称可能需要添加_ (下划线)。 它看起来像:

 /* 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 ++编译器支持函数的属性。 他们将此属性描述为:

naked storage-class属性是特定于Microsoft的C语言扩展。 对于使用naked storage-class属性声明的函数,编译器生成没有prolog和epilog代码的代码。 您可以使用此function使用内联汇编程序代码编写自己的prolog / epilog代码序列。 裸函数在编写虚拟设备驱动程序时特别有用。

中断服务例程的示例可以这样做:

 __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目标上受支持:

打断

使用此属性指示指定的函数是中断处理程序或exception处理程序(取决于传递给函数的参数,进一步说明)。 当存在此属性时,编译器生成适合在中断处理程序中使用的函数入口和出口序列。 IRET指令而不是RET指令用于从中断处理程序返回。 除了由IRET指令恢复的EFLAGS寄存器外,所有寄存器都由编译器保留。 由于GCC不保留MPX,SSE,MMX和x87状态,因此应使用GCC选项-mgeneral-regs-only来编译中断和exception处理程序。

这种方法仍然存在不足之处。 如果您希望C代码访问中断时出现的寄存器内容,则目前没有可靠的方法来实现此机制。 如果您正在编写软件中断并需要访问寄存器以确定要采取的操作(即:Linux上的int 0x80 ),这将非常方便。 另一个例子是允许中断将所有寄存器内容转储到显示器以进行调试。

我找到了一个简洁的解决方法:

在程序集中定义函数但调用extern c函数:

 bits 32 global _bar extern _foo section .data section .text _bar: call _foo iret 

在C:

 void foo(){ //do your stuff here } extern void bar(); //bar is now your "naked" function 

在windows下使用nasm和gcc编译

我猜你想要一个函数的语法,而不需要C编译器做任何事情。 你可以通过链接到汇编函数来实现这一点(然后它需要将正确的东西复制到堆栈中)。

您的汇编例程只需要C编译器调用的入口点,例如:

程序集部分将包含以下标头:

 global my_function my_function: push r1 push r2 ; Code ret 

对应于:

 void my_function ( int arg1, char arg2 );