私は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
このアプリケーションは、私が開発しているOSの一部です。具体的には、ブートローダーはこのコードをBIOSメモリのアドレス0x70D00にロードします。これにより、.rodataのアドレスは0x70D00より大きくなります。GCCには純粋な16ビットコードのサポートが組み込まれていないため、「push OFFSET FLAT:.LC0」を実行すると、純粋な16ビット環境でWORDがプッシュされることを意味することはわかりません。つまり、.rodataのアドレスが-たとえば-0x70DAAの場合、命令は'push0x70DAA'になります。これが、リンカがエラーをスローする理由です。
関数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に同じことを強制する方法があるかどうか疑問に思っていました...明らかに機能する1つの代替方法は、属性((regparm(N)))を関数fに関連付けることです。しかし、これは実際には良い選択肢ではありません。なぜなら、レジスタをfで直接使用するのではなく、スタック上のレジスタを実際にプッシュするわけではなく、どの関数に対してもこれを実行できないからです。これについては、短いグーグル検索を行うことで詳しく知ることができます。必要に応じて、このオプションの機能と実際に機能しない理由を正確に投稿しますが、この質問投稿は長くなりすぎます。
要するに、私の質問は次のとおりです。GCCに、関数をプッシュする前に、レジスタ内の関数に渡された引数をMOVするように依頼できますか?
前もって感謝します!