web-dev-qa-db-ja.com

.bssセクションのゼロで初期化された変数は、elfファイルのスペースを占めますか?

私が正しく理解していれば、ELFファイルの.bssセクションを使用して、ゼロで初期化された変数にスペースを割り当てます。私たちのツールチェーンはELFファイルを生成するため、私の質問:.bssセクションには実際にこれらのすべてのゼロを含める必要がありますか?スペースを無駄に浪費しているようです。たとえば、グローバルに10メガバイトの配列を割り当てると、ELFファイルで10メガバイトのゼロが発生します。ここで何が間違っていると思いますか?

44
Wouter Lievens

ELFを使用してからしばらく経ちました。しかし、私はまだこのことを覚えていると思います。いいえ、これらのゼロは物理的に含まれていません。 ELFファイルプログラムヘッダーを調べると、各ヘッダーに2つの数値があることがわかります。1つはファイル内のサイズです。もう1つは、仮想メモリに割り当てられたときのセクションのサイズ(readelf -l ./a.out)です。

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
  INTERP         0x000114 0x08048114 0x08048114 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00454 0x00454 R E 0x1000
  LOAD           0x000454 0x08049454 0x08049454 0x00104 0x61bac RW  0x1000
  DYNAMIC        0x000468 0x08049468 0x08049468 0x000d0 0x000d0 RW  0x4
  NOTE           0x000128 0x08048128 0x08048128 0x00020 0x00020 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

タイプLOADのヘッダーは、ファイルが実行のためにロードされるときに仮想メモリにコピーされるヘッダーです。他のヘッダーには、必要な共有ライブラリなどの他の情報が含まれています。ご覧のとおり、FileSizeMemSizは、bssセクションを含むヘッダー(2番目のLOAD 1つ)で大きく異なります。

0x00104 (file-size) 0x61bac (mem-size)

このサンプルコードの場合:

int a[100000];
int main() { }

ELF仕様では、mem-sizeがfile-sizeよりも大きいセグメントの部分は、仮想メモリのゼロで埋められていると述べています。 2番目のLOADヘッダーのセグメントからセクションへのマッピングは次のとおりです。

03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss

したがって、他にもいくつかのセクションがあります。 C++コンストラクタ/デストラクタ用。 Javaについても同様です。次に、.dynamicセクションのコピーと、動的リンクに役立つその他のものが含まれます(これは、必要な共有ライブラリを含む場所だと思います)。その後、初期化されたグローバルとローカル静的変数を含む.dataセクション。最後に、.bssセクションが表示されます。ファイルサイズではカバーされないため、ロード時にゼロで埋められます。

ところで、-Mリンカーオプションを使用すると、特定のシンボルがどの出力セクションに配置されるかを確認できます。 gccの場合、-Wl,-Mを使用して、オプションをリンカーに渡します。上記の例は、a.bss内に割り当てられていることを示しています。これは、初期化されていないオブジェクトが本当に.bssになり、他の場所にないことを確認するのに役立ちます。

.bss            0x08049560    0x61aa0
 [many input .o files...]
 *(COMMON) 
 *fill*         0x08049568       0x18 00
 COMMON         0x08049580    0x61a80 /tmp/cc2GT6nS.o
                0x08049580                a
                0x080ab000                . = ALIGN ((. != 0x0)?0x4:0x1) 
                0x080ab000                . = ALIGN (0x4) 
                0x080ab000                . = ALIGN (0x4) 
                0x080ab000                _end = .

GCCは、古いコンパイラとの互換性のために、デフォルトで初期化されていないグローバルをCOMMONセクションに保持します。これにより、複数の定義エラーなしでプログラム内で2回グローバルを定義できます。 -fno-commonを使用して、GCCがオブジェクトファイルの.bssセクションを使用するようにします(最終的にリンクされた実行可能ファイルには違いがありません。 リンカースクリプトld -verboseで表示します)。しかし、それはあなたを怖がらせるべきではありません。それは単なる内部の詳細です。 gccのマンページを参照してください。

ELFファイルの.bssセクションは、プログラムで初期化されていないである静的データに使用されますが、実行時にゼロに設定されることが保証されています。これが違いを説明する小さな例です。

int main() {
    static int bss_test1[100];
    static int bss_test2[100] = {0};
    return 0;
}

この場合、bss_test1は初期化されていないため、.bssに配置されます。ただし、bss_test2は、一連のゼロとともに.dataセグメントに配置されます。ランタイムローダーは基本的に.bss用に予約されたスペースの量を割り当て、ユーザーランドコードの実行が始まる前にそれをゼロにします。

objdumpnm、または同様のユーティリティを使用して、違いを確認できます。

moozletoots$ objdump -t a.out | grep bss_test
08049780 l     O .bss   00000190              bss_test1.3
080494c0 l     O .data  00000190              bss_test2.4

これは通常、組み込み開発者が最初に遭遇するサプライズの1つです... staticを明示的にゼロに初期化しないでください。ランタイムローダーは(通常)それを処理します。明示的に何かを初期化するとすぐに、実行可能イメージにデータを含めるようにコンパイラー/リンカーに指示します。

21
D.Shawley

.bssセクションは、実行可能ファイルに格納されていません。最も一般的なセクション(.text.data.bss)のうち、.text(実際のコード)と.data(初期化されたデータ)のみがELFに存在しますファイル。

3
mouviciel

つまり、.bssは物理的にファイルに存在せず、ダイナミックローダーが.bssセクションをアプリケーションプログラムに割り当てるために、そのサイズに関する情報のみが存在します。経験則として、LOADのみです。TLSセグメントはアプリケーションプログラムのメモリを取得し、残りはダイナミックローダーに使用されます。

静的実行可能ファイルについては、bssセクションにも実行可能ファイルのスペースが与えられます

ローダーがない組み込みアプリケーションは、これが一般的です。

スマン

1