在推送它们之前,请向GCC询问MOV中的MOV参数

时间:2012-11-29 18:59:54

标签: gcc push parameter-passing calling-convention 16-bit

我在C ++中编写了一些16位(双关语)代码,用G ++编译它。更多关于我在这里编译的上下文:Force GCC to push arguments on the stack before calling function (using PUSH instruction)

我现在面临的问题是关于在尝试链接我的目标文件时LD抛出的错误。具体来说,这是一个代码情况:

asm(".code16gcc\n");
void f(const char*);
int main(){
    f("A constant string put in section .rodata at link-time");
}
void f(const char* s){ }

在汇编代码中,使用-S和-mno-accumulate-outgoing-args选项G ++会将其转换为(仅写入程序集的相关部分):

/APP
    .code16gcc

    .section    .rodata
.LC0:
    .string "A constant string put in section .rodata at link-time"
main:
.LFB0:
    /* here would be main's prologue, not put because it ain't relevant */

    // THIS IS THE CALL f("A constant string put in section .rodata at link-time");
    push    OFFSET FLAT:.LC0
    call    _Z1fPKc

此应用程序是我正在开发的操作系统的一部分。具体来说,引导加载程序将此代码加载到BIOS内存中的地址0x70D00处。这使.rodata的地址大于0x70D00。由于GCC没有对纯16位代码的内置支持,因此它不知道执行'push OFFSET FLAT:.LC0'意味着在纯粹的16位情况下推送WORD。这意味着,如果.rodata的地址是 - 比如说 - 0x70DAA,则指令将是“推送0x70DAA”。这就是链接器抛出错误的原因:

在函数main': relocation truncated to fit: R_386_16 against .rodata'

- 因为链接器知道0x70DAA不适合用词。什么可以解决这个问题是要求GC​​C在推动它们之前MOV注册参数。类似的东西:

/APP
    .code16gcc

    .section    .rodata
.LC0:
    .string "A constant string put in section .rodata at link-time"
main:
.LFB0:
    /* here would be main's prologue, not put because it ain't relevant */

    // THIS IS THE CALL f("A constant string put in section .rodata at link-time"); , now using EAX before pushing the string literal's offset in .rodata
    mov eax, OFFSET FLAT:.LC0 // move in eax instead
    push    eax // and push eax!
    call    _Z1fPKc

这是MSVC在某些情况下优化的功能。我想知道是否有办法迫使GCC做同样的事情......显然可行的另一种方法是将属性((regparm(N)))与函数f联系起来。但这并不是一个好的选择,因为它不会真正推动堆栈中的寄存器,而不是直接在f中使用它们 - 并且不能为任何函数执行此操作。你可以通过做一个简短的谷歌搜索找到更多相关信息,如果需要,我会在这里发布这个选项的确切内容以及它为什么不能真正起作用,但这个问题的帖子开始变得太长了。

简而言之,我的问题是: 我可以向GCC要求MOV在推送它们之前传递给寄存器中的函数的参数吗?

提前致谢!

1 个答案:

答案 0 :(得分:0)

我已经考虑过解决这个问题的方法,尽管我更喜欢使用MOV-to-REG-and-PUSH排序方法。我想到的是,这只发生在编译器可以在编译时计算的地址,比如放在.rodata中的字符串的地址。

知道了,我在main中创建了一个局部变量,并将其用作传递的参数,如下所示:

asm(".code16gcc\n");
void f(const char*);
int main(){
    const char* s = "A constant string put in section .rodata at link-time";
    // Now use 's' as the argument instead of the string literal
    f(s);
}
void f(const char* s){ }

这有效地将生成的汇编代码更改为:

/APP
    .code16gcc

    .section    .rodata
.LC0:
    .string "A constant string put in section .rodata at link-time"
main:
.LFB0:
    /* here would be main's prologue, not put because it ain't relevant */

    // THIS IS THE CALL f(s);
    mov DWORD PTR [ebp-12], OFFSET FLAT:.LC0 // now specifically loaded in the DWORD 's'
    sub esp, 12
    push DWORD PTR [ebp-12]
    call    _Z1fPKc

可以看出,现在使用局部变量,字符串文字的地址(在.rodata中)专门在DWORD中传输。这有效地避免了链接器错误,尽管它使用了一些可忽略的额外堆栈空间。