draugr.git


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 }