在没有编译器生成 prologue/epilogue & RET 指令的情况下创建 C 函数?
Creating a C function without compiler generated prologue/epilogue & RET instruction?
考虑这个函数:
void foo(){
//do something
}
在汇编中它看起来像这样(不准确):
push something
;do stuff
pop something
ret
但我不想要这个生成的代码 (RET, PUSH, POP ...)。我只想要一个代码块的标签,所以我必须 return 自己:
void bar(){
//do something
asm("iret") //i want to use this function as a ISR
}
在汇编中它看起来像这样:
; do something
iret
没有 PUSH、POP 或 RET。是否有任何预处理器指令或关键字可以让我完成此操作?
我正在使用 GCC,以及 Windows 下的 NASM,我正在尝试生成我的自己的中断服务例程 (ISR)。
我猜你想要的语法是在 C 编译器不做任何事情的情况下拥有一个函数。你可以通过链接到你的汇编函数来做到这一点,(然后它当然需要将正确的东西复制到堆栈上)。
您的汇编例程只需要一个入口点供 C 编译器调用,例如:
程序集部分将包含以下内容header:
global my_function
my_function:
push r1
push r2
; Code
ret
对应于:
void my_function ( int arg1, char arg2 );
我找到了一个巧妙的解决方法:
在汇编中定义函数但调用外部 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
使用 nasm 和 windows
下的 gcc 编译
还不完全清楚你想要完成什么。看起来你想要一个中断处理程序来默认执行 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;
}
__asm__
GCC 中的基本 __asm__
语句可以放在函数之外。我们为我们的中断服务例程 (ISR) 定义标签,并使用 .globl
使它们在外部可见(您可能不需要全局可见性,但无论如何我都会显示它)。
我创建了几个示例中断服务例程。一个只做 iret
,另一个对 C 处理程序进行函数调用。我们保存所有寄存器并在之后恢复它们。 C 函数要求将方向标志设置为正向,因此我们需要 CLD before calling the C function. This sample code works for 32-bit targets. 64-bit can be done by saving the registers individually rather than using PUSHA and POPA.
注意:如果在Windows上使用GCC,函数名inside 装配块可能需要在前面加上 _
(下划线)。它看起来像:
/* 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 属性。他们将此属性描述为:
The naked storage-class attribute is a Microsoft-specific extension to the C language. For functions declared with the naked storage-class attribute, the compiler generates code without prolog and epilog code. You can use this feature to write your own prolog/epilog code sequences using inline assembler code. Naked functions are particularly useful in writing virtual device drivers.
一个示例中断服务例程可以这样完成:
__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 目标上得到支持:
interrupt
Use this attribute to indicate that the specified function is an interrupt handler or an exception handler (depending on parameters passed to the function, explained further). The compiler generates function entry and exit sequences suitable for use in an interrupt handler when this attribute is present. The IRET instruction, instead of the RET instruction, is used to return from interrupt handlers. All registers, except for the EFLAGS register which is restored by the IRET instruction, are preserved by the compiler. Since GCC doesn’t preserve MPX, SSE, MMX nor x87 states, the GCC option -mgeneral-regs-only should be used to compile interrupt and exception handlers.
这个方法还是有不足的地方。如果您希望 C 代码访问中断时出现的寄存器内容,目前没有 可靠的 方法用这个机制来做。如果您正在编写软件中断并且需要访问寄存器以确定要采取的操作(即:int 0x80
on Linux),这将很方便。另一个例子是允许中断将所有寄存器内容转储到显示器以进行调试。
考虑这个函数:
void foo(){
//do something
}
在汇编中它看起来像这样(不准确):
push something
;do stuff
pop something
ret
但我不想要这个生成的代码 (RET, PUSH, POP ...)。我只想要一个代码块的标签,所以我必须 return 自己:
void bar(){
//do something
asm("iret") //i want to use this function as a ISR
}
在汇编中它看起来像这样:
; do something
iret
没有 PUSH、POP 或 RET。是否有任何预处理器指令或关键字可以让我完成此操作?
我正在使用 GCC,以及 Windows 下的 NASM,我正在尝试生成我的自己的中断服务例程 (ISR)。
我猜你想要的语法是在 C 编译器不做任何事情的情况下拥有一个函数。你可以通过链接到你的汇编函数来做到这一点,(然后它当然需要将正确的东西复制到堆栈上)。
您的汇编例程只需要一个入口点供 C 编译器调用,例如:
程序集部分将包含以下内容header:
global my_function
my_function:
push r1
push r2
; Code
ret
对应于:
void my_function ( int arg1, char arg2 );
我找到了一个巧妙的解决方法:
在汇编中定义函数但调用外部 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
使用 nasm 和 windows
下的 gcc 编译还不完全清楚你想要完成什么。看起来你想要一个中断处理程序来默认执行 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;
}
__asm__
GCC 中的基本 __asm__
语句可以放在函数之外。我们为我们的中断服务例程 (ISR) 定义标签,并使用 .globl
使它们在外部可见(您可能不需要全局可见性,但无论如何我都会显示它)。
我创建了几个示例中断服务例程。一个只做 iret
,另一个对 C 处理程序进行函数调用。我们保存所有寄存器并在之后恢复它们。 C 函数要求将方向标志设置为正向,因此我们需要 CLD before calling the C function. This sample code works for 32-bit targets. 64-bit can be done by saving the registers individually rather than using PUSHA and POPA.
注意:如果在Windows上使用GCC,函数名inside 装配块可能需要在前面加上 _
(下划线)。它看起来像:
/* 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 属性。他们将此属性描述为:
The naked storage-class attribute is a Microsoft-specific extension to the C language. For functions declared with the naked storage-class attribute, the compiler generates code without prolog and epilog code. You can use this feature to write your own prolog/epilog code sequences using inline assembler code. Naked functions are particularly useful in writing virtual device drivers.
一个示例中断服务例程可以这样完成:
__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 目标上得到支持:
interrupt
Use this attribute to indicate that the specified function is an interrupt handler or an exception handler (depending on parameters passed to the function, explained further). The compiler generates function entry and exit sequences suitable for use in an interrupt handler when this attribute is present. The IRET instruction, instead of the RET instruction, is used to return from interrupt handlers. All registers, except for the EFLAGS register which is restored by the IRET instruction, are preserved by the compiler. Since GCC doesn’t preserve MPX, SSE, MMX nor x87 states, the GCC option -mgeneral-regs-only should be used to compile interrupt and exception handlers.
这个方法还是有不足的地方。如果您希望 C 代码访问中断时出现的寄存器内容,目前没有 可靠的 方法用这个机制来做。如果您正在编写软件中断并且需要访问寄存器以确定要采取的操作(即:int 0x80
on Linux),这将很方便。另一个例子是允许中断将所有寄存器内容转储到显示器以进行调试。