3

ソース ファイルをインライン アセンブリでコンパイルおよびリンクすると、リンク エラーが発生します。

テストファイルは次のとおりです。

via:$ cat test.cxx
extern int libtest();
int main(int argc, char* argv[])
{
    return libtest();
}

$ cat lib.cxx
#include <stdint.h>
int libtest()
{
    uint32_t rnds_00_15;    
    __asm__ __volatile__
    (
        ".intel_syntax noprefix         ;\n\t"
        "mov DWORD PTR [rnds_00_15], 1  ;\n\t"
        "cmp DWORD PTR [rnds_00_15], 1  ;\n\t"
        "je  done                       ;\n\t"
        "done:                          ;\n\t"
        ".att_syntax noprefix           ;\n\t"
        :
        : [rnds_00_15] "m" (rnds_00_15)
        : "memory", "cc"
    );

    return 0;
}

プログラムをコンパイルしてリンクすると、次のようになります。

via:$ g++ -fPIC test.cxx lib.cxx -c
via:$ g++ -fPIC lib.o test.o -o test.exe
lib.o: In function `libtest()':
lib.cxx:(.text+0x1d): undefined reference to `rnds_00_15'
lib.cxx:(.text+0x27): undefined reference to `rnds_00_15'
collect2: error: ld returned 1 exit status

実際のプログラムはもっと複雑です。ルーチンのレジスタが不足しているため、フラグrnds_00_15はメモリ オペランドでなければなりません。の使用はrnds_00_15、asm ブロックに対してローカルです。C コードで宣言されているのは、メモリがスタック上に割り当てられていることを確認するためです。C コードに関する限り、読み取りも書き込みも行いません。これをメモリ入力としてリストするので、GCC はそれを使用していることを認識し、拡張 ASM で「C 変数名」を関連付けます。

リンク エラーが表示される理由とその修正方法を教えてください。

4

1 に答える 1

6

コンパイルしgcc -masm=intel、asm テンプレート文字列内でモードを切り替えようとしないでください。私の知る限り、clang に相当するものはありません (MacOS はデフォルトでclang をgcc/としてインストールすることに注意してください)。g++

また、もちろん、有効な GNU C インライン asm を使用する必要があります。オペランドを使用して、読み書きする C オブジェクトをコンパイラに伝えます。


Intel の構文でパーセント記号が使用されているとは思えません。おそらく私は何かを逃していますか?

%operandExtended-Asm テンプレート (単一 %の を使用)への置換と、アセンブラーが認識する最終的な asm の間で混乱しています。

最後の asm で%%リテラルを使用する必要があります。%Intel-syntax inline asm では使用"mov %%eax, 1"しませんが、それでも"mov %0, 1"orを使用します%[named_operand]

https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.htmlを参照してください。Basic asm (オペランドなし) では置換はなく、% はテンプレートで特別なものではないため、何らかの理由で orのようなオペランドを使用していない場合はmov $1, %eax、Basic asm と Extended で記述します。mov $1, %%eaxmov $1, %[tmp]mov $1, %0


uint32_t rnds_00_15;自動ストレージ付きのローカルです。もちろん、その名前の asm シンボルはありません。

を使用%[rnds_00_15]してコンパイルします-masm=intel (最後に を削除し.att_syntaxます。これにより、後に続くコンパイラ生成 asm が壊れます)。

また、DWORD PTRオペランド展開には既に含まれているため、を削除する必要があります。(GASは問題なく受け入れますが、2番目のものが優先されるため、無意味で誤解を招く可能性があります。)DWORD PTR [rsp - 4]DWORD PTR DWORD PTR [rsp - 4]

また"=m"、コンパイラにスタック上のスクラッチ スペースを確保させたい場合は、出力オペランドが必要になります。C で使用されていない場合でも、入力専用オペランドを変更してはなりません。おそらく、コンパイラは、書き込まれておらず、初期化されていない (つまり UB) ため、他の何かとオーバーラップする可能性があると判断します。(あなたの"memory"クロバーが安全かどうかはわかりませんが、ここで早期クロバー出力オペランドを使用しない理由はありません。)

%=また、 を使用して一意の番号を取得することで、ラベル名の競合を回避する必要があります。

Godboltコンパイラエクスプローラー-masm=intelドロップダウンのオプションに応じて使用)での作業例(GCCとICC、ただし残念ながらclangではありません)。「バイナリ モード」(11010 ボタン) を使用して、警告なしで asm にコンパイルした後に実際にアセンブルされることを証明できます。

int libtest_intel()
{
    uint32_t rnds_00_15;
    // Intel syntax operand-size can only be overridden with operand modifiers
    // because the expansion includes an explicit DWORD PTR
    __asm__ __volatile__
    (  // ".intel_syntax noprefix \n\t"
        "mov %[rnds_00_15], 1  \n\t"
        "cmp %[rnds_00_15], 1  \n\t"
        "je  .Ldone%=                 \n\t"
        ".Ldone%=:                    \n\t"
        : [rnds_00_15] "=&m" (rnds_00_15)
        :
        : // no clobbers
    );
    return 0;
}

gcc -O3 -masm=intelこの asm に( を使用して) コンパイルします。gcc -m32 -masm=intelもちろん、次のものでも動作します。

libtest_intel:
    mov DWORD PTR [rsp-4], 1  
    cmp DWORD PTR [rsp-4], 1  
    je  .Ldone8                 
.Ldone8:                    

    xor     eax, eax
    ret

これをclangで動作させることができませんでした:.intel_syntax noprefix明示的に残したときに窒息しました。


オペランド サイズのオーバーライド:

dword 入力オペランドの下位バイトのみにアクセスする%b[tmp]ようにコンパイラに代入させるために使用する必要があります。BYTE PTR [rsp-4]これをやりたい場合は、AT&T 構文をお勧めします。



%[rnds_00_15]結果の使用Error: junk '(%ebp)' after expression.

これは、コンパイラに通知せずに Intel 構文に切り替えたためです。Intel アドレッシング モードを使用する場合は、 でコンパイルし-masm=intelて、コンパイラが正しい構文でテンプレートに代入できるようにします。

これが、私がそのくだらない GCC インライン アセンブリをほぼすべての犠牲を払って回避する理由です。男私はこのくだらないツールを軽蔑します。

使い方が間違っているだけです。少し面倒ですが、設計方法を理解していれば理にかなっており、ほとんどの場合うまく機能します。

繰り返しますが、コンパイラは asm 文字列をまったく解析しませんが、%operand . .intel_syntax noprefexこれが、 AT&T 構文に気付かず、代用し続ける理由です。

ただし、メモリ オペランドのオペランド サイズをオーバーライドしたり、オフセットを追加したりする場合など、AT&T 構文を使用すると、より適切かつ簡単に機能します。(たとえば4 + %[mem]、AT&T 構文で動作します)。


方言の代替:

依存しないインライン asm を書きたい場合-masm=intelは、Dialect の代替手段を使用します (コードが非常に醜くなります。1 つまたは 2 つの命令をラップする以外にはお勧めしません)。

オペランド サイズのオーバーライドも示します

#include <stdint.h>
int libtest_override_operand_size()
{
    uint32_t rnds_00_15;
    // Intel syntax operand-size can only be overriden with operand modifiers
    // because the expansion includes an explicit DWORD PTR
    __asm__ __volatile__
    (
        "{movl $1, %[rnds_00_15] | mov %[rnds_00_15], 1}  \n\t"
        "{cmpl $1, %[rnds_00_15] | cmp %k[rnds_00_15], 1}  \n\t"
        "{cmpw $1, %[rnds_00_15] | cmp %w[rnds_00_15], 1}  \n\t"
        "{cmpb $1, %[rnds_00_15] | cmp %b[rnds_00_15], 1}  \n\t"
        "je  .Ldone%=                     \n\t"
        ".Ldone%=:                        \n\t"
        : [rnds_00_15] "=&m" (rnds_00_15)
    );
    return 0;
}

Intel 構文では、gcc は次のようにコンパイルします。

     mov DWORD PTR [rsp-4], 1  
     cmp DWORD PTR [rsp-4], 1  
     cmp WORD PTR [rsp-4], 1  
     cmp BYTE PTR [rsp-4], 1  
    je  .Ldone38                     
.Ldone38:                        

    xor     eax, eax
    ret

AT&T 構文を使用すると、次のようにコンパイルされます。

    movl $1, -4(%rsp)   
    cmpl $1, -4(%rsp)   
    cmpw $1, -4(%rsp)   
    cmpb $1, -4(%rsp)   
    je  .Ldone38                     
.Ldone38:                        

    xorl    %eax, %eax
    ret
于 2018-08-18T05:40:51.177 に答える