draugr.git


draugr

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

elf.odin (6104B)

      1 package elf
      2 
      3 import "core:encoding/endian"
      4 import "core:fmt"
      5 
      6 ELF_MAGIC :: [4]u8{0x7f, 'E', 'L', 'F'}
      7 
      8 ELF_CLASS_32 :: 1
      9 ELF_CLASS_64 :: 2
     10 
     11 ELF_DATA_LSB :: 1
     12 ELF_DATA_MSB :: 2
     13 
     14 ET_REL  :: 1
     15 ET_EXEC :: 2
     16 ET_DYN  :: 3
     17 ET_CORE :: 4
     18 
     19 EM_SPARC        :: 2
     20 EM_386          :: 3
     21 EM_68K          :: 4
     22 EM_MIPS         :: 8
     23 EM_MIPS_RS3_LE  :: 10
     24 EM_PARISC       :: 15
     25 EM_PPC          :: 20
     26 EM_PPC64        :: 21
     27 EM_S390         :: 22
     28 EM_ARM          :: 40
     29 EM_SH           :: 42
     30 EM_SPARCV9      :: 43
     31 EM_ARC          :: 45
     32 EM_IA_64        :: 50
     33 EM_X86_64       :: 62
     34 EM_CRIS         :: 76
     35 EM_AVR          :: 83
     36 EM_FR30         :: 84
     37 EM_V850         :: 87
     38 EM_M32R         :: 88
     39 EM_OPENRISC     :: 92
     40 EM_ARC_COMPACT  :: 93
     41 EM_XTENSA       :: 94
     42 EM_MSP430       :: 105
     43 EM_BLACKFIN     :: 106
     44 EM_UNICORE      :: 110
     45 EM_TI_C6000     :: 140
     46 EM_NDS32        :: 167
     47 EM_AARCH64      :: 183
     48 EM_MICROBLAZE   :: 189
     49 EM_TILEGX       :: 191
     50 EM_ARC_COMPACT2 :: 195
     51 EM_RISCV        :: 243
     52 EM_BPF          :: 247
     53 EM_CSKY         :: 252
     54 EM_LOONGARCH    :: 258
     55 
     56 PT_NULL    :: 0
     57 PT_LOAD    :: 1
     58 PT_DYNAMIC :: 2
     59 PT_INTERP  :: 3
     60 PT_NOTE    :: 4
     61 PT_SHLIB   :: 5
     62 PT_PHDR    :: 6
     63 PT_TLS     :: 7
     64 PT_GNU_EH_FRAME :: 0x6474e550
     65 PT_GNU_STACK    :: 0x6474e551
     66 PT_GNU_RELRO    :: 0x6474e552
     67 PT_GNU_PROPERTY :: 0x6474e553
     68 
     69 PF_X :: 1
     70 PF_W :: 2
     71 PF_R :: 4
     72 
     73 SHF_WRITE     :: 1
     74 SHF_ALLOC     :: 2
     75 SHF_EXECINSTR :: 4
     76 
     77 SHT_NULL     :: 0
     78 SHT_PROGBITS :: 1
     79 SHT_SYMTAB   :: 2
     80 SHT_STRTAB   :: 3
     81 SHT_RELA     :: 4
     82 SHT_HASH     :: 5
     83 SHT_DYNAMIC  :: 6
     84 SHT_NOTE     :: 7
     85 SHT_NOBITS   :: 8
     86 SHT_REL      :: 9
     87 SHT_SHLIB    :: 10
     88 SHT_DYNSYM   :: 11
     89 SHT_INIT_ARRAY    :: 14
     90 SHT_FINI_ARRAY    :: 15
     91 SHT_PREINIT_ARRAY :: 16
     92 SHT_GROUP         :: 17
     93 SHT_SYMTAB_SHNDX  :: 18
     94 SHT_RELR          :: 19
     95 SHT_GNU_ATTRIBUTES :: 0x6ffffff5
     96 SHT_GNU_HASH       :: 0x6ffffff6
     97 SHT_GNU_VERDEF     :: 0x6ffffffd
     98 SHT_GNU_VERNEED    :: 0x6ffffffe
     99 SHT_GNU_VERSYM     :: 0x6fffffff
    100 
    101 SHN_UNDEF  :: 0
    102 SHN_XINDEX :: 0xFFFF
    103 PN_XNUM    :: 0xFFFF
    104 
    105 STB_LOCAL  :: 0
    106 STB_GLOBAL :: 1
    107 STB_WEAK   :: 2
    108 
    109 STT_NOTYPE  :: 0
    110 STT_OBJECT  :: 1
    111 STT_FUNC    :: 2
    112 STT_SECTION :: 3
    113 STT_FILE    :: 4
    114 
    115 ELF_Header :: struct {
    116 	class        : u8,
    117 	data         : u8,
    118 	osabi        : u8,
    119 	type         : u16,
    120 	machine      : u16,
    121 	entry        : u64,
    122 	phoff        : u64,
    123 	shoff        : u64,
    124 	phentsize    : u16,
    125 	phnum        : u16,
    126 	shentsize    : u16,
    127 	shnum        : u16,
    128 	shstrndx     : u16,
    129 	flags        : u32,
    130 }
    131 
    132 Program_Header :: struct {
    133 	type    : u32,
    134 	flags   : u64,
    135 	offset  : u64,
    136 	vaddr   : u64,
    137 	paddr   : u64,
    138 	filesz  : u64,
    139 	memsz   : u64,
    140 	align   : u64,
    141 }
    142 
    143 Section_Header :: struct {
    144 	name      : u32,
    145 	type      : u32,
    146 	flags     : u64,
    147 	addr      : u64,
    148 	offset    : u64,
    149 	size      : u64,
    150 	link      : u32,
    151 	info      : u32,
    152 	addralign : u64,
    153 	entsize   : u64,
    154 }
    155 
    156 Symbol :: struct {
    157 	name  : string,
    158 	value : u64,
    159 	size  : u64,
    160 	bind  : u8,
    161 	type  : u8,
    162 	shndx : u16,
    163 }
    164 
    165 ELF_Info :: struct {
    166 	header        : ELF_Header,
    167 	program_hdrs  : [dynamic]Program_Header,
    168 	section_hdrs  : [dynamic]Section_Header,
    169 	symbols       : [dynamic]Symbol,
    170 	dyn_symbols   : [dynamic]Symbol,
    171 	dyn_entries   : [dynamic]Dyn_Entry,
    172 	needed        : [dynamic]string,
    173 	relocs        : [dynamic]Reloc,
    174 	soname        : string,
    175 	rpath         : string,
    176 	runpath       : string,
    177 	is_64bit      : bool,
    178 	is_pie        : bool,
    179 	is_dynamic    : bool,
    180 	interp_offset : u64,
    181 	shstrtab      : []u8,
    182 	strtab        : []u8,
    183 	dynstr        : []u8,
    184 }
    185 
    186 // Overflow-safe: no addition that can wrap.
    187 in_bounds :: proc(data: []u8, offset: u64, size: u64) -> bool {
    188 	return offset <= u64(len(data)) && size <= u64(len(data)) - offset
    189 }
    190 
    191 Byte_Order :: endian.Byte_Order
    192 
    193 byte_order :: proc(header: ELF_Header) -> Byte_Order {
    194 	return .Little if header.data == ELF_DATA_LSB else .Big
    195 }
    196 
    197 // Out-of-range reads yield 0 rather than faulting; ELF fields are untrusted.
    198 read_u16 :: proc(data: []u8, offset: int, order: Byte_Order) -> u16 {
    199 	if offset < 0 || offset + 2 > len(data) { return 0 }
    200 	v, _ := endian.get_u16(data[offset:offset + 2], order)
    201 	return v
    202 }
    203 
    204 read_u32 :: proc(data: []u8, offset: int, order: Byte_Order) -> u32 {
    205 	if offset < 0 || offset + 4 > len(data) { return 0 }
    206 	v, _ := endian.get_u32(data[offset:offset + 4], order)
    207 	return v
    208 }
    209 
    210 read_u64 :: proc(data: []u8, offset: int, order: Byte_Order) -> u64 {
    211 	if offset < 0 || offset + 8 > len(data) { return 0 }
    212 	v, _ := endian.get_u64(data[offset:offset + 8], order)
    213 	return v
    214 }
    215 
    216 probe :: proc(data: []u8) -> bool {
    217 	if len(data) < 16 { return false }
    218 	if data[0] != ELF_MAGIC[0] { return false }
    219 	if data[1] != ELF_MAGIC[1] { return false }
    220 	if data[2] != ELF_MAGIC[2] { return false }
    221 	if data[3] != ELF_MAGIC[3] { return false }
    222 	return true
    223 }
    224 
    225 parse_header :: proc(data: []u8) -> (ELF_Header, string) {
    226 	header: ELF_Header
    227 	
    228 	if len(data) < 16 {
    229 		return header, "Data too small"
    230 	}
    231 	
    232 	if !probe(data) {
    233 		return header, "Invalid ELF magic"
    234 	}
    235 	
    236 	header.class = data[4]
    237 	header.data = data[5]
    238 	header.osabi = data[7]
    239 	
    240 	is_64bit := header.class == ELF_CLASS_64
    241 	order := byte_order(header)
    242 
    243 	if !is_64bit {
    244 		if header.class != ELF_CLASS_32 {
    245 			return header, fmt.tprintf("Unknown ELF class: %d", header.class)
    246 		}
    247 	}
    248 
    249 	header.type = read_u16(data, 16, order)
    250 	header.machine = read_u16(data, 18, order)
    251 
    252 	if is_64bit {
    253 		header.entry = read_u64(data, 24, order)
    254 		header.phoff = read_u64(data, 32, order)
    255 		header.shoff = read_u64(data, 40, order)
    256 		header.flags = read_u32(data, 48, order)
    257 		header.phentsize = read_u16(data, 54, order)
    258 		header.phnum = read_u16(data, 56, order)
    259 		header.shentsize = read_u16(data, 58, order)
    260 		header.shnum = read_u16(data, 60, order)
    261 		header.shstrndx = read_u16(data, 62, order)
    262 	} else {
    263 		header.entry = u64(read_u32(data, 24, order))
    264 		header.phoff = u64(read_u32(data, 28, order))
    265 		header.shoff = u64(read_u32(data, 32, order))
    266 		header.flags = read_u32(data, 36, order)
    267 		header.phentsize = read_u16(data, 42, order)
    268 		header.phnum = read_u16(data, 44, order)
    269 		header.shentsize = read_u16(data, 46, order)
    270 		header.shnum = read_u16(data, 48, order)
    271 		header.shstrndx = read_u16(data, 50, order)
    272 	}
    273 
    274 	return header, ""
    275 }