draugr.git


draugr

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

reloc_test.odin (3593B)

      1 package elf
      2 
      3 import "core:testing"
      4 
      5 build_relr_elf :: proc(is64: bool, little: bool, words: []u64) -> []u8 {
      6 	dyn_off  :: 0x100
      7 	relr_off :: 0x200
      8 	total    :: 0x300
      9 
     10 	ph_off   := 64 if is64 else 52
     11 	ph_size  := PHDR_SIZE_64 if is64 else PHDR_SIZE_32
     12 	word     := 8 if is64 else 4
     13 
     14 	data := make([]u8, total)
     15 	data[0] = 0x7f
     16 	data[1] = 'E'
     17 	data[2] = 'L'
     18 	data[3] = 'F'
     19 	data[4] = ELF_CLASS_64 if is64 else ELF_CLASS_32
     20 	data[5] = ELF_DATA_LSB if little else ELF_DATA_MSB
     21 	data[6] = 1
     22 
     23 	put_u16(data, 16, ET_DYN, little)
     24 	put_u16(data, 18, EM_X86_64 if is64 else EM_ARM, little)
     25 
     26 	if is64 {
     27 		put_u64(data, 32, u64(ph_off), little)
     28 		put_u16(data, 54, u16(ph_size), little)
     29 		put_u16(data, 56, 2, little)
     30 	} else {
     31 		put_u32(data, 28, u32(ph_off), little)
     32 		put_u16(data, 42, u16(ph_size), little)
     33 		put_u16(data, 44, 2, little)
     34 	}
     35 
     36 	write_phdr(data, ph_off, is64, little, Program_Header{
     37 		type = PT_LOAD, offset = 0, vaddr = DYN_BASE,
     38 		filesz = total, memsz = total, flags = PF_R | PF_X, align = 0x1000,
     39 	})
     40 
     41 	write_phdr(data, ph_off + ph_size, is64, little, Program_Header{
     42 		type = PT_DYNAMIC, offset = dyn_off, vaddr = DYN_BASE + dyn_off,
     43 		filesz = 64, memsz = 64, flags = PF_R, align = 8,
     44 	})
     45 
     46 	ent := 16 if is64 else 8
     47 	dyn := [][2]u64{
     48 		{DT_RELR,    DYN_BASE + relr_off},
     49 		{DT_RELRSZ,  u64(len(words) * word)},
     50 		{DT_RELRENT, u64(word)},
     51 		{DT_NULL,    0},
     52 	}
     53 	pos := dyn_off
     54 	for e in dyn {
     55 		if is64 {
     56 			put_u64(data, pos, e[0], little)
     57 			put_u64(data, pos + 8, e[1], little)
     58 		} else {
     59 			put_u32(data, pos, u32(e[0]), little)
     60 			put_u32(data, pos + 4, u32(e[1]), little)
     61 		}
     62 		pos += ent
     63 	}
     64 
     65 	pos = relr_off
     66 	for w in words {
     67 		if is64 {
     68 			put_u64(data, pos, w, little)
     69 		} else {
     70 			put_u32(data, pos, u32(w), little)
     71 		}
     72 		pos += word
     73 	}
     74 	return data
     75 }
     76 
     77 @(test)
     78 test_relr_decodes_address_then_bitmap :: proc(t: ^testing.T) {
     79 	words := []u64{0x3da0, 0x03, 0x8001}
     80 	data := build_relr_elf(true, true, words)
     81 	defer delete(data)
     82 
     83 	info, _ := load(data)
     84 	defer destroy(&info)
     85 
     86 	testing.expect_value(t, len(info.relocs), 3)
     87 	testing.expect_value(t, info.relocs[0].offset, 0x3da0)
     88 	testing.expect_value(t, info.relocs[1].offset, 0x3da8)
     89 	testing.expect_value(t, info.relocs[2].offset, 0x4010)
     90 
     91 	for r in info.relocs {
     92 		testing.expect_value(t, r.type, u32(R_X86_64_RELATIVE))
     93 		testing.expect_value(t, r.kind, Reloc_Source.Relr)
     94 	}
     95 }
     96 
     97 @(test)
     98 test_relr_empty_bitmap_emits_nothing :: proc(t: ^testing.T) {
     99 	words := []u64{0x1000, 0x01}
    100 	data := build_relr_elf(true, true, words)
    101 	defer delete(data)
    102 
    103 	info, _ := load(data)
    104 	defer destroy(&info)
    105 
    106 	testing.expect_value(t, len(info.relocs), 1)
    107 	testing.expect_value(t, info.relocs[0].offset, 0x1000)
    108 }
    109 
    110 @(test)
    111 test_relr_big_endian :: proc(t: ^testing.T) {
    112 	words := []u64{0x2000, 0x07}
    113 	data := build_relr_elf(true, false, words)
    114 	defer delete(data)
    115 
    116 	info, _ := load(data)
    117 	defer destroy(&info)
    118 
    119 	testing.expect_value(t, len(info.relocs), 3)
    120 	testing.expect_value(t, info.relocs[0].offset, 0x2000)
    121 	testing.expect_value(t, info.relocs[1].offset, 0x2008)
    122 	testing.expect_value(t, info.relocs[2].offset, 0x2010)
    123 }
    124 
    125 @(test)
    126 test_reloc_info_split_differs_by_class :: proc(t: ^testing.T) {
    127 	testing.expect_value(t, reloc_type_str(EM_X86_64, 7), "R_X86_64_JUMP_SLOT")
    128 	testing.expect_value(t, reloc_type_str(EM_AARCH64, 1026), "R_AARCH64_JUMP_SLOT")
    129 	testing.expect_value(t, reloc_type_str(EM_X86_64, 999), "999")
    130 
    131 	testing.expect(t, is_jump_slot(EM_X86_64, 7), "x86-64 JUMP_SLOT")
    132 	testing.expect(t, is_jump_slot(EM_AARCH64, 1026), "aarch64 JUMP_SLOT")
    133 	testing.expect(t, !is_jump_slot(EM_X86_64, 8), "RELATIVE is not a jump slot")
    134 }