|
| 1 | +--- |
| 2 | +title: 2024秋冬季训练营第四阶段总结-Zoneshiyi |
| 3 | +date: 2024-12-19 19:45:08 |
| 4 | +tags: |
| 5 | + - author: Zoneshiyi |
| 6 | +--- |
| 7 | +# mocklibc |
| 8 | + |
| 9 | +## ELF结构 |
| 10 | + |
| 11 | +### ELF Header |
| 12 | +描述整个文件的组织。 |
| 13 | +``` Rust |
| 14 | +pub struct Elf32_Ehdr { |
| 15 | + // Magic、Class(32-bit vs 64-bit)、Data(2's complement、endian)、ELF Version、OS/ABI、ABI Version |
| 16 | + pub e_ident: [u8; abi::EI_NIDENT], |
| 17 | + // Relocatable file、Executable file、Shared object file、Core file |
| 18 | + pub e_type: u16, |
| 19 | + // CPU 平台属性 |
| 20 | + pub e_machine: u16, |
| 21 | + // ELF 版本号,通常为1 |
| 22 | + pub e_version: u32, |
| 23 | + pub e_entry: u32, |
| 24 | + pub e_phoff: u32, |
| 25 | + pub e_shoff: u32, |
| 26 | + pub e_flags: u32, |
| 27 | + // Size of elf header |
| 28 | + pub e_ehsize: u16, |
| 29 | + // Size of ph entry |
| 30 | + pub e_phentsize: u16, |
| 31 | + // Number of ph |
| 32 | + pub e_phnum: u16, |
| 33 | + // Size of sh entry |
| 34 | + pub e_shentsize: u16, |
| 35 | + // Number of ph |
| 36 | + pub e_shnum: u16, |
| 37 | + // Section header string table index |
| 38 | + pub e_shstrndx: u16, |
| 39 | +} |
| 40 | + |
| 41 | +pub struct Elf64_Ehdr { |
| 42 | + ... |
| 43 | + pub e_entry: u64, |
| 44 | + pub e_phoff: u64, |
| 45 | + pub e_shoff: u64, |
| 46 | + ... |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +### Sections 和 Segments |
| 51 | +segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,在链接阶段,可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。一个segment包含若干个section。 |
| 52 | +#### Program Header Table |
| 53 | +描述文件中的各种segments,用来告诉系统如何创建进程映像的。 |
| 54 | +```Rust |
| 55 | +pub struct ProgramHeader { |
| 56 | + /// Program segment type |
| 57 | + pub p_type: u32, |
| 58 | + /// Offset into the ELF file where this segment begins |
| 59 | + pub p_offset: u64, |
| 60 | + /// Virtual adress where this segment should be loaded |
| 61 | + pub p_vaddr: u64, |
| 62 | + /// Physical address where this segment should be loaded |
| 63 | + pub p_paddr: u64, |
| 64 | + /// Size of this segment in the file |
| 65 | + pub p_filesz: u64, |
| 66 | + /// Size of this segment in memory |
| 67 | + pub p_memsz: u64, |
| 68 | + /// Flags for this segment |
| 69 | + pub p_flags: u32, |
| 70 | + /// file and memory alignment |
| 71 | + pub p_align: u64, |
| 72 | +} |
| 73 | +``` |
| 74 | +**p_type** |
| 75 | +```Rust |
| 76 | +/// Program header table entry unused |
| 77 | +pub const PT_NULL: u32 = 0; |
| 78 | +/// Loadable program segment |
| 79 | +pub const PT_LOAD: u32 = 1; |
| 80 | +/// Dynamic linking information |
| 81 | +pub const PT_DYNAMIC: u32 = 2; |
| 82 | +/// Program interpreter |
| 83 | +pub const PT_INTERP: u32 = 3; |
| 84 | +/// Auxiliary information |
| 85 | +pub const PT_NOTE: u32 = 4; |
| 86 | +/// Unused |
| 87 | +pub const PT_SHLIB: u32 = 5; |
| 88 | +/// The program header table |
| 89 | +pub const PT_PHDR: u32 = 6; |
| 90 | +/// Thread-local storage segment |
| 91 | +pub const PT_TLS: u32 = 7; |
| 92 | +/// GCC .eh_frame_hdr segment |
| 93 | +pub const PT_GNU_EH_FRAME: u32 = 0x6474e550; |
| 94 | +/// Indicates stack executability |
| 95 | +pub const PT_GNU_STACK: u32 = 0x6474e551; |
| 96 | +/// Read-only after relocation |
| 97 | +pub const PT_GNU_RELRO: u32 = 0x6474e552; |
| 98 | +/// The segment contains .note.gnu.property section |
| 99 | +pub const PT_GNU_PROPERTY: u32 = 0x6474e553; |
| 100 | +/// Values between [PT_LOOS, PT_HIOS] in this inclusive range are reserved for |
| 101 | +/// operating system-specific semantics. |
| 102 | +pub const PT_LOOS: u32 = 0x60000000; |
| 103 | +/// Values between [PT_LOOS, PT_HIOS] in this inclusive range are reserved for |
| 104 | +/// operating system-specific semantics. |
| 105 | +pub const PT_HIOS: u32 = 0x6fffffff; |
| 106 | +/// Values between [PT_LOPROC, PT_HIPROC] in this inclusive range are reserved |
| 107 | +/// for processor-specific semantics. |
| 108 | +pub const PT_LOPROC: u32 = 0x70000000; |
| 109 | +/// Values between [PT_LOPROC, PT_HIPROC] in this inclusive range are reserved |
| 110 | +/// for processor-specific semantics. |
| 111 | +pub const PT_HIPROC: u32 = 0x7fffffff; |
| 112 | +``` |
| 113 | +#### Section Header Table |
| 114 | +```Rust |
| 115 | +pub struct SectionHeader { |
| 116 | + /// Section Name,对应字符串在string table段中的偏移 |
| 117 | + pub sh_name: u32, |
| 118 | + /// Section Type |
| 119 | + pub sh_type: u32, |
| 120 | + /// Section Flags |
| 121 | + pub sh_flags: u64, |
| 122 | + /// in-memory address where this section is loaded |
| 123 | + pub sh_addr: u64, |
| 124 | + /// Byte-offset into the file where this section starts |
| 125 | + pub sh_offset: u64, |
| 126 | + /// Section size in bytes |
| 127 | + pub sh_size: u64, |
| 128 | + /// Defined by section type |
| 129 | + pub sh_link: u32, |
| 130 | + /// Defined by section type |
| 131 | + pub sh_info: u32, |
| 132 | + /// address alignment |
| 133 | + pub sh_addralign: u64, |
| 134 | + /// size of an entry if section data is an array of entries |
| 135 | + pub sh_entsize: u64, |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +## 自定义加载ELF |
| 140 | + |
| 141 | +### 静态链接 |
| 142 | + |
| 143 | +**编译选项:** |
| 144 | +```bash |
| 145 | +STATIC_FLAG = \ |
| 146 | +-nostdlib \ |
| 147 | +-nostartfiles \ |
| 148 | +-nodefaultlibs \ |
| 149 | +-ffreestanding \ |
| 150 | +-O0 \ |
| 151 | +-mcmodel=medany \ |
| 152 | +-static \ |
| 153 | +-no-pie \ |
| 154 | +-L./target/riscv64gc-unknown-linux-musl/release/ -lmocklibc |
| 155 | + |
| 156 | +riscv64-linux-musl-gcc hello.c $(STATIC_FLAG) -o hello |
| 157 | +``` |
| 158 | + |
| 159 | +静态编译的可执行文件加载比较简单,直接将其加载到内存中的一块连续空间即可。 |
| 160 | + |
| 161 | +唯一要注意的是同时开启`-static`和`-no-pie`选项才能生产类型为EXEC (Executable file)的ELF文件,同时还需要通过linker.ld链接脚本正确设置起始地址。 |
| 162 | + |
| 163 | +### 动态链接 |
| 164 | + |
| 165 | +**编译选项:** |
| 166 | +```bash |
| 167 | +DYNAMIC_FLAG = \ |
| 168 | +-nostdlib \ |
| 169 | +-nostartfiles \ |
| 170 | +-nodefaultlibs \ |
| 171 | +-ffreestanding \ |
| 172 | +-O0 \ |
| 173 | +-mcmodel=medany \ |
| 174 | +-L./target/riscv64gc-unknown-linux-musl/release/ -lmocklibc |
| 175 | + |
| 176 | +riscv64-linux-musl-gcc hello.c $(DYNAMIC_FLAG) -o hello -Wl,-dynamic-linker /path/to/ld-musl-riscv64.so.1 |
| 177 | + |
| 178 | +``` |
| 179 | + |
| 180 | +linux加载动态链接的可执行文件流程比较复杂,一方面是系统在执行应用前有很多额外的处理,另一方面是加载器本身不提供函数,需要加载一系列的动态链接库。而在我们当前的Unikernel框架下,并不存在动态链接库,系统启动时所有函数都加载到了内存中,因此可以大幅简化加载流程。 |
| 181 | + |
| 182 | +首先解析program header将elf文件中类型为PT_LOAD的segment加载到内存中,然后解析.dynsym、.rela.plt节的信息可以知道需要动态链接的函数名,以及重定位条目在内存中的位置。在内核中建立了函数名到对于函数地址的映射,根据这些信息修改重定位条目就能让程序正确执行。 |
0 commit comments