我正在用 C++ 编写一些 16 位(双关语)代码,用 G++ 编译它。更多关于我在这里编译的上下文:强制 GCC 在调用函数之前将参数推送到堆栈上(使用 PUSH 指令)
我现在面临的问题是尝试链接我的目标文件时抛出的错误 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 位情况下推送一个字。这意味着,如果 .rodata 的地址是 - 比如说 - 0x70DAA,那么指令将是“推送 0x70DAA”。这就是链接器抛出错误的原因:
在函数main': relocation truncated to fit: R_386_16 against
.rodata'
-- 因为链接器知道 0x70DAA 不适合一个字。解决问题的方法是要求 GCC 在推送它们之前将参数移动到寄存器中。就像是:
/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 中使用它们 - 并且不能对任何函数执行此操作。您可以通过简短的 google 搜索了解更多信息,如果需要,我将在此处准确发布此选项的作用以及为什么它不会真正起作用,但这个问题帖子开始变得太长。
简而言之,我的问题是:我可以要求 GCC 将传递给函数的参数移动到寄存器中,然后再推送它们吗?
提前致谢!