draugr.git


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 }