17

gcc 4.6.2 では、使用されていないと見なされるコードが関数から削除されているようです。

test.c

int main(void) {
  goto exit;
  handler:
    __asm__ __volatile__("jmp 0x0");
  exit:
  return 0;
}

の分解main()

   0x08048404 <+0>:     push   ebp
   0x08048405 <+1>:     mov    ebp,esp
   0x08048407 <+3>:     nop    # <-- This is all whats left of my jmp.
   0x08048408 <+4>:     mov    eax,0x0
   0x0804840d <+9>:     pop    ebp
   0x0804840e <+10>:    ret

コンパイラ オプション

最適化は有効になっていませんgcc -m32 -o test test.c(-m32私は 64 ビット マシンを使用しているため)。

どうすればこの動作を止めることができますか?

編集:コードを変更するのではなく、コンパイラオプションを使用することをお勧めします。

4

7 に答える 7

6

それがまさにその通りのように見えます-gcc関数内のコードに到達できないことがわかると、それを削除します。他のコンパイラは異なる場合があります。
ではgcc、コンパイルの初期段階で「制御フロー グラフ」を作成します。これは、それぞれが条件を持たず、分岐によって接続された「基本ブロック」のグラフです。実際のコードを発行するとき、ルートから到達できないグラフの部分は破棄されます。
これは最適化フェーズの一部ではないため、コンパイル オプションの影響を受けません。

したがって、どのソリューションgccでも、コードが到達可能であると考える必要があります。

私のおすすめ:

アセンブリ コードを到達できない場所 (GCC が削除する可能性がある場所) に配置する代わりに、到達可能な場所に配置して、問題のある命令をスキップできます。

int main(void) {
     goto exit;

     exit:
     __asm__ __volatile__ (
        "jmp 1f\n"
        "jmp $0x0\n"
        "1:\n"
    );
    return 0;
}

また、問題に関するこのスレッドも参照してください。

于 2012-06-13T13:54:18.693 に答える
4

これは機能しますか、gccが到達不能を認識できないようにします

int main(void)  
{ 
    volatile int y = 1;
    if (y) goto exit;
handler:
    __asm__ __volatile__("jmp 0x0");  
exit:   
    return 0; 
}
于 2012-06-19T16:20:54.543 に答える
1

gccが到達不能コードを削除するのを防ぐ方法を聞いたことがありません。何をしても、gccが到達不能コードを検出すると、常にそれを削除するようです(gccの-Wunreachable-codeオプションを使用して、到達不能と見なされるものを確認してください)。

そうは言っても、このコードを静的関数に入れることはでき、最適化されません。

static int func()
{
    __asm__ __volatile__("jmp $0x0");
}

int main(void)
{
    goto exit;

handler:
    func();

exit:
    return 0;
}

PS
このソリューションは、同じ「ハンドラー」コードブロックを元のコードの複数の場所に埋め込むときにコードの冗長性を回避したい場合に特に便利です。

于 2012-06-17T16:16:34.300 に答える
1

2012/6/18 更新

考えてみるとgoto exit、 asm ブロックに を入れることができます。つまり、コードを 1 行だけ変更する必要があります。

int main(void) {
  __asm__ ("jmp exit");

  handler:
    __asm__ __volatile__("jmp $0x0");
  exit:
  return 0;
}

これは、以下の私の他のソリューションよりも大幅にクリーンです (そして、@ugoren の現在のソリューションよりも優れている可能性があります)。


これはかなりハックですが、うまくいくようです: 通常の条件下では決して従うことのできない条件でハンドラーを非表示にしますが、コンパイラーがインラインアセンブラーで適切に分析を実行できないようにすることで、ハンドラーが削除されないようにします。

int main (void) {
  int x = 0;
  __asm__ __volatile__ ("" : "=r"(x));
  // compiler can't tell what the value of x is now, but it's always 0

  if (x) {
handler:
    __asm__ __volatile__ ("jmp $0x0");
  }

  return 0;
}

が保存されて-O3いても:jmp

    testl   %eax, %eax   
    je      .L2     
.L3:
    jmp $0x0
.L2:
    xorl    %eax, %eax 
    ret

(これは非常に危険に思えるので、これを行うためのより良い方法があることを願っています。インライン asm のトリックを行う必要がないように、作業の前にa を配置するだけで編集します。)volatilex

于 2012-06-13T13:22:27.160 に答える
0

gcc は関数内で asm ステートメントを複製し、最適化中に (-O0 であっても) それらを削除する可能性があるため、これは確実に機能しません。

これを確実に行う 1 つの方法は、グローバル asm ステートメント (つまり、関数の外側にある asm ステートメント) を使用することです。gcc はこれを直接出力にコピーし、問題なくグローバル ラベルを使用できます。

于 2013-03-01T08:39:56.087 に答える