强制 GCC 在调用函数之前将参数压入堆栈(使用 PUSH 指令) [英] Force GCC to push arguments on the stack before calling function (using PUSH instruction)

查看:42
本文介绍了强制 GCC 在调用函数之前将参数压入堆栈(使用 PUSH 指令)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have started developing a small 16-bit OS under GCC/G++. I am using a GCC cross-compiler, which I compiled under Cygwin, I am putting asm(".code16gcc\n") as the first line of each .CPP file, using Intel ASM syntax and the command lines for compiling and linking a .CPP file look like this:

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)

我目前面临的问题是 GCC 将函数调用代码转换为汇编代码的方式.

The problem I am currently facing is the way GCC translates function-calling code into assembly.

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

To be more specific, instead of using the PUSH instruction to pass the arguments, GCC "calculates" the offsets relative to ESP the arguments should be located at, and then uses the MOV instruction to write the stack manually.

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

This is not beneficial for me, since I rely on the PUSH instruction in my assembly code. To illustrate my problem clearer, take these 2 functions:

void f2(int x);

void f1(){
    int arg = 8;
    asm("mov eax, 5");
    asm("push eax");
    f2(arg);
    asm("pop eax");
}
void f2(int x){
}

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

In function f1, I am saving EAX using the PUSH instruction, and I would expect to have it restored to 5 after calling f2 and executing the "POP EAX" instruction. It turns out however that EAX becomes 8, not 5. That's because the ASSEMBLY CODE GCC generates looks like this (I've included the source as well for clarity):

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

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

I have tried using some G++ compilation flags like -mpush-args or -mno-push-args and another one which I can't remember and GCC still doesn't want to use PUSH. The version I'm using is i586-elf-g++ (GCC) 4.7.2 (Cross-Compiler recompiled in Cygwin).

先谢谢你!

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

UPDATE: Here's a webpage I've found: http://fixunix.com/linux/6799-gcc-function-call-pass-arguments-via-push.html

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

That just seems really stupid for GCC to do, considering that it limits the usability of inline assembly for complex stuff. :( Please leave an answer if you have a suggestion.

推荐答案

我很幸运地找到了这个问题的解决方案,但它最终达到了我想要的效果.以下是 4.7.2 版的 GCC 手册的说明:

I've been very lucky finding a solution to this problem, but it finally does what I want it to do. Here's what the GCC manual for version 4.7.2 state:

-mpush-args
-mno-push-args
Use PUSH operations to store outgoing parameters. This method is shorter
and usually equally fast as method using SUB/MOV operations and is enabled
by default. In some cases disabling it may improve performance because of
improved scheduling and reduced dependencies.

-maccumulate-outgoing-args
If enabled, the maximum amount of space required for outgoing arguments will
be computed in the function prologue. This is faster on most modern CPUs
because of reduced dependencies, improved scheduling and reduced stack usage
when preferred stack boundary is not equal to 2. The drawback is a notable
increase in code size. This switch implies ‘-mno-push-args’.

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

I'm saying I am lucky because -mpush-args does not work, what works is instead "-mno-accumulate-outgoing-args", which is not even documented!

这篇关于强制 GCC 在调用函数之前将参数压入堆栈(使用 PUSH 指令)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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