draugr.git


draugr

~ single ELF file analysis tool
git clone https://github.com/djnnvx/draugr.git
log | files | refs | archive | README

reloc.odin (7223B)

      1 package elf
      2 
      3 import "core:fmt"
      4 
      5 Reloc_Source :: enum {
      6 	Rel,
      7 	Rela,
      8 	Relr,
      9 }
     10 
     11 Reloc :: struct {
     12 	offset   : u64,
     13 	addend   : i64,
     14 	sym      : u32,
     15 	type     : u32,
     16 	sym_name : string,
     17 	source   : string,
     18 	kind     : Reloc_Source,
     19 }
     20 
     21 REL_SIZE_64  :: 16
     22 RELA_SIZE_64 :: 24
     23 REL_SIZE_32  :: 8
     24 RELA_SIZE_32 :: 12
     25 
     26 parse_relocs :: proc(data: []u8, info: ^ELF_Info) {
     27 	if parse_relocs_from_sections(data, info) {
     28 		return
     29 	}
     30 	parse_relocs_from_dynamic(data, info)
     31 }
     32 
     33 parse_relocs_from_sections :: proc(data: []u8, info: ^ELF_Info) -> bool {
     34 	found := false
     35 	for shdr in info.section_hdrs {
     36 		kind: Reloc_Source
     37 		switch shdr.type {
     38 		case SHT_REL:  kind = .Rel
     39 		case SHT_RELA: kind = .Rela
     40 		case SHT_RELR: kind = .Relr
     41 		case:          continue
     42 		}
     43 		if !in_bounds(data, shdr.offset, shdr.size) || shdr.size == 0 {
     44 			continue
     45 		}
     46 		found = true
     47 		name := get_section_name(info, shdr)
     48 		syms := symbols_for_link(info, shdr.link)
     49 		read_reloc_table(data, info, shdr.offset, shdr.size, kind, name, syms)
     50 	}
     51 	return found
     52 }
     53 
     54 symbols_for_link :: proc(info: ^ELF_Info, link: u32) -> []Symbol {
     55 	if int(link) < len(info.section_hdrs) && info.section_hdrs[link].type == SHT_SYMTAB {
     56 		return info.symbols[:]
     57 	}
     58 	return info.dyn_symbols[:]
     59 }
     60 
     61 parse_relocs_from_dynamic :: proc(data: []u8, info: ^ELF_Info) {
     62 	syms := info.dyn_symbols[:]
     63 
     64 	if addr, ok := dyn_val(info, DT_RELA); ok {
     65 		if size, ok2 := dyn_val(info, DT_RELASZ); ok2 {
     66 			read_dynamic_table(data, info, addr, size, .Rela, "DT_RELA", syms)
     67 		}
     68 	}
     69 	if addr, ok := dyn_val(info, DT_REL); ok {
     70 		if size, ok2 := dyn_val(info, DT_RELSZ); ok2 {
     71 			read_dynamic_table(data, info, addr, size, .Rel, "DT_REL", syms)
     72 		}
     73 	}
     74 	if addr, ok := dyn_val(info, DT_RELR); ok {
     75 		if size, ok2 := dyn_val(info, DT_RELRSZ); ok2 {
     76 			read_dynamic_table(data, info, addr, size, .Relr, "DT_RELR", syms)
     77 		}
     78 	}
     79 	if addr, ok := dyn_val(info, DT_JMPREL); ok {
     80 		if size, ok2 := dyn_val(info, DT_PLTRELSZ); ok2 {
     81 			kind: Reloc_Source = .Rela
     82 			if pltrel, ok3 := dyn_val(info, DT_PLTREL); ok3 && pltrel == DT_REL {
     83 				kind = .Rel
     84 			}
     85 			read_dynamic_table(data, info, addr, size, kind, "DT_JMPREL", syms)
     86 		}
     87 	}
     88 }
     89 
     90 read_dynamic_table :: proc(data: []u8, info: ^ELF_Info, vaddr, size: u64, kind: Reloc_Source, source: string, syms: []Symbol) {
     91 	off, ok := vaddr_to_offset(info, vaddr)
     92 	if !ok || !in_bounds(data, off, size) {
     93 		return
     94 	}
     95 	read_reloc_table(data, info, off, size, kind, source, syms)
     96 }
     97 
     98 read_reloc_table :: proc(data: []u8, info: ^ELF_Info, offset, size: u64, kind: Reloc_Source, source: string, syms: []Symbol) {
     99 	if kind == .Relr {
    100 		read_relr_table(data, info, offset, size, source)
    101 		return
    102 	}
    103 
    104 	order := byte_order(info.header)
    105 	is64 := info.is_64bit
    106 
    107 	entsize := u64(0)
    108 	switch {
    109 	case kind == .Rela && is64:  entsize = RELA_SIZE_64
    110 	case kind == .Rela && !is64: entsize = RELA_SIZE_32
    111 	case kind == .Rel && is64:   entsize = REL_SIZE_64
    112 	case:                        entsize = REL_SIZE_32
    113 	}
    114 
    115 	for pos := offset; pos + entsize <= offset + size; pos += entsize {
    116 		r: Reloc
    117 		r.kind = kind
    118 		r.source = source
    119 
    120 		raw_info: u64
    121 		if is64 {
    122 			r.offset = read_u64(data, int(pos), order)
    123 			raw_info = read_u64(data, int(pos + 8), order)
    124 			r.sym = u32(raw_info >> 32)
    125 			r.type = u32(raw_info & 0xffffffff)
    126 			if kind == .Rela {
    127 				r.addend = i64(read_u64(data, int(pos + 16), order))
    128 			}
    129 		} else {
    130 			r.offset = u64(read_u32(data, int(pos), order))
    131 			raw_info = u64(read_u32(data, int(pos + 4), order))
    132 			r.sym = u32(raw_info >> 8)
    133 			r.type = u32(raw_info & 0xff)
    134 			if kind == .Rela {
    135 				r.addend = i64(i32(read_u32(data, int(pos + 8), order)))
    136 			}
    137 		}
    138 
    139 		if int(r.sym) < len(syms) {
    140 			r.sym_name = syms[r.sym].name
    141 		}
    142 		append(&info.relocs, r)
    143 	}
    144 }
    145 
    146 // RELR packs relative relocations: an even word is an address, an odd word is a
    147 // bitmap of the words that follow it.
    148 read_relr_table :: proc(data: []u8, info: ^ELF_Info, offset, size: u64, source: string) {
    149 	order := byte_order(info.header)
    150 	is64 := info.is_64bit
    151 	word := u64(8) if is64 else u64(4)
    152 	rel_type := relative_reloc_type(info.header.machine)
    153 
    154 	base := u64(0)
    155 	for pos := offset; pos + word <= offset + size; pos += word {
    156 		entry := read_u64(data, int(pos), order) if is64 else u64(read_u32(data, int(pos), order))
    157 
    158 		if entry & 1 == 0 {
    159 			append(&info.relocs, Reloc{
    160 				offset = entry, type = rel_type, source = source, kind = .Relr,
    161 			})
    162 			base = entry + word
    163 			continue
    164 		}
    165 
    166 		bits := entry >> 1
    167 		for i := u64(0); bits != 0; i += 1 {
    168 			if bits & 1 != 0 {
    169 				append(&info.relocs, Reloc{
    170 					offset = base + i * word, type = rel_type, source = source, kind = .Relr,
    171 				})
    172 			}
    173 			bits >>= 1
    174 		}
    175 		base += (word * 8 - 1) * word
    176 	}
    177 }
    178 
    179 R_X86_64_RELATIVE  :: 8
    180 R_386_RELATIVE     :: 8
    181 R_AARCH64_RELATIVE :: 1027
    182 R_ARM_RELATIVE     :: 23
    183 R_RISCV_RELATIVE   :: 3
    184 
    185 relative_reloc_type :: proc(machine: u16) -> u32 {
    186 	switch machine {
    187 	case EM_X86_64:  return R_X86_64_RELATIVE
    188 	case EM_386:     return R_386_RELATIVE
    189 	case EM_AARCH64: return R_AARCH64_RELATIVE
    190 	case EM_ARM:     return R_ARM_RELATIVE
    191 	case EM_RISCV:   return R_RISCV_RELATIVE
    192 	}
    193 	return 0
    194 }
    195 
    196 is_jump_slot :: proc(machine: u16, type: u32) -> bool {
    197 	switch machine {
    198 	case EM_X86_64:  return type == 7
    199 	case EM_386:     return type == 7
    200 	case EM_AARCH64: return type == 1026
    201 	case EM_ARM:     return type == 22
    202 	case EM_RISCV:   return type == 5
    203 	}
    204 	return false
    205 }
    206 
    207 reloc_type_str :: proc(machine: u16, type: u32) -> string {
    208 	switch machine {
    209 	case EM_X86_64:
    210 		switch type {
    211 		case 0:  return "R_X86_64_NONE"
    212 		case 1:  return "R_X86_64_64"
    213 		case 2:  return "R_X86_64_PC32"
    214 		case 3:  return "R_X86_64_GOT32"
    215 		case 4:  return "R_X86_64_PLT32"
    216 		case 5:  return "R_X86_64_COPY"
    217 		case 6:  return "R_X86_64_GLOB_DAT"
    218 		case 7:  return "R_X86_64_JUMP_SLOT"
    219 		case 8:  return "R_X86_64_RELATIVE"
    220 		case 9:  return "R_X86_64_GOTPCREL"
    221 		case 16: return "R_X86_64_DTPMOD64"
    222 		case 17: return "R_X86_64_DTPOFF64"
    223 		case 18: return "R_X86_64_TPOFF64"
    224 		case 37: return "R_X86_64_IRELATIVE"
    225 		case 41: return "R_X86_64_TLSDESC"
    226 		}
    227 	case EM_386:
    228 		switch type {
    229 		case 0:  return "R_386_NONE"
    230 		case 1:  return "R_386_32"
    231 		case 2:  return "R_386_PC32"
    232 		case 5:  return "R_386_COPY"
    233 		case 6:  return "R_386_GLOB_DAT"
    234 		case 7:  return "R_386_JMP_SLOT"
    235 		case 8:  return "R_386_RELATIVE"
    236 		case 42: return "R_386_IRELATIVE"
    237 		}
    238 	case EM_AARCH64:
    239 		switch type {
    240 		case 0:    return "R_AARCH64_NONE"
    241 		case 257:  return "R_AARCH64_ABS64"
    242 		case 1024: return "R_AARCH64_COPY"
    243 		case 1025: return "R_AARCH64_GLOB_DAT"
    244 		case 1026: return "R_AARCH64_JUMP_SLOT"
    245 		case 1027: return "R_AARCH64_RELATIVE"
    246 		case 1032: return "R_AARCH64_IRELATIVE"
    247 		}
    248 	case EM_ARM:
    249 		switch type {
    250 		case 0:   return "R_ARM_NONE"
    251 		case 2:   return "R_ARM_ABS32"
    252 		case 20:  return "R_ARM_COPY"
    253 		case 21:  return "R_ARM_GLOB_DAT"
    254 		case 22:  return "R_ARM_JUMP_SLOT"
    255 		case 23:  return "R_ARM_RELATIVE"
    256 		case 160: return "R_ARM_IRELATIVE"
    257 		}
    258 	case EM_RISCV:
    259 		switch type {
    260 		case 0:  return "R_RISCV_NONE"
    261 		case 1:  return "R_RISCV_32"
    262 		case 2:  return "R_RISCV_64"
    263 		case 3:  return "R_RISCV_RELATIVE"
    264 		case 4:  return "R_RISCV_COPY"
    265 		case 5:  return "R_RISCV_JUMP_SLOT"
    266 		case 58: return "R_RISCV_IRELATIVE"
    267 		}
    268 	}
    269 	return fmt.tprintf("%d", type)
    270 }