0

私は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するように依頼できますか?

前もって感謝します!

4

1 に答える 1

0

私はこの問題の回避策を考えましたが、MOVからREGおよび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で具体的に転送されます。これにより、ごくわずかな余分なスタックスペースが使用されますが、リンカーエラーが効果的に回避されます。

于 2012-11-29T22:55:37.153 に答える