draugr~ single ELF file analysis tool |
| git clone https://github.com/djnnvx/draugr.git |
| log | files | refs | archive | README |
loader.odin (8200B)
1 package elf 2 3 load :: proc(data: []u8) -> (ELF_Info, string) { 4 info: ELF_Info 5 6 header, err := parse_header(data) 7 if err != "" { 8 return info, err 9 } 10 info.header = header 11 info.is_64bit = header.class == ELF_CLASS_64 12 info.is_pie = header.type == ET_DYN 13 info.is_dynamic = false 14 15 info.program_hdrs = make([dynamic]Program_Header) 16 info.section_hdrs = make([dynamic]Section_Header) 17 info.symbols = make([dynamic]Symbol) 18 info.dyn_symbols = make([dynamic]Symbol) 19 info.dyn_entries = make([dynamic]Dyn_Entry) 20 info.needed = make([dynamic]string) 21 info.relocs = make([dynamic]Reloc) 22 23 // Sections first: PN_XNUM resolves e_phnum through section[0]. 24 parse_section_headers(data, &info) 25 parse_program_headers(data, &info) 26 parse_symbols(data, &info) 27 parse_dynamic(data, &info) 28 parse_relocs(data, &info) 29 30 return info, "" 31 } 32 33 destroy :: proc(info: ^ELF_Info) { 34 delete(info.program_hdrs) 35 delete(info.section_hdrs) 36 delete(info.symbols) 37 delete(info.dyn_symbols) 38 delete(info.dyn_entries) 39 delete(info.needed) 40 delete(info.relocs) 41 } 42 43 get_section_name :: proc(info: ^ELF_Info, shdr: Section_Header) -> string { 44 if info.shstrtab == nil { 45 return "<unknown>" 46 } 47 if len(info.shstrtab) == 0 { 48 return "<unknown>" 49 } 50 start := int(shdr.name) 51 for i := start; i < len(info.shstrtab); i += 1 { 52 if info.shstrtab[i] == 0 { 53 return string(info.shstrtab[start:i]) 54 } 55 } 56 return "" 57 } 58 59 get_symbol_name :: proc(strtab: []u8, name_offset: u32) -> string { 60 if strtab == nil { 61 return "" 62 } 63 if len(strtab) == 0 { 64 return "" 65 } 66 start := int(name_offset) 67 if start >= len(strtab) { 68 return "" 69 } 70 for i := start; i < len(strtab); i += 1 { 71 if strtab[i] == 0 { 72 return string(strtab[start:i]) 73 } 74 } 75 return "" 76 } 77 78 PHDR_SIZE_64 :: 56 79 PHDR_SIZE_32 :: 32 80 SHDR_SIZE_64 :: 64 81 SHDR_SIZE_32 :: 40 82 SYM_SIZE_64 :: 24 83 SYM_SIZE_32 :: 16 84 85 read_program_header :: proc(data: []u8, offset: int, is_64bit: bool, order: Byte_Order) -> Program_Header { 86 ph: Program_Header 87 if is_64bit { 88 ph.type = read_u32(data, offset, order) 89 ph.flags = u64(read_u32(data, offset + 4, order)) 90 ph.offset = read_u64(data, offset + 8, order) 91 ph.vaddr = read_u64(data, offset + 16, order) 92 ph.paddr = read_u64(data, offset + 24, order) 93 ph.filesz = read_u64(data, offset + 32, order) 94 ph.memsz = read_u64(data, offset + 40, order) 95 ph.align = read_u64(data, offset + 48, order) 96 } else { 97 ph.type = read_u32(data, offset, order) 98 ph.offset = u64(read_u32(data, offset + 4, order)) 99 ph.vaddr = u64(read_u32(data, offset + 8, order)) 100 ph.paddr = u64(read_u32(data, offset + 12, order)) 101 ph.filesz = u64(read_u32(data, offset + 16, order)) 102 ph.memsz = u64(read_u32(data, offset + 20, order)) 103 ph.flags = u64(read_u32(data, offset + 24, order)) 104 ph.align = u64(read_u32(data, offset + 28, order)) 105 } 106 return ph 107 } 108 109 parse_program_headers :: proc(data: []u8, info: ^ELF_Info) { 110 header := info.header 111 is_64bit := info.is_64bit 112 order := byte_order(info.header) 113 114 if !in_bounds(data, header.phoff, 0) { return } 115 116 min_size := PHDR_SIZE_64 if is_64bit else PHDR_SIZE_32 117 entsize := int(header.phentsize) 118 if entsize < min_size { entsize = min_size } 119 120 // PN_XNUM: the real segment count lives in section[0].sh_info. 121 phnum := u64(header.phnum) 122 if phnum == PN_XNUM && len(info.section_hdrs) > 0 { 123 phnum = u64(info.section_hdrs[0].info) 124 } 125 126 offset := int(header.phoff) 127 for i := u64(0); i < phnum; i += 1 { 128 if offset + min_size > len(data) { break } 129 130 ph := read_program_header(data, offset, is_64bit, order) 131 offset += entsize 132 append(&info.program_hdrs, ph) 133 134 if ph.type == PT_INTERP { 135 info.interp_offset = ph.offset 136 info.is_dynamic = true 137 } 138 if ph.type == PT_DYNAMIC { 139 info.is_dynamic = true 140 } 141 } 142 } 143 144 read_section_header :: proc(data: []u8, offset: int, is_64bit: bool, order: Byte_Order) -> Section_Header { 145 sh: Section_Header 146 if is_64bit { 147 sh.name = read_u32(data, offset, order) 148 sh.type = read_u32(data, offset + 4, order) 149 sh.flags = read_u64(data, offset + 8, order) 150 sh.addr = read_u64(data, offset + 16, order) 151 sh.offset = read_u64(data, offset + 24, order) 152 sh.size = read_u64(data, offset + 32, order) 153 sh.link = read_u32(data, offset + 40, order) 154 sh.info = read_u32(data, offset + 44, order) 155 sh.addralign = read_u64(data, offset + 48, order) 156 sh.entsize = read_u64(data, offset + 56, order) 157 } else { 158 sh.name = read_u32(data, offset, order) 159 sh.type = read_u32(data, offset + 4, order) 160 sh.flags = u64(read_u32(data, offset + 8, order)) 161 sh.addr = u64(read_u32(data, offset + 12, order)) 162 sh.offset = u64(read_u32(data, offset + 16, order)) 163 sh.size = u64(read_u32(data, offset + 20, order)) 164 sh.link = read_u32(data, offset + 24, order) 165 sh.info = read_u32(data, offset + 28, order) 166 sh.addralign = u64(read_u32(data, offset + 32, order)) 167 sh.entsize = u64(read_u32(data, offset + 36, order)) 168 } 169 return sh 170 } 171 172 parse_section_headers :: proc(data: []u8, info: ^ELF_Info) { 173 header := info.header 174 is_64bit := info.is_64bit 175 order := byte_order(info.header) 176 177 // e_shoff == 0 means no section header table, so shnum == 0 is not the 178 // SHN_XINDEX escape hatch and section[0] would be the ELF header itself. 179 if header.shoff == 0 { return } 180 if !in_bounds(data, header.shoff, 0) { return } 181 182 min_size := SHDR_SIZE_64 if is_64bit else SHDR_SIZE_32 183 entsize := int(header.shentsize) 184 if entsize < min_size { entsize = min_size } 185 186 start := int(header.shoff) 187 shnum := u64(header.shnum) 188 shstrndx := u64(header.shstrndx) 189 190 // e_shnum == 0 and e_shstrndx == SHN_XINDEX are escape hatches: the real 191 // values live in section[0].sh_size and section[0].sh_link. 192 if shnum == 0 || shstrndx == SHN_XINDEX { 193 if start + min_size > len(data) { return } 194 sh0 := read_section_header(data, start, is_64bit, order) 195 if shnum == 0 { shnum = sh0.size } 196 if shstrndx == SHN_XINDEX { shstrndx = u64(sh0.link) } 197 } 198 199 offset := start 200 for i := u64(0); i < shnum; i += 1 { 201 if offset + min_size > len(data) { break } 202 append(&info.section_hdrs, read_section_header(data, offset, is_64bit, order)) 203 offset += entsize 204 } 205 206 if shstrndx < u64(len(info.section_hdrs)) { 207 shstrtab_hdr := info.section_hdrs[shstrndx] 208 if shstrtab_hdr.type != SHT_NULL && in_bounds(data, shstrtab_hdr.offset, shstrtab_hdr.size) { 209 s := int(shstrtab_hdr.offset) 210 info.shstrtab = data[s : s + int(shstrtab_hdr.size)] 211 } 212 } 213 } 214 215 parse_symbols :: proc(data: []u8, info: ^ELF_Info) { 216 for shdr in info.section_hdrs { 217 if shdr.type != SHT_SYMTAB && shdr.type != SHT_DYNSYM { 218 continue 219 } 220 strtab_idx := int(shdr.link) 221 if strtab_idx >= len(info.section_hdrs) { continue } 222 223 strtab_hdr := info.section_hdrs[strtab_idx] 224 if !in_bounds(data, strtab_hdr.offset, strtab_hdr.size) { continue } 225 if !in_bounds(data, shdr.offset, shdr.size) { continue } 226 227 strtab_start := int(strtab_hdr.offset) 228 strtab := data[strtab_start : strtab_start + int(strtab_hdr.size)] 229 230 if shdr.type == SHT_DYNSYM { 231 info.dynstr = strtab 232 } else { 233 info.strtab = strtab 234 } 235 236 is_64bit := info.is_64bit 237 order := byte_order(info.header) 238 offset := int(shdr.offset) 239 240 min_size := u64(SYM_SIZE_64) if is_64bit else u64(SYM_SIZE_32) 241 entsize := shdr.entsize 242 if entsize < min_size { entsize = min_size } 243 244 num_symbols := shdr.size / entsize 245 for j := u64(0); j < num_symbols; j += 1 { 246 sym: Symbol 247 name_off: u32 248 sym_info: u8 249 next := offset + int(entsize) 250 251 if is_64bit { 252 if offset + SYM_SIZE_64 > len(data) { break } 253 name_off = read_u32(data, offset, order) 254 sym_info = data[offset + 4] 255 sym.shndx = read_u16(data, offset + 6, order) 256 sym.value = read_u64(data, offset + 8, order) 257 sym.size = read_u64(data, offset + 16, order) 258 } else { 259 if offset + SYM_SIZE_32 > len(data) { break } 260 name_off = read_u32(data, offset, order) 261 sym.value = u64(read_u32(data, offset + 4, order)) 262 sym.size = u64(read_u32(data, offset + 8, order)) 263 sym_info = data[offset + 12] 264 sym.shndx = read_u16(data, offset + 14, order) 265 } 266 offset = next 267 268 sym.name = get_symbol_name(strtab, name_off) 269 sym.bind = sym_info >> 4 270 sym.type = sym_info & 0xf 271 272 if shdr.type == SHT_DYNSYM { 273 append(&info.dyn_symbols, sym) 274 } else { 275 append(&info.symbols, sym) 276 } 277 } 278 } 279 }