From 1ada73ff5619991ae3e28ab4295e3c3c657ba2b5 Mon Sep 17 00:00:00 2001 From: zoneshiyi Date: Fri, 20 Dec 2024 19:59:43 +0800 Subject: [PATCH] =?UTF-8?q?2024=E7=A7=8B=E5=86=AC=E5=AD=A3=E8=AE=AD?= =?UTF-8?q?=E7=BB=83=E8=90=A5=E7=AC=AC=E5=9B=9B=E9=98=B6=E6=AE=B5=E6=80=BB?= =?UTF-8?q?=E7=BB=93-Zoneshiyi.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\265\346\200\273\347\273\223-Zoneshiyi.md" | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 "source/_posts/2024\347\247\213\345\206\254\345\255\243\350\256\255\347\273\203\350\220\245\347\254\254\345\233\233\351\230\266\346\256\265\346\200\273\347\273\223-Zoneshiyi.md" diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\350\256\255\347\273\203\350\220\245\347\254\254\345\233\233\351\230\266\346\256\265\346\200\273\347\273\223-Zoneshiyi.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\350\256\255\347\273\203\350\220\245\347\254\254\345\233\233\351\230\266\346\256\265\346\200\273\347\273\223-Zoneshiyi.md" new file mode 100644 index 0000000000..75c01b5fba --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\350\256\255\347\273\203\350\220\245\347\254\254\345\233\233\351\230\266\346\256\265\346\200\273\347\273\223-Zoneshiyi.md" @@ -0,0 +1,182 @@ +--- +title: 2024秋冬季训练营第四阶段总结-Zoneshiyi +date: 2024-12-19 19:45:08 +tags: + - author: Zoneshiyi +--- +# mocklibc + +## ELF结构 + +### ELF Header +描述整个文件的组织。 +``` Rust +pub struct Elf32_Ehdr { + // Magic、Class(32-bit vs 64-bit)、Data(2's complement、endian)、ELF Version、OS/ABI、ABI Version + pub e_ident: [u8; abi::EI_NIDENT], + // Relocatable file、Executable file、Shared object file、Core file + pub e_type: u16, + // CPU 平台属性 + pub e_machine: u16, + // ELF 版本号,通常为1 + pub e_version: u32, + pub e_entry: u32, + pub e_phoff: u32, + pub e_shoff: u32, + pub e_flags: u32, + // Size of elf header + pub e_ehsize: u16, + // Size of ph entry + pub e_phentsize: u16, + // Number of ph + pub e_phnum: u16, + // Size of sh entry + pub e_shentsize: u16, + // Number of ph + pub e_shnum: u16, + // Section header string table index + pub e_shstrndx: u16, +} + +pub struct Elf64_Ehdr { + ... + pub e_entry: u64, + pub e_phoff: u64, + pub e_shoff: u64, + ... +} +``` + +### Sections 和 Segments +segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,在链接阶段,可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。一个segment包含若干个section。 +#### Program Header Table +描述文件中的各种segments,用来告诉系统如何创建进程映像的。 +```Rust +pub struct ProgramHeader { + /// Program segment type + pub p_type: u32, + /// Offset into the ELF file where this segment begins + pub p_offset: u64, + /// Virtual adress where this segment should be loaded + pub p_vaddr: u64, + /// Physical address where this segment should be loaded + pub p_paddr: u64, + /// Size of this segment in the file + pub p_filesz: u64, + /// Size of this segment in memory + pub p_memsz: u64, + /// Flags for this segment + pub p_flags: u32, + /// file and memory alignment + pub p_align: u64, +} +``` +**p_type** +```Rust +/// Program header table entry unused +pub const PT_NULL: u32 = 0; +/// Loadable program segment +pub const PT_LOAD: u32 = 1; +/// Dynamic linking information +pub const PT_DYNAMIC: u32 = 2; +/// Program interpreter +pub const PT_INTERP: u32 = 3; +/// Auxiliary information +pub const PT_NOTE: u32 = 4; +/// Unused +pub const PT_SHLIB: u32 = 5; +/// The program header table +pub const PT_PHDR: u32 = 6; +/// Thread-local storage segment +pub const PT_TLS: u32 = 7; +/// GCC .eh_frame_hdr segment +pub const PT_GNU_EH_FRAME: u32 = 0x6474e550; +/// Indicates stack executability +pub const PT_GNU_STACK: u32 = 0x6474e551; +/// Read-only after relocation +pub const PT_GNU_RELRO: u32 = 0x6474e552; +/// The segment contains .note.gnu.property section +pub const PT_GNU_PROPERTY: u32 = 0x6474e553; +/// Values between [PT_LOOS, PT_HIOS] in this inclusive range are reserved for +/// operating system-specific semantics. +pub const PT_LOOS: u32 = 0x60000000; +/// Values between [PT_LOOS, PT_HIOS] in this inclusive range are reserved for +/// operating system-specific semantics. +pub const PT_HIOS: u32 = 0x6fffffff; +/// Values between [PT_LOPROC, PT_HIPROC] in this inclusive range are reserved +/// for processor-specific semantics. +pub const PT_LOPROC: u32 = 0x70000000; +/// Values between [PT_LOPROC, PT_HIPROC] in this inclusive range are reserved +/// for processor-specific semantics. +pub const PT_HIPROC: u32 = 0x7fffffff; +``` +#### Section Header Table +```Rust +pub struct SectionHeader { + /// Section Name,对应字符串在string table段中的偏移 + pub sh_name: u32, + /// Section Type + pub sh_type: u32, + /// Section Flags + pub sh_flags: u64, + /// in-memory address where this section is loaded + pub sh_addr: u64, + /// Byte-offset into the file where this section starts + pub sh_offset: u64, + /// Section size in bytes + pub sh_size: u64, + /// Defined by section type + pub sh_link: u32, + /// Defined by section type + pub sh_info: u32, + /// address alignment + pub sh_addralign: u64, + /// size of an entry if section data is an array of entries + pub sh_entsize: u64, +} +``` + +## 自定义加载ELF + +### 静态链接 + +**编译选项:** +```bash +STATIC_FLAG = \ +-nostdlib \ +-nostartfiles \ +-nodefaultlibs \ +-ffreestanding \ +-O0 \ +-mcmodel=medany \ +-static \ +-no-pie \ +-L./target/riscv64gc-unknown-linux-musl/release/ -lmocklibc + +riscv64-linux-musl-gcc hello.c $(STATIC_FLAG) -o hello +``` + +静态编译的可执行文件加载比较简单,直接将其加载到内存中的一块连续空间即可。 + +唯一要注意的是同时开启`-static`和`-no-pie`选项才能生产类型为EXEC (Executable file)的ELF文件,同时还需要通过linker.ld链接脚本正确设置起始地址。 + +### 动态链接 + +**编译选项:** +```bash +DYNAMIC_FLAG = \ +-nostdlib \ +-nostartfiles \ +-nodefaultlibs \ +-ffreestanding \ +-O0 \ +-mcmodel=medany \ +-L./target/riscv64gc-unknown-linux-musl/release/ -lmocklibc + +riscv64-linux-musl-gcc hello.c $(DYNAMIC_FLAG) -o hello -Wl,-dynamic-linker /path/to/ld-musl-riscv64.so.1 + +``` + +linux加载动态链接的可执行文件流程比较复杂,一方面是系统在执行应用前有很多额外的处理,另一方面是加载器本身不提供函数,需要加载一系列的动态链接库。而在我们当前的Unikernel框架下,并不存在动态链接库,系统启动时所有函数都加载到了内存中,因此可以大幅简化加载流程。 + +首先解析program header将elf文件中类型为PT_LOAD的segment加载到内存中,然后解析.dynsym、.rela.plt节的信息可以知道需要动态链接的函数名,以及重定位条目在内存中的位置。在内核中建立了函数名到对于函数地址的映射,根据这些信息修改重定位条目就能让程序正确执行。