强制 GCC 在调用函数之前将参数压入堆栈(使用 PUSH 指令)

Cor*_*uzu 1 x86 gcc arguments inline-assembly stack-memory

我已经开始在 GCC/G++ 下开发一个小型 16 位操作系统。我正在使用 GCC 交叉编译器,它是在 Cygwin 下编译的,我将 asm(".code16gcc\n") 作为每个 .CPP 文件的第一行,使用 Intel ASM 语法和命令行来编译和链接.CPP 文件如下所示:

G++: i586-elf-g++ -c $(CPP_FILE) -o $(OBJECT_OUTPUT) -nostdinc -ffreestanding -nostdlib -fno-builtin -fno-rtti -fno-exceptions -fpermissive -masm=intel

LD: i586-elf-ld -T $(LD_SCRIPT) $(OBJECT_OUTPUT) -o $(BINARY_OUTPUT)
Run Code Online (Sandbox Code Playgroud)

我目前面临的问题是GCC 将函数调用代码翻译成汇编的方式。

更具体地说,GCC 不使用 PUSH 指令来传递参数,而是“计算”参数相对于 ESP 的偏移量,然后使用 MOV 指令手动写入堆栈。

这对我来说没有好处,因为我依赖汇编代码中的 PUSH 指令。为了更清楚地说明我的问题,请使用以下两个函数:

void f2(int x);

void f1(){
    int arg = 8;
    asm("mov eax, 5");  // note: super hacky unsafe use of GNU C inline asm
    asm("push eax");    // Writing registers without declaring a clobber is UB
    f2(arg);
    asm("pop eax");
}
void f2(int x){
}
Run Code Online (Sandbox Code Playgroud)

在函数 f1 中,我使用 PUSH 指令保存 EAX,并且我希望在调用 f2 并执行“POP EAX”指令后将其恢复为 5。然而,事实证明 EAX 变成了 8,而不是 5。这是因为 GCC 生成的汇编代码看起来像这样(为了清楚起见,我也包含了源代码):

void f1()
C++: {
    push ebp
    mov ebp,esp
    sub esp,byte +0x14

    C++: int arg = 8;
        mov dword [ebp-0x4],0x8
    
    C++: asm("mov eax, 5");
        mov eax,0x5
    
    C++: asm("push eax");
        push eax
    
    C++: f2(arg);
        mov eax,[ebp-0x4]
        mov [dword esp],eax =======>>>>>> HERE'S THE PROBLEM, WHY NOT 'PUSH EAX' ?!!
        call f2
        
    C++: asm("pop eax");
        pop eax
    
C++: }
    o32 leave
    o32 ret
    
void f2(int x)
C++: {
    push ebp
    mov ebp,esp
C++: }
    pop ebp
    o32 ret
Run Code Online (Sandbox Code Playgroud)

我尝试过使用一些 G++ 编译标志,例如 -mpush-args 或 -mno-push-args 以及另一个我不记得的标志,并且 GCC 仍然不想使用 PUSH。我使用的版本是i586-elf-g++ (GCC) 4.7.2(在Cygwin中重新编译的交叉编译器)。

先感谢您!

更新:这是我找到的一个网页:http://fixunix.com/linux/6799-gcc-function-call-pass-arguments-via-push.html

考虑到它限制了内联汇编对于复杂内容的可用性,这对于 GCC 来说似乎真的很愚蠢。:( 如果您有建议,请留下答案。

Cor*_*uzu 5

我很幸运找到了这个问题的解决方案,但它最终实现了我想要的功能。\n这是 4.7.2 版本的 GCC 手册的说明:

\n\n
-mpush-args\n-mno-push-args\nUse PUSH operations to store outgoing parameters. This method is shorter\nand usually equally fast as method using SUB/MOV operations and is enabled\nby default. In some cases disabling it may improve performance because of\nimproved scheduling and reduced dependencies.\n\n-maccumulate-outgoing-args\nIf enabled, the maximum amount of space required for outgoing arguments will\nbe computed in the function prologue. This is faster on most modern CPUs\nbecause of reduced dependencies, improved scheduling and reduced stack usage\nwhen preferred stack boundary is not equal to 2. The drawback is a notable\nincrease in code size. This switch implies \xe2\x80\x98-mno-push-args\xe2\x80\x99.\n
Run Code Online (Sandbox Code Playgroud)\n\n

我说我很幸运,因为 -mpush-args 不起作用,有效的是“-mno-accumulate-outgoing-args”,它甚至没有记录!

\n