46

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

4

4 に答える 4

69

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、ファイルが実行のためにロードされるときに仮想メモリにコピーされるヘッダーです。他のヘッダーには、必要な共有ライブラリなどの他の情報が含まれています。ご覧のとおり、セクションを含むヘッダー(2番目のヘッダー)では、FileSizeとが大幅に異なります。MemSizbssLOAD

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

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

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

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

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回定義できます。GCCにオブジェクトファイルの.bssセクションを使用-fno-commonさせるために使用します(とにかく.bss出力セクションに入るので、最終的にリンクされた実行可能ファイルに違いはありません。これはリンカースクリプトによって制御されます。表示します。とld -verbose)。しかし、それはあなたを怖がらせるべきではありません、それはただ内部の詳細です。gccのマンページを参照してください。

于 2009-03-04T14:21:19.933 に答える
21

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 つです... statics を明示的にゼロに初期化しないでください。ランタイム ローダー (通常) がそれを処理します。何かを明示的に初期化するとすぐに、コンパイラ/リンカーにデータを実行可能イメージに含めるように指示します。

于 2009-03-04T14:28:53.753 に答える
3

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

于 2009-03-04T14:19:26.770 に答える
1

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

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

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

スマン

于 2009-03-27T17:38:19.317 に答える