我在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不适合用词。什么可以解决这个问题是要求GCC在推动它们之前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在推送它们之前传递给寄存器中的函数的参数吗?
提前致谢!
答案 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中传输。这有效地避免了链接器错误,尽管它使用了一些可忽略的额外堆栈空间。