web-dev-qa-db-ja.com

.bss vs COMMON:何がどこに行くのですか?

私の本から:

.bss:

初期化されていないグローバルC変数

一般:

まだ割り当てられていない初期化されていないデータオブジェクト

はっきりとした区別はありません。初期化されていない、割り当てられていないデータオブジェクトが何であるかさえ完全には理解していません...何もないようです。 GNUのreadelfツールを使用していくつかの単純なCコードを調べようとしましたが、単一のCOMMONシンボルが見つかりません。私はFORTRANのCOMMONタイプがCOMMONシンボルの例であるようなものを読みました-しかし私はFORTRANを知りません

誰かが私のために2つを区別できるでしょうか?可能であれば、できればCの例を使用しますか?大変感謝しています。

edit:from this post、変数c here:

int c;
int main() {} ...

共通である必要があります。しかし、objdump -tを使用すると、cが.bssにあることがわかります。

混乱

21
gone
// file a.c
// file-scope

int a = 0;  // goes into BSS

a.cをオブジェクトファイルa.oにコンパイルした後、aシンボルはBSSセクションに入ります。

// file b.c
// file-scope

int b;  // goes into COMMON section

b.cをオブジェクトファイルb.oにコンパイルした後、bシンボルはCOMMONセクションに入ります。

a.ob.oをリンクした後、abの両方のシンボルがBSSセクションに入ります。共通シンボルはオブジェクトファイルにのみ存在し、実行可能ファイルには存在しません。 UnixでのCOMMONシンボルの考え方は、特定の条件下で、単一の共通シンボルの下で(異なるコンパイル単位で)同じ変数の複数の外部定義を許可することです。

18
ouah

コモンズは、リンク段階の前にのみ表示されます。コモンズは後でbssまたはデータに入るものですが、それがどこに行くかを決めるのはリンカー次第です。これにより、同じ変数を異なるコンパイル単位で定義できます。私の知る限り、これは主に、int foo;の代わりにextern int foo;を含むいくつかの古いヘッダーファイルを許可するためです。

仕組みは次のとおりです。

$ cat > a.c
int foo;
$ cat > b.c
int foo;
$ cat > main.c
extern int foo;
int main(int argc, char **argv) { return foo; }
$ cc -c a.c && cc -c b.c && cc -c main.c && cc -o x a.o b.o main.o
$ objdump -t a.o | grep foo
0000000000000004       O *COM*  0000000000000004 foo
$ objdump -t b.o | grep foo
0000000000000004       O *COM*  0000000000000004 foo
$ objdump -t x | grep foo
0000000000600828 g     O .bss   0000000000000004              foo
$

これは、異なるコンパイル単位の変数の最大1つが初期化されている場合にのみ機能することに注意してください。

$ echo "int foo = 0;" > a.c
$ cc -c a.c && cc -c b.c && cc -c main.c && cc -o x a.o b.o main.o
$ echo "int foo = 0;" > b.c
$ cc -c a.c && cc -c b.c && cc -c main.c && cc -o x a.o b.o main.o
b.o:(.bss+0x0): multiple definition of `foo'
a.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status
$

これは恐ろしいことであり、古代のシステムとの互換性であり、決して信頼すべきではありません。物事を適切に行います-すべてのコンパイルユニットでグローバル変数の定義を1つだけにし、ヘッダーを介して他のすべての場所でそれを外部に宣言します。

13
Art

リンク中にcommonを許可すると、異なるユニットが同じ変数を宣言でき、リンカはそれらを同じ場所に配置します。タイプは同じである必要はないので、それはある種のリンクタイムユニオンです。これはFortranのCOMMON機能です。 Cのリンクでcommonを許可しない場合、そのような状況ではリンク時間エラーが発生します。このようなcommonリンクは、初期化されていないグローバルでのみ可能です。そうしないと、どの初期化を行うべきかが不明確になるためです。

bssに向かうグローバルは、Cが0に初期化されると定義する、初期化されていないグローバルです。ほとんどのオブジェクト形式は、サイズのみが指定され、ローダーがセクション全体をゼロで埋めるセクションをサポートします。

追伸:gccを使用する場合は、-fno-commonオプションを使用してcommonシンボルをbssセクションに強制できます。これは、Artが主張するように、適切で推奨される方法です。

8
Bryan Olivier