web-dev-qa-db-ja.com

GCC:定数変数が.rodataに配置されない理由

GCCが_static const_変数をELFまたはそのようなファイルの_.rodata_セグメント(または最適化の場合は_.text_セグメント)に配置すると信じていました。しかし、そうではないようです。

現在、GNU/Linux搭載のラップトップでgcc (GCC) 4.7.0 20120505 (prerelease)を使用しています。また、静的定数変数を_.bss_セグメントに配置します。

_/*
 * this is a.c, and in its generated asm file a.s, the following line gives:
 *   .comm a,4,4 
 * which would place variable a in .bss but not .rodata(or .text)
 */
static const int a;

int main()
{
    int *p = (int*)&a;
    *p = 0;  /* since a is in .data, write access to that region */
             /* won't trigger an exception */
    return 0;
}
_

それで、これはバグですか、それとも機能ですか?私はこれをバグとしてbugzillaに報告することにしましたが、最初に助けを求める方が良いかもしれません。

GCCがconst変数を_.rodata_に配置できない理由はありますか?

更新:

テストしたように、明示的な初期化(_const int a = 0;_など)を持つ定数変数は、GCCによって_.rodata_に配置されますが、変数は初期化されていません。したがって、この質問は後で終了する可能性があります-私はおそらく正しい質問を提示しませんでした。

また、以前の言葉で、変数aが「.data」セクションに配置されていると書きましたが、これは誤りです。初期化されていないため、実際には_.bss_セクションに配置されます。上記のテキストが修正されました。

23
starrify

他の互換性のあるシンボルとマージでき、明示的に初期化された定義がない場合はbss(ディスク上の領域を使用しない)に入ることができるコンパイラーがそれを共通にしています。 rodataに置くことはトレードオフになります。実行時にメモリを節約しますが(コミットチャージ)、ディスク上のスペースをより多く使用します(巨大なアレイでは潜在的に多く)。

Rodataを使用する場合は、-fno-common GCCのオプション。

なぜ[〜#〜] gcc [〜#〜]するのですか?開発者自身に尋ねずにその質問に実際に答えることはできません。推測が許可されている場合は、最適化に関連する賭けをします。コンパイラーはconstを強制するhaveを行いません。

とは言っても、言語自体、特に未定義の振る舞いを見る方が良いと思います。未定義の動作については少し言及されていますが、どれも詳細には説明されていません。

定数の変更は未定義の動作ですConstはコントラクトです 、これは特にC(およびC++)に当てはまります。

「しかし、もし私がconstをconst_cast離れて、とにかくyを変更するとどうなりますか?」次に、未定義の動作があります。

未定義の動作手段は、コンパイラーが文字通り何でもしたいことを実行することが許可されていることであり、コンパイラーが実行することを決定したものは違反と見なされません。 ISO 9899規格の。

.4.

1未定義の動作

移植性のない、またはエラーのあるプログラム構造、またはエラーのあるデータの使用時の動作この国際規格は要件を課さない

2注予測できない結果を伴う状況の完全な無視から、(診断メッセージの発行の有無にかかわらず)環境に特徴的な文書化された方法での変換またはプログラムの実行中の動作、変換または実行の終了(診断メッセージの発行)。

ISO/IEC 9899:1999、§3.4.3

これは、未定義の動作を呼び出したため、コンパイラーが行うことはすべてnotincorrectであることによって技術的に正しいことを意味します。エルゴ、GCCが取るのは正しいです...

static const int a = 0;

...そして、それを.rodata記号に変えながら、...

static const int a; // guaranteed to be zero

...それを.bss記号に変えます。

前者の場合、aを変更しようとすると、たとえプロキシであっても、通常、セグメンテーション違反が発生し、カーネルが実行中のプログラムを強制終了します。後者の場合、プログラムはおそらくクラッシュすることなく実行されます。

そうは言っても、コンパイラーがどちらを実行するかを推測することは合理的ではありません。 Constは契約であり、定数であると想定されるデータを変更しないことにより、契約を守るのはプログラマーです。その契約に違反すると、未定義の動作、およびそれに付随するすべての移植性の問題とプログラムのバグが発生します。

したがって、GCCは2つのことを実行できます。

それは可能性があります .rodataにシンボルを書き込み、OSカーネルで保護します

それはメモリ保護が保証されていない場所にオブジェクトを書き込みます。その場合...

あるかもしれない値を変更する

あるかもしれない値を変更し、すぐに元に戻す

それは可能性があります値が変化しないという根拠の下で問題のコードを完全に削除します(0 -> 0)、基本的に最適化しています...

int main(){
    int *p = &a;
    *p = 0;
    return 0;
}

...に...

int main(void){
    return 0;
}

それはかもしれないあなたが生まれる前にあなたの両親を終わらせるためにモデルT-800を時間的に送り返すことさえします。

これらの動作はすべて合法である(まあ、legal標準に準拠しているという意味で)、バグレポートは保証されていません。

3
Braden Best

const修飾済みと宣言されているオブジェクトへの書き込みは未定義の動作です。

Cではオブジェクト自体を変更不可として宣言する方法はありません。オブジェクトへの特定のアクセスを通じてオブジェクトが変更可能になるのを禁止するだけです。ここにint*、したがって、コンパイラが診断を発行することを強制されないという意味で、変更は「許可」されます。 Cでキャストを実行するということは、自分が何をしているのかを知っているはずだということです。

2
Jens Gustedt

GCCがconst変数を.rodataに配置できない理由はありますか?

プログラムはコンパイラーによって最適化されます(-O0いくつかの最適化が行われます)。一定の伝播が行われます: http://en.wikipedia.org/wiki/Constant_folding

次のようにコンパイラをだまそうとします(このプログラムは技術的に未定義の動作であることに注意してください):

#include <stdio.h>

static const int a;

int main(void)
{
    *(int *) &a = printf("");  // compiler cannot assume it is 0

    printf("%d\n", a);

    return 0;
}
1
ouah