Tun .bss-Abschnitt null-initialisierte Variablen besetzen Platz in der elf-Datei?
Frage
Wenn ich das richtig verstehe, die .bss
Abschnitt in ELF-Dateien verwendet wird, um Speicherplatz für null-initialisierte Variablen.Unsere tool-Kette erzeugt ELF-Dateien, daher meine Frage:funktioniert die .bss
Abschnitt tatsächlich enthalten alle die Nullen?Es scheint wie eine schreckliche Verschwendung von Räumen, die, wenn, sagen wir, ich weisen Sie eine Globale zehn megabyte array, es Ergebnisse in zehn Megabyte Nullen in der ELF-Datei."Was sehe ich hier falsch?
Lösung
Wurde seit einiger Zeit arbeitete ich mit ELF.Aber ich glaube, ich erinnere mich noch an dieses Zeug.Keine, es nicht physisch enthalten Nullen.Wenn Sie einen Blick in eine ELF-Datei-Programm-header, dann werden Sie sehen, jede Kopfzeile hat zwei Nummern:Eine ist die Größe der Datei.Und die andere ist die Größe der Abschnitt hat, wenn die zugeordneten virtuellen Speicher (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
Header type LOAD
sind Sie die eine, die kopiert werden in den virtuellen Speicher, wenn die Datei geladen wird, die für die Ausführung.Andere Header enthalten weitere Informationen, wie Sie die gemeinsam genutzten Bibliotheken, die benötigt werden.Wie Sie sehen, die FileSize
und MemSiz
wesentlich für die Kopfzeile enthält die bss
Abschnitt (die zweite LOAD
einer):
0x00104 (file-size) 0x61bac (mem-size)
Für dieses Beispiel-code:
int a[100000];
int main() { }
Der ELF-Spezifikation sagt, dass das Teil ein segment, das die mem-size ist größer als die Datei-Größe ist nur ausgefüllt, mit Nullen im virtuellen Speicher.Das segment Abschnitt mapping des zweiten LOAD
header ist wie folgt:
03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
So gibt es einige andere Abschnitte es auch.Für C++ - Konstruktor/Destruktor.Das gleiche gilt für Java.So enthält es eine Kopie der .dynamic
Abschnitt und andere Sachen nützlich für die dynamische Verknüpfung (ich glaube, das ist der Ort, der enthält die benötigten shared libraries unter Sonstiges).Nach, dass die .data
Abschnitt enthält initialisierte globals und lokale statische Variablen.Am Ende der .bss
Abschnitt wird angezeigt, die gefüllt ist von Nullen auf die Ladezeit, da die Datei-Größe nicht abdecken.
Durch die Weg, Sie können sehen, in die output-Sektion ein bestimmtes symbol platziert werden, durch die Verwendung der -M
linker-option.Für den gcc verwenden Sie -Wl,-M
setzen Sie die option durch den linker.Das obige Beispiel zeigt, dass a
zugeordnet wird innerhalb .bss
.Es kann Ihnen helfen, stellen Sie sicher, dass Sie Ihr nicht initialisierte Objekte wirklich am Ende in .bss
und nicht irgendwo anders:
.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 hält nicht initialisierte globals in einem GEMEINSAMEN Abschnitt standardmäßig für die Kompatibilität mit alten Compilern, die es ermöglichen, globals definiert zweimal in einem Programm ohne mehrfach definiert Fehler.Verwenden -fno-common
machen GCC verwenden .bss-Abschnitte für die Objekt-Dateien (nicht einen Unterschied machen, der für die endgültige verknüpfte ausführbare Datei, denn wie Sie sehen, ist es gehen, um in eine .bss-Ausgabe-Bereich sowieso.Dies wird gesteuert durch linker-Skript.Zeigen Sie es mit ld -verbose
).Aber das sollte nicht erschrecken, es ist nur eine interne detail.Siehe die manpage von gcc.
Andere Tipps
Der .bss
Abschnitt in einer ELF-Datei für statische Daten verwendet wird, die ist nicht initialisiert programmatisch aber garantiert auf Null zur Laufzeit festgelegt werden. Hier ist ein kleines Beispiel, das die Differenz wird erklären.
int main() {
static int bss_test1[100];
static int bss_test2[100] = {0};
return 0;
}
In diesem Fall bss_test1
in die .bss
gelegt, da es nicht initialisiert ist. bss_test2
jedoch wird zusammen mit einer Reihe von Nullen in das .data
Segment platziert. Der Runtime-loader ordnet grundsätzlich die Menge an Speicherplatz für die .bss
reserviert und Nullen es aus, bevor ein Userland-Code beginnt mit der Ausführung.
Sie können den Unterschied mit objdump
, nm
oder ähnlichen Dienstprogramme siehe:
moozletoots$ objdump -t a.out | grep bss_test
08049780 l O .bss 00000190 bss_test1.3
080494c0 l O .data 00000190 bss_test2.4
Diese ist in der Regel einer der ersten Überraschungen , dass Embedded-Entwicklern den Weg laufen ... nie explizit auf Null Statik initialisieren. Die Runtime-Loader (in der Regel) kümmert sich darum. Sobald Sie etwas explizit initialisieren, Sie erzählen die Compiler / Linker die Daten in der ausführbaren Bild aufzunehmen.
Ein .bss
Abschnitt wird in einer ausführbaren Datei nicht gespeichert. Von den am häufigsten Abschnitte (.text
, .data
, .bss
), nur .text
(Ist-Code) und .data
(initialisierten Daten) sind in einer ELF-Datei.
Das ist richtig, .bss physisch nicht in der Datei vorhanden ist, sondern nur die Informationen über die Größe vorhanden ist, für das dynamische Lade den .bss Abschnitt für das Anwendungsprogramm zuzuweisen. Als Faustregel gilt nur LOAD, TLS Segment den Speicher für das Anwendungsprogramm erhält, Rest für dynamische Lader verwendet werden.
über statische ausführbare Datei, bss Abschnitte wird auch Platz im execuatble gegeben
Embedded-Anwendung, wo es keine loader diese üblich ist.
Suman