创建没有编译器生成的序言/尾声的 C 函数RET指令? [英] Creating a C function without compiler generated prologue/epilogue & RET instruction?

查看:23
本文介绍了创建没有编译器生成的序言/尾声的 C 函数RET指令?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个函数:

void foo(){//做一点事}

在组装时它看起来像这样(不准确):

推送东西;做东西流行的东西退

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

void bar(){//做一点事asm("iret")//我想将此函数用作ISR}

在组装时它看起来像这样:

<代码>;做一点事愤怒

没有PUSHPOPRET.是否有任何预处理器指令或关键字可以让我完成此操作?

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

解决方案

并不完全清楚您想要完成什么.似乎您想要一个中断处理程序,它在默认情况下执行 iret 没有其他推送和弹出.

<小时>

海湾合作委员会

使用 GCC(不使用 NASM)可以做到:

/* 对 ISR 入口点进行 C extern 声明 */extern void isr_test1(void);extern void isr_test2(void);/* 定义一个什么都不做的 ISR 存根 */__asm__(".global isr_test1
""isr_test1:
	"/* 这里还有其他东西 */愤怒");/* 定义一个调用 C 函数的 ISR 存根 */__asm__(".global isr_test2
""isr_test2:
	""cld
	"/* 为 C 函数设置前进方向标志 */"pusha
	"/* 保存所有寄存器 *//* 这里还有其他东西 */"调用 isr_test2_handler
	""popa
	"/* 恢复所有寄存器 */愤怒");无效 isr_test2_handler(void){返回;}

GCC 中的基本 __asm__ 语句可以放在函数之外.我们为我们的中断服务程序 (ISR) 定义标签,并使用 .globl 使它们在外部可见(您可能不需要全局可见性,但无论如何我都会显示它).

我创建了几个示例中断服务例程.一个只执行 iret,另一个只对 C 处理程序进行函数调用.我们保存所有寄存器并在之后恢复它们.C 函数需要向前设置方向标志,因此我们需要一个 CLD 在调用 C 函数之前.此示例代码适用于 32 位目标.64 位可以通过单独保存寄存器来完成,而不是使用 PUSHAPOPA.>

注意:如果在 Windows 上使用 GCC,汇编块内部的函数名称可能需要是以 _ 开头(下划线).它看起来像:

/* 对 ISR 入口点进行 C extern 声明 */extern void isr_test1(void);extern void isr_test2(void);/* 定义一个什么都不做的 ISR 存根 */__asm__(".global _isr_test1
""_isr_test1:
	"/* 这里还有其他东西 */愤怒");/* 定义一个调用 C 函数的 ISR 存根 */__asm__(".global _isr_test2
""_isr_test2:
	""cld
	"/* 为 C 函数设置前进方向标志 */"pusha
	"/* 保存所有寄存器 *//* 这里还有其他东西 */"调用 _isr_test2_handler
	""popa
	"/* 恢复所有寄存器 */愤怒");无效 isr_test2_handler(void){返回;}

<小时>

MSVC/MSVC++

Microsoft 的 C/C++ 编译器支持 naked 函数的属性.他们将此属性描述为:

<块引用>

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

一个示例中断服务程序可以这样完成:

__declspec(naked) int isr_test(void){/* 函数体 */__asm { 愤怒 };}

您需要处理保存和恢复寄存器的问题,以与上述 GCC 示例类似的方式自己设置方向标志.

<小时>

GCC 7.x+ 在 x86/x86-64 目标上引入了中断属性

在 GCC 7.0+ 上,您现在可以使用 __attribute__((中断)) 函数.此属性最近才在 x86 和 x86-64 目标上受支持:

<块引用>

中断

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

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

Consider this function:

void foo(){
    //do something
}

In assembly it would look something like this (not accurate):

push something

;do stuff

pop something
ret

But I don't want this generated code (RET, PUSH, POP ...). I just want a label to a block of code, so I have to return myself:

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

and in assembly it would look something like this:

; do something
iret

without PUSH, POP or RET. Are there any pre-processor directives or keywords that would allow me to accomplish this?

I'm using GCC, and NASM under Windows and I am attempting to generate my own Interrupt Service Routines (ISR).

解决方案

It's not entirely clear what you want to accomplish. it seems like you want an interrupt handler that does the iret without other pushes and pops by default.


GCC

Using GCC (without NASM) something like this is possible:

/* 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
"
        "isr_test1:
	"
        /* Other stuff here */
        "iret");    

/* Define an ISR stub that makes a call to a C function */
__asm__(".global isr_test2
"
        "isr_test2:
	"
        "cld
	"                    /* Set direction flag forward for C functions */
        "pusha
	"                  /* Save all the registers */
        /* Other stuff here */
        "call isr_test2_handler
	"
        "popa
	"                   /* Restore all the registers */
        "iret");

void isr_test2_handler(void)
{
    return;
}

Basic __asm__ statements in GCC can be placed outside of a function. We define labels for our Interrupt Service Routines (ISRs) and make them externally visible with .globl (You may not need global visibility but I show it anyway).

I create a couple of sample interrupt service routines. One that does nothing more than an iret and the other that makes a function call to a C handler. We save all the registers and restore them after. C functions require the direction flag be set forward so we need a 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.

Note: If using GCC on Windows the function names inside the assembly blocks will likely need to be prepended with an _ (underscore). It would look like:

/* 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
"
        "_isr_test1:
	"
        /* Other stuff here */
        "iret");    

/* Define an ISR stub that makes a call to a C function */
__asm__(".global _isr_test2
"
        "_isr_test2:
	"
        "cld
	"                    /* Set direction flag forward for C functions */
        "pusha
	"                  /* Save all the registers */
        /* Other stuff here */
        "call _isr_test2_handler
	"
        "popa
	"                   /* Restore all the registers */
        "iret");

void isr_test2_handler(void)
{
    return;
}


MSVC/MSVC++

Microsoft's C/C++ compilers support the naked attribute on functions. They describe this attribute as:

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.

An example Interrupt Service Routine could be done like this:

__declspec(naked) int isr_test(void)
{
    /* Function body */
    __asm { iret };
}

You'll need to deal with the issues of saving and restoring registers, setting the direction flag yourself in a similar manner to the GCC example above.


GCC 7.x+ introduced Interrupt Attribute on x86/x86-64 Targets

On GCC 7.0+ you can now use __attribute__((interrupt)) on functions. This attribute was only recently supported on x86 and x86-64 targets:

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.

This method still has deficiencies. If you ever want your C code to access the contents of a register as they appeared at the time of the interrupt, there is currently no reliable way to do it with this mechanism. This would be handy if you were writing a software interrupt and needed access to the registers to determine what actions to take (ie: int 0x80 on Linux). Another example would be to allow an interrupt to dump all the register contents to the display for debug purposes.

这篇关于创建没有编译器生成的序言/尾声的 C 函数RET指令?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆