内联装配破坏了红色区域

Mik*_*urg 20 c x86 gcc inline-assembly red-zone

我正在编写一个加密程序,并且核心(一个宽的乘法例程)是用x86-64汇编编写的,两者都是为了速度而且因为它广泛使用adc那些不容易从C中访问的指令.我不想内联这个函数,因为它很大,并且在内循环中被调用了好几次.

理想情况下,我还想为此函数定义一个自定义调用约定,因为它在内部使用所有寄存器(除外rsp),不破坏其参数,并在寄存器中返回.现在,它适应了C调用约定,但当然这使它变慢(大约10%).

为了避免这种情况,我可以调用它,asm("call %Pn" : ... : my_function... : "cc", all the registers);但有没有办法告诉GCC调用指令与堆栈混淆?否则GCC会将所有这些寄存器放在红色区域中,而顶部的寄存器将被破坏.我可以使用-mno-red-zone编译整个模块,但是我更喜欢告诉GCC,比方说,红色区域的前8个字节将被破坏,以便它不会放任何东西.

Ben*_*son 5

从你原来的问题我没有意识到gcc限制红区使用到叶子功能.我不认为这是x86_64 ABI所要求的,但它是编译器的合理简化假设.在这种情况下,您只需要将调用汇编例程的函数设置为非叶子以进行编译:

int global;

was_leaf()
{
    if (global) other();
}
Run Code Online (Sandbox Code Playgroud)

GCC无法判断是否global为真,因此无法优化调用,other()因此was_leaf()不再是叶函数.我编译了这个(用更多代码触发堆栈使用)并观察到它作为一个叶子它没有移动,%rsp并且显示它做了修改.

我还尝试char buf[150]在一个叶子中简单地分配超过128个字节(只是),但我很震惊地看到它只进行了部分减法:

    pushq   %rbp
    movq    %rsp, %rbp
    subq    $40, %rsp
    movb    $7, -155(%rbp)
Run Code Online (Sandbox Code Playgroud)

如果我把失败的代码放回去了 subq $160, %rsp

  • 有`__attribute __(leaf)`但不幸的是没有像`__attribute __(nonleaf)那样的东西. (3认同)

Mat*_*ard 0

不确定,但查看GCC 函数属性文档,我发现了stdcall可能感兴趣的函数属性。

我仍然想知道你发现你的 asm 调用版本有什么问题。如果只是为了美观,您可以将其转换为宏或内联函数。

  • “call”指令将当前指令指针压入堆栈。如果堆栈下没有任何内容(在“红色区域”中),这很好,但在 x86-64 上,ABI 允许编译器将内容放入叶函数中,即那些不调用任何内容的函数。但是,GCC 不会将此“call”视为调用,因为它隐藏在内联汇编中。因此它可能会在红色区域中放置一些东西,并且它会被调用破坏。这不仅仅是理论上的可能性,它确实发生了,并且实际上导致了我的代码中的错误。另外,stdcall 不会这样做。 (3认同)