私の理解が正しければ.bss
、ELF ファイルのセクションは、ゼロで初期化された変数にスペースを割り当てるために使用されます。私たちのツール チェーンは ELF ファイルを生成するので、私の質問:.bss
セクションには実際にこれらすべてのゼロを含める必要がありますか? たとえば、グローバルな 10 メガバイトの配列を割り当てると、ELF ファイルに 10 メガバイトのゼロが発生するという、スペースの無駄遣いがあまりにもひどいように思えます。ここで何が間違っているのですか?
4 に答える
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
とが大幅に異なります。MemSiz
bss
LOAD
0x00104 (file-size) 0x61bac (mem-size)
このサンプルコードの場合:
int a[100000];
int main() { }
ELF仕様では、mem-sizeがfile-sizeよりも大きいセグメントの部分は、仮想メモリにゼロで埋められるだけであるとされています。LOAD
2番目のヘッダーのセグメントからセクションへのマッピングは次のようになります。
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のマンページを参照してください。
ELF ファイルのセクションは、プログラムでは初期化されないが、実行時にゼロに設定されることが保証され.bss
ている静的データに使用されます。違いを説明する小さな例を次に示します。
int main() {
static int bss_test1[100];
static int bss_test2[100] = {0};
return 0;
}
この場合、初期化されていないため、bss_test1
に配置され.bss
ます。bss_test2
ただし.data
、一連のゼロとともにセグメントに配置されます。.bss
ランタイム ローダーは、基本的に、ユーザーランド コードの実行が開始される前に、に予約されているスペースの量を割り当て、それをゼロにします。
objdump
、nm
、または同様のユーティリティを使用して違いを確認できます。
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 を明示的にゼロに初期化しないでください。ランタイム ローダー (通常) がそれを処理します。何かを明示的に初期化するとすぐに、コンパイラ/リンカーにデータを実行可能イメージに含めるように指示します。
.bss
セクションは実行可能ファイルに保存されません。最も一般的なセクション(、、)のうち、.text
(実際.data
のコード)と(初期化されたデータ)のみがELFファイルに存在します。.bss
.text
.data
その通りです。.bss はファイル内に物理的に存在するのではなく、ダイナミック ローダーがアプリケーション プログラムに .bss セクションを割り当てるために、そのサイズに関する情報だけが存在します。経験則として LOAD のみ、TLS セグメントはアプリケーション プログラム用のメモリを取得し、残りは動的ローダーに使用されます。
静的実行ファイルについては、実行可能ファイルの bss セクションにもスペースが与えられます
これは一般的なローダーのない組み込みアプリケーションです。
スマン