简体   繁体   English

内联汇编,x86 中的红色区域?

[英]inline assembly, red zone in x86?

红区堆栈图Does the red zone exist in x86 ? x86 中是否存在红色区域? And even if not, can you explain to me by AMD64 ?即使没有,你能用 AMD64 向我解释一下吗?

Where is the red zone ?红区在哪里? WIKI: "the red zone is a fixed-size area in a function's stack frame below the current stack pointer" "current stack pointer": the meaning this area (128 bytes if i understand) always below of the updated stack pointer no distance ? WIKI:“红色区域是函数堆栈帧中当前堆栈指针下方的固定大小区域”“当前堆栈指针”:意思是这个区域(如果我理解的话是 128 字节)总是在更新的堆栈指​​针下方没有距离? or we have from the start some distance between, and this distance get closer and closer when i sub the esp ?或者我们从一开始就有一段距离,当我加入 esp 时,这个距离会越来越近?

Should I give importance to the red zone ?我应该重视红色区域吗? always when i sub the esp ?总是当我分 esp 时? like in push or call function or create locals (caller callee) ?像推送或调用函数或创建本地人(调用者被调用者)?

If I create local variables I will get to the red zone ?如果我创建局部变量,我会到达红色区域吗? and is it dangerous ?它危险吗? (leaf/not leaf functions) (叶/非叶函数)

Does it matter if I call a leaf/not leaf function ?如果我调用叶子/非叶子函数有关系吗?

To avoid this area should I do: sub esp, 128 ?为了避免这个区域,我应该这样做:sub esp, 128 ? and if yes wen ?如果是,文? before each sub the esp ?在每个子 esp 之前?

#include <stdio.h>

int main() {
    int x = 5, y, z;
    // push
    __asm {
        sub esp, 4 // allocate cell on the stack (allocate static memory) 
        // we cant put 2 addresses on the address bus  (we cant do: mov memory, memory)
        mov eax, dword ptr [x] // save value of x on eax 
        mov [esp], eax         // insert to the allocated cell value of eax (5 from x) 
    }
    // peek
    __asm {
        mov eax, [esp] // the value of what is at the top of the stack
        mov dword ptr [y], eax // copy to y
    }
    printf("y = %d\n", y);
    // pop 
    __asm {
        mov eax, [esp] // the value of what is at the top of the stack
        mov dword ptr[z], eax // copy to z
        add esp, 4 // deallocate cell on the stack (deallocate static memory) 
    }
    printf("z = %d", z);
    return 0;
}

or like this:或像这样:

#include <stdio.h>

void f(int x) {
    printf("%d\n", x);
}

void g() {
    int x = 5;
    f(x);
}

int main() {
    __asm { 
        mov eax, 3
        push eax
        call f 
        add esp, 4
    }
    __asm { call g }
    return 0;
}

No 32-bit x86 calling conventions use a red-zone, and MSVC can't target the x86-64 convention that does (AMD64 System V, used on all non-Windows systems).没有 32 位 x86 调用约定使用红色区域,并且 MSVC 不能针对使用的 x86-64 约定(AMD64 System V,用于所有非 Windows 系统)。

Since you use push eax (and stack args at all), your code can only compile for 32-bit mode, so you don't need to worry about a red-zone.由于您使用push eax (和堆栈参数),您的代码只能编译为 32 位模式,因此您无需担心红区。 That's true even if you were compiling with clang -fasm-blocks eg for Linux to allow asm{} syntax, instead of with MSVC or clang-cl for Windows.即使您使用clang -fasm-blocks进行编译也是如此,例如在 Linux 上允许asm{}语法,而不是在 Windows 上使用 MSVC 或clang-cl


If you were writing different code with x86-64 64-bit registers, for a non-Windows OS with clang -fasm-blocks or with normal GNU C inline asm (like asm("instructions" : "+r"(x) : "r"(y) : "rax") ), then you would need to worry about calling functions (or using the stack at all) from inside inline asm.如果您使用 x86-64 64 位寄存器编写不同的代码,对于具有clang -fasm-blocks或普通 GNU C 内联 asm 的非 Windows 操作系统(如asm("instructions" : "+r"(x) : "r"(y) : "rax") ),那么您需要担心从内联 asm 内部调用函数(或根本使用堆栈)。

For that, see Calling printf in extended inline ASM for how to skip past the red-zone before using the stack, and how to declare clobbers on all the call-preserved registers.为此,请参阅在扩展内联 ASM 中调用 printf,了解如何在使用堆栈之前跳过红色区域,以及如何在所有保留调用的寄存器上声明 clobbers。

Unfortunately there's no way to tell GCC/clang that you want to clobber the red-zone, so unless you build that with with -mno-red-zone , you need to work around it.不幸的是,没有办法告诉 GCC/clang 你想要破坏红色区域,所以除非你用-mno-red-zone构建它,否则你需要解决它。

I believe the ownership of a red zone belongs to the currently running function.我相信红色区域的所有权属于当前正在运行的功能。 So, the red zone is there for the currently running function to use without additional stack adjustment.因此,红色区域供当前正在运行的函数使用,无需额外的堆栈调整。

Calling another function effectively gives control to red-zone memory to the callee — though the "red zone" will shift if the call operation pushes.调用另一个函数可以有效地将红区内存的控制权交给被调用者——尽管如果调用操作被推送,“红区”将会移动。 Once the callee returns to the caller, the caller gets back ownership of the red zone, but cannot expect data in the red zone to be the same: the red zone is not preserved by a call — the caller may have used some stack space below the stack pointer via pushing/decrementing or without pushing (ie the red zone).一旦被调用者返回给调用者,调用者就取回了红色区域的所有权,但不能期望红色区域中的数据相同:调用不保留红色区域 - 调用者可能已经使用了下面的一些堆栈空间通过推/减或不推(即红色区域)的堆栈指针。 (If a function wants call-preserved memory it must move the stack pointer to claim some.) (如果一个函数想要调用保留的内存,它必须移动堆栈指针来声明一些。)

If a function chooses to use the red zone, then it must manage the data it places there.如果一个函数选择使用红色区域,那么它必须管理它放置在那里的数据。 Calling another function will transfer the red zone to the callee;调用另一个函数会将红色区域转移给被调用者; the caller gives it up.来电者放弃它。

So, you can use the red zone, also push as needed, also make a function call, but you'll have to follow the rules in that as much as a callee inherited a red zone, when the callee makes a call it becomes the caller and a new callee has the red zone.因此,您可以使用红色区域,也可以根据需要推送,也可以进行函数调用,但是您必须遵守规则,因为被调用者继承了红色区域,当被调用者进行调用时,它成为呼叫者和新的被呼叫者具有红色区域。

Because the callee owns the red zone, there is no need to avoid it.因为被调用者拥有红色区域,所以没有必要避开它。 You can simply use the memory, or push (or otherwise decrement the stack pointer) to claim some non-red zone memory which automatically and by definition moves the red zone.您可以简单地使用内存,或推送(或以其他方式递减堆栈指针)来声明一些非红色区域内存,这些内存会根据定义自动移动红色区域。


From the discussion in comment below, Mgetz and PeterCordes inform us that the red zone size is guaranteed to callee's, by the ABI and allows for user mode signal handlers to be run on the same thread as currently executing — to avoid conflicts with the stack space, such user mode signal handlers will side step the red zone while sharing the same stack (not to mention preserving all register they use).从下面评论中的讨论中,Mgetz 和 PeterCordes 告诉我们,红色区域的大小是由 ABI 保证被调用者的,并允许用户模式信号处理程序与当前执行的线程在同一线程上运行 - 以避免与堆栈空间发生冲突,这样的用户模式信号处理程序将在共享相同堆栈的同时绕过红色区域(更不用说保留它们使用的所有寄存器)。


It is also possible that if you use stack space too far below the stack pointer, you may get segmentation fault or other error.如果您使用的堆栈空间在堆栈指针下方太远,也可能会出现分段错误或其他错误。 Moving the stack pointer is the proper way to inform the operating system that you want more stack space.移动堆栈指针是通知操作系统您需要更多堆栈空间的正确方法。 However, the ABI guarantees at least the red zone size of memory below the stack pointer will be accessible without such fault.但是,ABI 至少保证堆栈指针下方的红色区域大小的内存可以访问而不会出现此类错误。

@Peter Cordes even this code in 32bit will work good ? @Peter Cordes 即使是 32 位的这段代码也能很好地工作? (not worry red zone even its callee) (不用担心红色区域,甚至它的被调用者)

    #include <stdio.h>

int sum2(int num1, int num2) {
    int sum;
    printf("num1: %d\nnum2: %d\n", num1, num2);
    __asm {
        mov eax, dword ptr [num1]
        add eax, dword ptr [num2]
        mov [sum], eax
    }
    return sum;
}

int main() {
    int num1 = 2, num2 = 3, sum;
    __asm {
        mov eax, num2
        push eax
        mov ecx, num1
        push ecx
        call sum2
        mov [sum], eax
        add esp, 8
    }
    printf("sum %d ", sum);
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM