draugr~ single ELF file analysis tool |
| git clone https://github.com/djnnvx/draugr.git |
| log | files | refs | archive | README |
dynamic.odin (6353B)
1 package elf 2 3 import "core:fmt" 4 5 DT_NULL :: 0 6 DT_NEEDED :: 1 7 DT_PLTRELSZ :: 2 8 DT_PLTGOT :: 3 9 DT_HASH :: 4 10 DT_STRTAB :: 5 11 DT_SYMTAB :: 6 12 DT_RELA :: 7 13 DT_RELASZ :: 8 14 DT_RELAENT :: 9 15 DT_STRSZ :: 10 16 DT_SYMENT :: 11 17 DT_INIT :: 12 18 DT_FINI :: 13 19 DT_SONAME :: 14 20 DT_RPATH :: 15 21 DT_SYMBOLIC :: 16 22 DT_REL :: 17 23 DT_RELSZ :: 18 24 DT_RELENT :: 19 25 DT_PLTREL :: 20 26 DT_DEBUG :: 21 27 DT_TEXTREL :: 22 28 DT_JMPREL :: 23 29 DT_BIND_NOW :: 24 30 DT_INIT_ARRAY :: 25 31 DT_FINI_ARRAY :: 26 32 DT_INIT_ARRAYSZ :: 27 33 DT_FINI_ARRAYSZ :: 28 34 DT_RUNPATH :: 29 35 DT_FLAGS :: 30 36 37 DT_PREINIT_ARRAY :: 32 38 DT_PREINIT_ARRAYSZ :: 33 39 DT_SYMTAB_SHNDX :: 34 40 DT_RELRSZ :: 35 41 DT_RELR :: 36 42 DT_RELRENT :: 37 43 44 DT_GNU_HASH :: 0x6ffffef5 45 DT_TLSDESC_PLT :: 0x6ffffef6 46 DT_TLSDESC_GOT :: 0x6ffffef7 47 DT_VERSYM :: 0x6ffffff0 48 DT_RELACOUNT :: 0x6ffffff9 49 DT_RELCOUNT :: 0x6ffffffa 50 DT_FLAGS_1 :: 0x6ffffffb 51 DT_VERDEF :: 0x6ffffffc 52 DT_VERDEFNUM :: 0x6ffffffd 53 DT_VERNEED :: 0x6ffffffe 54 DT_VERNEEDNUM :: 0x6fffffff 55 DT_AUXILIARY :: 0x7ffffffd 56 DT_FILTER :: 0x7fffffff 57 58 DF_TEXTREL :: 0x4 59 DF_BIND_NOW :: 0x8 60 61 DF_1_NOW :: 0x1 62 DF_1_PIE :: 0x08000000 63 64 DYN_SIZE_64 :: 16 65 DYN_SIZE_32 :: 8 66 67 Dyn_Entry :: struct { 68 tag : u64, 69 val : u64, 70 } 71 72 dyn_tag_str :: proc(tag: u64) -> string { 73 switch tag { 74 case DT_NULL: return "NULL" 75 case DT_NEEDED: return "NEEDED" 76 case DT_PLTRELSZ: return "PLTRELSZ" 77 case DT_PLTGOT: return "PLTGOT" 78 case DT_HASH: return "HASH" 79 case DT_STRTAB: return "STRTAB" 80 case DT_SYMTAB: return "SYMTAB" 81 case DT_RELA: return "RELA" 82 case DT_RELASZ: return "RELASZ" 83 case DT_RELAENT: return "RELAENT" 84 case DT_STRSZ: return "STRSZ" 85 case DT_SYMENT: return "SYMENT" 86 case DT_INIT: return "INIT" 87 case DT_FINI: return "FINI" 88 case DT_SONAME: return "SONAME" 89 case DT_RPATH: return "RPATH" 90 case DT_SYMBOLIC: return "SYMBOLIC" 91 case DT_REL: return "REL" 92 case DT_RELSZ: return "RELSZ" 93 case DT_RELENT: return "RELENT" 94 case DT_PLTREL: return "PLTREL" 95 case DT_DEBUG: return "DEBUG" 96 case DT_TEXTREL: return "TEXTREL" 97 case DT_JMPREL: return "JMPREL" 98 case DT_BIND_NOW: return "BIND_NOW" 99 case DT_INIT_ARRAY: return "INIT_ARRAY" 100 case DT_FINI_ARRAY: return "FINI_ARRAY" 101 case DT_INIT_ARRAYSZ: return "INIT_ARRAYSZ" 102 case DT_FINI_ARRAYSZ: return "FINI_ARRAYSZ" 103 case DT_RUNPATH: return "RUNPATH" 104 case DT_FLAGS: return "FLAGS" 105 case DT_PREINIT_ARRAY: return "PREINIT_ARRAY" 106 case DT_PREINIT_ARRAYSZ: return "PREINIT_ARRAYSZ" 107 case DT_SYMTAB_SHNDX: return "SYMTAB_SHNDX" 108 case DT_RELRSZ: return "RELRSZ" 109 case DT_RELR: return "RELR" 110 case DT_RELRENT: return "RELRENT" 111 case DT_GNU_HASH: return "GNU_HASH" 112 case DT_TLSDESC_PLT: return "TLSDESC_PLT" 113 case DT_TLSDESC_GOT: return "TLSDESC_GOT" 114 case DT_VERSYM: return "VERSYM" 115 case DT_RELACOUNT: return "RELACOUNT" 116 case DT_RELCOUNT: return "RELCOUNT" 117 case DT_FLAGS_1: return "FLAGS_1" 118 case DT_VERDEF: return "VERDEF" 119 case DT_VERDEFNUM: return "VERDEFNUM" 120 case DT_VERNEED: return "VERNEED" 121 case DT_VERNEEDNUM: return "VERNEEDNUM" 122 case DT_AUXILIARY: return "AUXILIARY" 123 case DT_FILTER: return "FILTER" 124 } 125 return fmt.tprintf("0x%x", tag) 126 } 127 128 dyn_is_string_tag :: proc(tag: u64) -> bool { 129 switch tag { 130 case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH, DT_AUXILIARY, DT_FILTER: 131 return true 132 } 133 return false 134 } 135 136 dyn_string :: proc(info: ^ELF_Info, e: Dyn_Entry) -> string { 137 if !dyn_is_string_tag(e.tag) || info.dynstr == nil || e.val > u64(max(u32)) { 138 return "" 139 } 140 return get_symbol_name(info.dynstr, u32(e.val)) 141 } 142 143 vaddr_to_offset :: proc(info: ^ELF_Info, vaddr: u64) -> (u64, bool) { 144 for ph in info.program_hdrs { 145 if ph.type != PT_LOAD || ph.filesz == 0 { 146 continue 147 } 148 if vaddr >= ph.vaddr && vaddr - ph.vaddr < ph.filesz { 149 return ph.offset + (vaddr - ph.vaddr), true 150 } 151 } 152 return 0, false 153 } 154 155 dyn_val :: proc(info: ^ELF_Info, tag: u64) -> (u64, bool) { 156 for e in info.dyn_entries { 157 if e.tag == tag { 158 return e.val, true 159 } 160 } 161 return 0, false 162 } 163 164 parse_dynamic :: proc(data: []u8, info: ^ELF_Info) { 165 offset, size, ok := find_dynamic(info) 166 if !ok || !in_bounds(data, offset, size) { 167 return 168 } 169 170 order := byte_order(info.header) 171 entsize := u64(DYN_SIZE_64) if info.is_64bit else u64(DYN_SIZE_32) 172 173 for pos := offset; pos + entsize <= offset + size; pos += entsize { 174 e: Dyn_Entry 175 if info.is_64bit { 176 e.tag = read_u64(data, int(pos), order) 177 e.val = read_u64(data, int(pos + 8), order) 178 } else { 179 e.tag = u64(read_u32(data, int(pos), order)) 180 e.val = u64(read_u32(data, int(pos + 4), order)) 181 } 182 if e.tag == DT_NULL { 183 break 184 } 185 append(&info.dyn_entries, e) 186 } 187 188 resolve_dynamic_strings(data, info) 189 } 190 191 find_dynamic :: proc(info: ^ELF_Info) -> (offset: u64, size: u64, ok: bool) { 192 for ph in info.program_hdrs { 193 if ph.type == PT_DYNAMIC && ph.filesz > 0 { 194 return ph.offset, ph.filesz, true 195 } 196 } 197 for shdr in info.section_hdrs { 198 if shdr.type == SHT_DYNAMIC && shdr.size > 0 { 199 return shdr.offset, shdr.size, true 200 } 201 } 202 return 0, 0, false 203 } 204 205 // DT_STRTAB is a vaddr, so this also works when section headers are stripped. 206 dynstr_from_dynamic :: proc(data: []u8, info: ^ELF_Info) -> []u8 { 207 strtab_va, has_va := dyn_val(info, DT_STRTAB) 208 strsz, has_sz := dyn_val(info, DT_STRSZ) 209 if !has_va || !has_sz { 210 return nil 211 } 212 off, ok := vaddr_to_offset(info, strtab_va) 213 if !ok || !in_bounds(data, off, strsz) { 214 return nil 215 } 216 return data[off : off + strsz] 217 } 218 219 resolve_dynamic_strings :: proc(data: []u8, info: ^ELF_Info) { 220 strtab := dynstr_from_dynamic(data, info) 221 if strtab == nil { 222 strtab = info.dynstr 223 } 224 if strtab == nil { 225 return 226 } 227 info.dynstr = strtab 228 229 for e in info.dyn_entries { 230 if e.val > u64(max(u32)) { 231 continue 232 } 233 name := get_symbol_name(strtab, u32(e.val)) 234 switch e.tag { 235 case DT_NEEDED: append(&info.needed, name) 236 case DT_SONAME: info.soname = name 237 case DT_RPATH: info.rpath = name 238 case DT_RUNPATH: info.runpath = name 239 } 240 } 241 }