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

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

问题描述



  void foo(){
//做某事
}

在汇编中它看起来像这样(不准确):

  push something 

; do stuff

pop something
ret

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

  void bar(){
//做一些事情
asm(iret)//我想用这个函数作为ISR
}

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

 ;做点什么
iret

不用 PUSH POP RET 。是否有任何预处理器指令或关键字可以实现这一目标?我使用的是 GCC NASM 在Windows下,我试图生成自己的中断服务例程(ISR)。

解决方案

你想要完成什么。它似乎是你想要一个中断处理程序,它在默认情况下执行 iret 没有其他推送和弹出。






GCC



使用 GCC (无 NASM )是可能的:

  / *创建ISR入口点的C外部声明* / 
extern void isr_test1(void);
extern void isr_test2(void);

/ *定义一个什么也不做的ISR存根* /
__asm __(。global isr_test1 \\\

isr_test1:\\\
\t
/ *其他的东西* /
iret);

定义一个调用C函数的ISR存根* /
__asm __(。global isr_test2 \\\

isr_test2:\\\
\ t
cld\\\
\t/ *为C函数设置方向标志* /
pusha \\\
\t/ *保存所有寄存器* /
/ *其他东西* /
调用isr_test2_handler \\\
\t
popa\\\
\t/ *恢复所有寄存器* /
iret );

void isr_test2_handler(void)
{
return;

$ / code>

基本 __ asm __ 语句在 GCC 中可以放在一个函数之外。我们为我们的中断服务例程(ISR)定义了标签,并使用 .globl 来使它们在外部可见(您可能不需要全局可见性,但是我仍然可以看到它)。

我创建了几个示例中断服务例程。除了 iret 之外,另一个函数调用一个 C 处理程序。我们保存所有的寄存器并在之后恢复。 C 功能需要设置方向标志,因此我们需要一个 CLD ,然后调用 C 函数。此示例代码适用于32位目标。 64位可以通过单独保存寄存器来完成,而不是使用 PUSHA em> POPA 注意:如果在Windows上使用 GCC ,则内部的函数名称 组装块可能需要在 _ (下划线)前加上。它看起来像:

  / *使ISR入口点的C外部声明* / 
extern void isr_test1(无效);
extern void isr_test2(void);

定义一个什么也不做的ISR存根* /
__asm __(。global _isr_test1 \\\

_isr_test1:\\\
\t
/ *其他的东西* /
iret);

定义一个调用C函数的ISR存根* /
__asm __(。global _isr_test2 \\\

_isr_test2:\\\
\ t
cld\\\
\t/ *为C函数设置方向标志* /
pusha \\\
\t/ *保存所有寄存器* /
/ *其他东西* /
调用_isr_test2_handler \\\
\t
popa\\\
\t/ *恢复所有寄存器* /
iret );

void isr_test2_handler(void)
{
return;

$ / code>






MSVC / MSVC ++ < h2>

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


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

中断服务例程可以像这样完成:

  __ declspec(裸体)int isr_test(void)
{
/ *函数体* /
__asm {iret };
}

您需要处理保存和恢复寄存器,设置方向标志与上面的 GCC 例子类似。




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



在GCC 7.0+上,您现在可以使用 __属性__((中断)) 。这个属性最近才被x86和x86-64目标支持:
$ b


中断



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

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


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\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;
}

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\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'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函数生成序言/结尾&amp; RET指令?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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