18

これをコンパイルするとき:

// external definitions
int value1 = 0;
static int value2 = 0;

gccコンパイラは次のアセンブリを生成します。

.globl value1
        .bss
        .align 4
        .type   value1, @object
        .size   value1, 4
value1:
        .zero   4
        .local  value2
        .comm   value2,4,4

ただし、変数をゼロ以外の値に初期化すると、次のようになります。

// external definitions
int value1 = 1;
static int value2 = 1;

gccコンパイラは以下を生成しました:

.globl value1
        .data
        .align 4
        .type   value1, @object
        .size   value1, 4
value1:
        .long   1
        .align 4
        .type   value2, @object
        .size   value2, 4
value2:
        .long   1

私の質問は次のとおりです。

  1. 最初のケースでは値がbssセグメントに割り当てられ、2番目のケースではデータセグメントに割り当てられるのはなぜですか。
  2. value2変数が最初のケースでは.localおよび.commとして定義され、2番目のケースでは定義されないのはなぜですか。
4

3 に答える 3

11

一般的に、bssセクションには初期化されていない値が含まれ、dataセクションには初期化された値が含まれます。ただし、gccは、ゼロに初期化された値をbssセクションではなくセクションにdata配置します。bssセクションは実行時にゼロにされるため、セクションにゼロを格納することはあまり意味がありませんdata。これにより、ディスクスペースが節約されます。 gcc:

-fno-zero-initialized-in-bssターゲットがBSSセクションをサポートしている場合、GCCはデフォルトで、ゼロに初期化された変数をBSSに配置します。これにより、結果のコードのスペースを節約できます。一部のプログラムはデータセクションに移動する変数に明示的に依存しているため、このオプションはこの動作をオフにします

オブジェクトファイルに対してローカルな静的.commストレージで使用される理由がわかりません。通常、共通のシンボルを宣言するために使用されます。定義/初期化されていない場合は、リンカーによって他のシンボルと同じ名前のシンボルとマージする必要があります。オブジェクトファイル。これが、変数が初期化されているため、2番目の例では使用されていない理由です。as

.commは、symbolという名前の共通シンボルを宣言します。リンクすると、あるオブジェクトファイルの共通シンボルが、別のオブジェクトファイルの同じ名前の定義済みまたは共通シンボルとマージされる場合があります。

于 2012-11-27T09:33:45.937 に答える
5

最初のケースは、値をゼロで初期化したためです。何も指定されていない場合、グローバル整数が0で初期化されるのは、 C標準(セクション6.7.8)の一部です。そのため、ファイル形式では、バイナリを特別なセクションに配置することで、バイナリを小さく保つことができますbssELF仕様(I-15ページ)のいくつかを見ると、次のことがわかります。

.bssこのセクションは、プログラムのメモリイメージに寄与する初期化されていないデータを保持します。定義上、プログラムの実行が開始されると、システムはデータをゼロで初期化します。セクションタイプSHT_NOBITSで示されているように、セクションはファイルスペースを占有しません。

最初のケースでは、コンパイラーが最適化を行いました。イニシャライザを格納するために実際のバイナリにスペースをとる必要はありません。これは、bssセグメントを使用して、必要なセグメントを無料で取得できるためです。

さて、外部ソースから静的なものが入ってくるという事実は少し興味深いです(通常は行われません)。ただし、コンパイル中のモジュールでは、他のモジュールと共有してはならず、。でマークする必要があります.local。イニシャライザに保存される実際の値がないため、このようになっているのではないかと思います。

2番目の例では、ゼロ以外の初期化子を指定したため、初期化されたデータセグメントに存在することがわかりますdatavalue1非常によく似ていますがvalue2、の場合、コンパイラは初期化子用のスペースを予約する必要があります。.localこの場合、値を設定してそれを使用するだけでよいため、マークを付ける必要はありません。.globl声明がないため、グローバルではありません。

ところで、http://refspecs.linuxbase.org/は、バイナリ形式などに関する低レベルの詳細のいくつかを訪問するのに適した場所です。

于 2012-11-27T09:55:25.753 に答える
3

BSSは、実行時に初期化されたデータを含むセグメントですが、データセグメントには、プログラムバイナリで初期化されたデータが含まれます。

これで、静的変数は、プログラムで明示的に実行されるかどうかに関係なく、常に初期化されます。ただし、初期化(DS)と非初期化(BSS)の2つのカテゴリがあります。

BSSに存在するすべての値は、プログラムのコードで初期化されていないため、実行時にプログラムが0(整数の場合)にロードされたときに初期化され、ポインターの場合はnullなどです。

したがって、0で初期化すると、値はBSSに送られ、割り当てられた他の値と同様に、データセグメントに変数が割り当てられます。

興味深い結果は、BSSで初期化されたデータのサイズがプログラムバイナリに含まれないのに対し、データセグメントのデータのサイズが含まれることです。

大きな静的配列を割り当てて、プログラムで使用してみてください。コードで明示的に初期化されていない場合の実行可能ファイルのサイズを確認してください。次に、次のようなゼロ以外の値で初期化します。

static int arr[1000] = {2};

後者の場合の実行可能ファイルのサイズは大幅に大きくなります

于 2012-11-27T09:36:45.920 に答える