ELF文件存在2MiB未使用空间,求原因解析
ELF文件中2MiB未使用空间的原因分析
我在加载ELF文件到内存时,发现文件内存在大量未使用空间。使用的ELF文件大小约2.1MiB,链接脚本如下:
ENTRY(start) OUTPUT_FORMAT(elf64-x86-64) SECTIONS { . = 0xC00000; phys = .; .text ALIGN(0x1000): { code = .; *(.text) . = ALIGN(0x1000); } .rodata ALIGN(0x1000): { *(.rodata*) . = ALIGN(0x1000); } .data ALIGN(0x1000): { data = .; *(.data) . = ALIGN(0x1000); } .bss ALIGN(0x1000): { bss = .; *(.bss) . = ALIGN(0x1000); } end = .; _end = .; __end = .; /DISCARD/ : { *(.comment) *(.note.gnu.build-id) } }
查看程序头(地址0x40处)的p_offset字段值为0x200000,所有段表的偏移均大于0x200000,x86_64-elf-objdump的输出也验证了这一点:
cdcontents/!kernel.bin: file format elf64-x86-64 cdcontents/!kernel.bin architecture: i386:x86-64, flags 0x00000112: EXEC_P, HAS_SYMS, D_PAGED start address 0x000000000c000000 Program Header: LOAD off 0x0000000000200000 vaddr 0x000000000c000000 paddr 0x000000000c000000 align 2**12 filesz 0x0000000000009384 memsz 0x000000000000b000 flags rwx Sections: Idx Name Size VMA LMA File off Algn 0 .text 00006000 000000000c000000 000000000c000000 00200000 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .text.startup 00000094 000000000c006000 000000000c006000 00206000 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 2 .data 00001000 000000000c007000 000000000c007000 00207000 2**5 CONTENTS, ALLOC, LOAD, DATA 3 .rodata 00001000 000000000c008000 000000000c008000 00208000 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .eh_frame 00000384 000000000c009000 000000000c009000 00209000 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .bss 00001000 000000000c00a000 000000000c00a000 00209384 2**5 ALLOC 6 .debug_info 000035be 0000000000000000 0000000000000000 00209384 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 7 .debug_abbrev 00000db5 0000000000000000 0000000000000000 0020c942 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 8 .debug_loclists 00006865 0000000000000000 0000000000000000 0020d6f7 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 9 .debug_aranges 000001b0 0000000000000000 0000000000000000 00213f5c 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 10 .debug_rnglists 0000059a 0000000000000000 0000000000000000 0021410c 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 11 .debug_line 00002046 0000000000000000 0000000000000000 002146a6 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 12 .debug_str 000005f3 0000000000000000 0000000000000000 002166ec 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 13 .debug_line_str 0000015c 0000000000000000 0000000000000000 00216cdf 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS
这并非链接脚本中对齐设置导致的,因为脚本里指定的对齐值是0x1000,而非0x200000。我想知道:从0x78(ELF头部结束位置)到0x200000的这段2MiB空间有何用途?
简化版Makefile如下:
C_SOURCES = $(wildcard lib/*.c) $(wildcard libc/*.c) HEADERS = $(wildcard include/*.h) $(wildcard include/kernel/*.h) OBJ = ${C_SOURCES:.c=.o} lib/idtr.o asmlib/memmove.o asmlib/memcpy.o asmlib/unalignedisfaster.o asmlib/instrset.o \ asmlib/cputype.o asmlib/cachesize64.o CFLAGS = -O2 -std=gnu11 -g -static -Wall -Wextra -Werror -Wno-unused-function \ -Wno-unused-parameter -nostartfiles -Wno-unused-but-set-variable \ -Wstrict-prototypes -Wpointer-arith -Wcast-align -Wwrite-strings -Wshadow \ -fno-stack-protector -Wundef -nostdlib -fno-builtin -nodefaultlibs \ -fms-extensions -ffreestanding -mcmodel=large -fverbose-asm -nostartfiles \ -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -Iinclude -Wfloat-equal all: kernel.bin kernel.bin: lib/kernel_start.o ${OBJ} x86_64-elf-gcc -o $@ $^ -T link.ld -ffreestanding -O2 -nostdlib -lgcc %.o: %.c x86_64-elf-gcc ${CFLAGS} -c $< -o $@ %.o: %.asm nasm $< -f elf64 -o $@
问题原因
这段2MiB的空白空间是由-mcmodel=large编译选项导致的。在x86_64的大内存模型(large model)下,链接器会将可加载段的文件偏移对齐到2MiB(0x200000),这是因为大模型下代码和数据的寻址范围更大,链接器需要满足特定的内存布局要求来确保地址计算的正确性。
验证与解决方法
- 可以通过将编译选项中的
-mcmodel=large替换为-mcmodel=kernel来解决这个问题。-mcmodel=kernel是专门为x86_64内核设计的内存模型,它默认使用1GiB的地址空间,不需要将段偏移对齐到2MiB,同时保留内核所需的寻址特性。 - 另外,如果你确实需要使用大内存模型,也可以通过在链接脚本中显式指定
PHDRS并设置段的偏移来强制覆盖默认对齐行为,但对于内核开发来说,-mcmodel=kernel是更合适的选择。
内容的提问来源于stack exchange,提问作者Cyao
相关产品推荐
相关产品推荐

