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 }