#operating-system #elf
Вопрос:
Мне интересно узнать, каковы соглашения для нулевых инициализированных сегментов/разделов в файлах ELF.
Обычно пишется, что раздел .bss файла ELF должен быть инициализирован нулем перед использованием.
Мне интересно, указано ли в соглашении/спецификации, должно ли указанное обнуление выполняться загрузчиком ELF или это должно выполняться самими инструкциями ELF.
В последнем случае, я полагаю, это будут некоторые инструкции/функции между точкой входа ELF и основной функцией программы…
Спасибо
Ответ №1:
Мне интересно, указывает ли соглашение/спецификация, должно ли указанное обнуление выполняться загрузчиком ELF
Да, косвенно.
или если это должно быть сделано самими инструкциями ЭЛЬФОВ.
Не существует такого понятия, как «инструкции ЭЛЬФА». Формат ELF (для исполняемой или общей библиотеки) указывает загрузчику, как загрузить и запустить объект ELF. Но весь разбор и настройка, которые происходят в загрузчике.
Так как же на самом деле происходит обнуление?
int foo[40960];
int main() { return 0; }
gcc t.c -no-pie
readelf -Wl a.out
Elf file type is EXEC (Executable file)
Entry point 0x401020
There are 11 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000400040 0x0000000000400040 0x000268 0x000268 R 0x8
INTERP 0x0002a8 0x00000000004002a8 0x00000000004002a8 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000400 0x000400 R 0x1000
LOAD 0x001000 0x0000000000401000 0x0000000000401000 0x00017d 0x00017d R E 0x1000
LOAD 0x002000 0x0000000000402000 0x0000000000402000 0x000110 0x000110 R 0x1000
LOAD 0x002e50 0x0000000000403e50 0x0000000000403e50 0x0001d8 0x028210 RW 0x1000
DYNAMIC 0x002e60 0x0000000000403e60 0x0000000000403e60 0x000190 0x000190 RW 0x8
NOTE 0x0002c4 0x00000000004002c4 0x00000000004002c4 0x000044 0x000044 R 0x4
GNU_EH_FRAME 0x002004 0x0000000000402004 0x0000000000402004 0x000034 0x000034 R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x002e50 0x0000000000403e50 0x0000000000403e50 0x0001b0 0x0001b0 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn
03 .init .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .dynamic .got .got.plt .data .bss
06 .dynamic
07 .note.gnu.build-id .note.ABI-tag
08 .eh_frame_hdr
09
10 .init_array .fini_array .dynamic .got
Здесь вы можете видеть, что
.bss
находится в 5-м сегменте.- Этот
LOAD
сегмент имеет крошечный.p_filesz == 0x1d8
, но довольно большой.p_memsz == 0x28210
размер .
Загрузчик выполняется mmap(...)
со смещением файла 0x2e50
, округленным до размера страницы (т. е. 0x2000
) и размера 0x028210 0xe50 == 0x29060
, который больше файла ( 0x3d20
в моем случае файл занимает только байты).
Это mmap
системный вызов, который предоставляет страницы с нулевым заполнением для любого отображения, которое продолжается после конца файла, поэтому фактические инструкции по вводу нуля являются частью ядра, а не загрузчика.
P.S. Вы можете убедиться в этом, посмотрев на readelf
вывод в вашей системе и сравнив его с выводом из strace ./a.out
.