draugr~ single ELF file analysis tool |
| git clone https://github.com/djnnvx/draugr.git |
| log | files | refs | archive | README |
elf_test.odin (41776B)
1 package elf 2 3 import "core:mem" 4 import "core:testing" 5 6 make_elf64_header :: proc(data: []u8, etype: u16, machine: u16) { 7 data[0] = 0x7f 8 data[1] = 0x45 9 data[2] = 0x4c 10 data[3] = 0x46 11 data[4] = ELF_CLASS_64 12 data[5] = ELF_DATA_LSB 13 data[6] = 1 14 data[7] = 0 15 16 data[16] = u8(etype) 17 data[17] = u8(etype >> 8) 18 data[18] = u8(machine) 19 data[19] = u8(machine >> 8) 20 data[24] = 0x00 21 data[25] = 0x00 22 data[26] = 0x40 23 data[27] = 0x00 24 data[32] = 64 25 data[33] = 0 26 data[52] = 64 27 data[53] = 0 28 data[54] = 56 29 data[55] = 0 30 data[58] = 64 31 data[59] = 0 32 } 33 34 @(test) 35 test_probe_elf64 :: proc(t: ^testing.T) { 36 data := make([]u8, 64) 37 defer delete(data) 38 make_elf64_header(data, ET_EXEC, EM_X86_64) 39 40 result := probe(data) 41 testing.expect(t, result == true, "should recognize ELF64 header") 42 } 43 44 @(test) 45 test_probe_elf32 :: proc(t: ^testing.T) { 46 data := make([]u8, 52) 47 defer delete(data) 48 49 data[0] = 0x7f 50 data[1] = 0x45 51 data[2] = 0x4c 52 data[3] = 0x46 53 data[4] = ELF_CLASS_32 54 data[5] = ELF_DATA_LSB 55 56 result := probe(data) 57 testing.expect(t, result == true, "should recognize ELF32 header") 58 } 59 60 @(test) 61 test_probe_invalid_magic :: proc(t: ^testing.T) { 62 data := []u8{0x00, 0x01, 0x02, 0x03} 63 64 result := probe(data) 65 testing.expect(t, result == false, "should reject invalid magic") 66 } 67 68 @(test) 69 test_probe_too_small :: proc(t: ^testing.T) { 70 data := []u8{0x7f, 0x45, 0x4c} 71 72 result := probe(data) 73 testing.expect(t, result == false, "should reject data smaller than 16 bytes") 74 } 75 76 @(test) 77 test_parse_header_class_64 :: proc(t: ^testing.T) { 78 data := make([]u8, 64) 79 defer delete(data) 80 make_elf64_header(data, ET_EXEC, EM_X86_64) 81 82 header, err := parse_header(data) 83 testing.expect(t, err == "", "parse should succeed") 84 testing.expect(t, header.class == ELF_CLASS_64, "class should be 64") 85 } 86 87 @(test) 88 test_parse_header_class_32 :: proc(t: ^testing.T) { 89 data := make([]u8, 52) 90 defer delete(data) 91 92 data[0] = 0x7f 93 data[1] = 0x45 94 data[2] = 0x4c 95 data[3] = 0x46 96 data[4] = ELF_CLASS_32 97 data[5] = ELF_DATA_LSB 98 data[16] = u8(ET_EXEC) 99 data[17] = 0 100 data[18] = u8(EM_386) 101 data[19] = 0 102 103 header, err := parse_header(data) 104 testing.expect(t, err == "", "parse should succeed") 105 testing.expect(t, header.class == ELF_CLASS_32, "class should be 32") 106 } 107 108 @(test) 109 test_parse_header_type_exec :: proc(t: ^testing.T) { 110 data := make([]u8, 64) 111 defer delete(data) 112 make_elf64_header(data, ET_EXEC, EM_X86_64) 113 114 header, err := parse_header(data) 115 116 testing.expect(t, err == "") 117 testing.expect(t, header.type == ET_EXEC, "type should be ET_EXEC") 118 } 119 120 @(test) 121 test_parse_header_type_dyn :: proc(t: ^testing.T) { 122 data := make([]u8, 64) 123 defer delete(data) 124 make_elf64_header(data, ET_DYN, EM_X86_64) 125 126 header, err := parse_header(data) 127 128 testing.expect(t, err == "") 129 testing.expect(t, header.type == ET_DYN, "type should be ET_DYN") 130 } 131 132 @(test) 133 test_parse_header_type_rel :: proc(t: ^testing.T) { 134 data := make([]u8, 64) 135 defer delete(data) 136 make_elf64_header(data, ET_REL, EM_X86_64) 137 138 header, err := parse_header(data) 139 140 testing.expect(t, err == "") 141 testing.expect(t, header.type == ET_REL, "type should be ET_REL") 142 } 143 144 @(test) 145 test_parse_header_machine_x86_64 :: proc(t: ^testing.T) { 146 data := make([]u8, 64) 147 defer delete(data) 148 make_elf64_header(data, ET_EXEC, EM_X86_64) 149 150 header, err := parse_header(data) 151 152 testing.expect(t, err == "") 153 testing.expect(t, header.machine == EM_X86_64, "machine should be x86-64") 154 } 155 156 @(test) 157 test_parse_header_machine_arm :: proc(t: ^testing.T) { 158 data := make([]u8, 64) 159 defer delete(data) 160 make_elf64_header(data, ET_EXEC, EM_ARM) 161 162 header, err := parse_header(data) 163 164 testing.expect(t, err == "") 165 testing.expect(t, header.machine == EM_ARM, "machine should be ARM") 166 } 167 168 @(test) 169 test_parse_header_machine_aarch64 :: proc(t: ^testing.T) { 170 data := make([]u8, 64) 171 defer delete(data) 172 make_elf64_header(data, ET_EXEC, EM_AARCH64) 173 174 header, err := parse_header(data) 175 176 testing.expect(t, err == "") 177 testing.expect(t, header.machine == EM_AARCH64, "machine should be AArch64") 178 } 179 180 @(test) 181 test_parse_header_machine_riscv :: proc(t: ^testing.T) { 182 data := make([]u8, 64) 183 defer delete(data) 184 make_elf64_header(data, ET_EXEC, EM_RISCV) 185 186 header, err := parse_header(data) 187 188 testing.expect(t, err == "") 189 testing.expect(t, header.machine == EM_RISCV, "machine should be RISC-V") 190 } 191 192 @(test) 193 test_machine_str_covers_firmware_arches :: proc(t: ^testing.T) { 194 testing.expect_value(t, machine_to_str(EM_MIPS), "MIPS") 195 testing.expect_value(t, machine_to_str(EM_PPC), "PowerPC") 196 testing.expect_value(t, machine_to_str(EM_PPC64), "PowerPC64") 197 testing.expect_value(t, machine_to_str(EM_ARM), "ARM") 198 testing.expect_value(t, machine_to_str(EM_AARCH64), "AArch64") 199 } 200 201 @(test) 202 test_read_u16_le_basic :: proc(t: ^testing.T) { 203 data := []u8{0x01, 0x02} 204 205 val := read_u16(data, 0, .Little) 206 testing.expect_value(t, val, u16(0x0201)) 207 } 208 209 @(test) 210 test_read_u16_le_offset :: proc(t: ^testing.T) { 211 data := []u8{0x00, 0x00, 0x01, 0x02} 212 213 val := read_u16(data, 2, .Little) 214 testing.expect_value(t, val, u16(0x0201)) 215 } 216 217 @(test) 218 test_read_u32_le_basic :: proc(t: ^testing.T) { 219 data := []u8{0x01, 0x02, 0x03, 0x04} 220 221 val := read_u32(data, 0, .Little) 222 testing.expect_value(t, val, u32(0x04030201)) 223 } 224 225 @(test) 226 test_read_u64_le_basic :: proc(t: ^testing.T) { 227 data := []u8{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} 228 229 val := read_u64(data, 0, .Little) 230 testing.expect_value(t, val, u64(0x0807060504030201)) 231 } 232 233 @(test) 234 test_read_u16_be_basic :: proc(t: ^testing.T) { 235 data := []u8{0x01, 0x02} 236 237 val := read_u16(data, 0, .Big) 238 testing.expect_value(t, val, u16(0x0102)) 239 } 240 241 @(test) 242 test_read_u32_be_basic :: proc(t: ^testing.T) { 243 data := []u8{0x01, 0x02, 0x03, 0x04} 244 245 val := read_u32(data, 0, .Big) 246 testing.expect_value(t, val, u32(0x01020304)) 247 } 248 249 @(test) 250 test_read_u64_be_basic :: proc(t: ^testing.T) { 251 data := []u8{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} 252 253 val := read_u64(data, 0, .Big) 254 testing.expect_value(t, val, u64(0x0102030405060708)) 255 } 256 257 @(test) 258 test_load_elf64_basic :: proc(t: ^testing.T) { 259 data := make([]u8, 64) 260 defer delete(data) 261 make_elf64_header(data, ET_EXEC, EM_X86_64) 262 263 info, err := load(data) 264 defer destroy(&info) 265 testing.expect(t, err == "", "load should succeed") 266 testing.expect(t, info.is_64bit == true, "should be 64-bit") 267 testing.expect(t, info.is_pie == false, "ET_EXEC should not be PIE") 268 } 269 270 @(test) 271 test_load_elf64_pie :: proc(t: ^testing.T) { 272 data := make([]u8, 64) 273 defer delete(data) 274 make_elf64_header(data, ET_DYN, EM_X86_64) 275 276 info, err := load(data) 277 defer destroy(&info) 278 testing.expect(t, err == "", "load should succeed") 279 testing.expect(t, info.is_pie == true, "ET_DYN should be PIE") 280 } 281 282 @(test) 283 test_load_elf32_basic :: proc(t: ^testing.T) { 284 data := make([]u8, 52) 285 defer delete(data) 286 287 data[0] = 0x7f 288 data[1] = 0x45 289 data[2] = 0x4c 290 data[3] = 0x46 291 data[4] = ELF_CLASS_32 292 data[5] = ELF_DATA_LSB 293 data[16] = u8(ET_EXEC) 294 data[17] = 0 295 data[18] = u8(EM_386) 296 data[19] = 0 297 298 info, err := load(data) 299 defer destroy(&info) 300 testing.expect(t, err == "", "load should succeed") 301 testing.expect(t, info.is_64bit == false, "should be 32-bit") 302 } 303 304 TEXT_BLOB :: "\x55\x89\xe5\x83\xec\x08\xc9\xc3" 305 INTERP_BLOB :: "/lib/ld-linux.so.2\x00" 306 SHSTRTAB_BLOB :: "\x00.text\x00.interp\x00.symtab\x00.strtab\x00.dynsym\x00.dynstr\x00.shstrtab\x00" 307 STRTAB_BLOB :: "\x00main\x00counter\x00printf\x00" 308 DYNSTR_BLOB :: "\x00puts\x00" 309 310 NAME_TEXT :: 1 311 NAME_INTERP :: 7 312 NAME_SYMTAB :: 15 313 NAME_STRTAB :: 23 314 NAME_DYNSYM :: 31 315 NAME_DYNSTR :: 39 316 NAME_SHSTRTAB :: 47 317 318 STR_MAIN :: 1 319 STR_COUNTER :: 6 320 STR_PRINTF :: 14 321 DYN_PUTS :: 1 322 323 SEC_NULL :: 0 324 SEC_TEXT :: 1 325 SEC_INTERP :: 2 326 SEC_SYMTAB :: 3 327 SEC_STRTAB :: 4 328 SEC_DYNSYM :: 5 329 SEC_DYNSTR :: 6 330 SEC_SHSTRTAB :: 7 331 332 Raw_Sym :: struct { 333 name : u32, 334 info : u8, 335 other : u8, 336 shndx : u16, 337 value : u64, 338 size : u64, 339 } 340 341 Fixture :: struct { 342 data : []u8, 343 class : u8, 344 endian : u8, 345 type : u16, 346 machine : u16, 347 entry : u64, 348 flags : u32, 349 phoff : u64, 350 shoff : u64, 351 phentsize : u16, 352 shentsize : u16, 353 phnum : u16, 354 shnum : u16, 355 shstrndx : u16, 356 symsize : int, 357 interp_off: u64, 358 segments : [4]Program_Header, 359 sections : [8]Section_Header, 360 symbols : [4]Raw_Sym, 361 dynsyms : [2]Raw_Sym, 362 } 363 364 put_u16 :: proc(data: []u8, off: int, v: u16, little: bool) { 365 if little { 366 data[off+0] = u8(v) 367 data[off+1] = u8(v >> 8) 368 } else { 369 data[off+0] = u8(v >> 8) 370 data[off+1] = u8(v) 371 } 372 } 373 374 put_u32 :: proc(data: []u8, off: int, v: u32, little: bool) { 375 for i in 0 ..< 4 { 376 shift := uint(8 * i) if little else uint(8 * (3 - i)) 377 data[off+i] = u8(v >> shift) 378 } 379 } 380 381 put_u64 :: proc(data: []u8, off: int, v: u64, little: bool) { 382 for i in 0 ..< 8 { 383 shift := uint(8 * i) if little else uint(8 * (7 - i)) 384 data[off+i] = u8(v >> shift) 385 } 386 } 387 388 align_up :: proc(v: int, a: int) -> int { 389 return (v + a - 1) / a * a 390 } 391 392 write_phdr :: proc(data: []u8, off: int, is64: bool, little: bool, p: Program_Header) { 393 if is64 { 394 put_u32(data, off+0, p.type, little) 395 put_u32(data, off+4, u32(p.flags), little) 396 put_u64(data, off+8, p.offset, little) 397 put_u64(data, off+16, p.vaddr, little) 398 put_u64(data, off+24, p.paddr, little) 399 put_u64(data, off+32, p.filesz, little) 400 put_u64(data, off+40, p.memsz, little) 401 put_u64(data, off+48, p.align, little) 402 } else { 403 put_u32(data, off+0, p.type, little) 404 put_u32(data, off+4, u32(p.offset), little) 405 put_u32(data, off+8, u32(p.vaddr), little) 406 put_u32(data, off+12, u32(p.paddr), little) 407 put_u32(data, off+16, u32(p.filesz), little) 408 put_u32(data, off+20, u32(p.memsz), little) 409 put_u32(data, off+24, u32(p.flags), little) 410 put_u32(data, off+28, u32(p.align), little) 411 } 412 } 413 414 write_shdr :: proc(data: []u8, off: int, is64: bool, little: bool, s: Section_Header) { 415 if is64 { 416 put_u32(data, off+0, s.name, little) 417 put_u32(data, off+4, s.type, little) 418 put_u64(data, off+8, s.flags, little) 419 put_u64(data, off+16, s.addr, little) 420 put_u64(data, off+24, s.offset, little) 421 put_u64(data, off+32, s.size, little) 422 put_u32(data, off+40, s.link, little) 423 put_u32(data, off+44, s.info, little) 424 put_u64(data, off+48, s.addralign, little) 425 put_u64(data, off+56, s.entsize, little) 426 } else { 427 put_u32(data, off+0, s.name, little) 428 put_u32(data, off+4, s.type, little) 429 put_u32(data, off+8, u32(s.flags), little) 430 put_u32(data, off+12, u32(s.addr), little) 431 put_u32(data, off+16, u32(s.offset), little) 432 put_u32(data, off+20, u32(s.size), little) 433 put_u32(data, off+24, s.link, little) 434 put_u32(data, off+28, s.info, little) 435 put_u32(data, off+32, u32(s.addralign), little) 436 put_u32(data, off+36, u32(s.entsize), little) 437 } 438 } 439 440 write_sym :: proc(data: []u8, off: int, is64: bool, little: bool, s: Raw_Sym) { 441 if is64 { 442 put_u32(data, off+0, s.name, little) 443 data[off+4] = s.info 444 data[off+5] = s.other 445 put_u16(data, off+6, s.shndx, little) 446 put_u64(data, off+8, s.value, little) 447 put_u64(data, off+16, s.size, little) 448 } else { 449 put_u32(data, off+0, s.name, little) 450 put_u32(data, off+4, u32(s.value), little) 451 put_u32(data, off+8, u32(s.size), little) 452 data[off+12] = s.info 453 data[off+13] = s.other 454 put_u16(data, off+14, s.shndx, little) 455 } 456 } 457 458 build_elf :: proc(class: u8, endian: u8) -> Fixture { 459 f: Fixture 460 f.class = class 461 f.endian = endian 462 463 is64 := class == ELF_CLASS_64 464 little := endian == ELF_DATA_LSB 465 466 ehsize := 64 if is64 else 52 467 f.phentsize = 56 if is64 else 32 468 f.shentsize = 64 if is64 else 40 469 f.symsize = 24 if is64 else 16 470 471 f.phnum = 4 472 f.shnum = 8 473 f.shstrndx = SEC_SHSTRTAB 474 f.type = ET_EXEC 475 f.machine = EM_X86_64 if is64 else EM_386 476 f.entry = 0x0000000000401120 if is64 else 0x08049000 477 f.flags = 0x05000200 478 479 off_ph := ehsize 480 off_interp := off_ph + int(f.phnum) * int(f.phentsize) 481 off_text := off_interp + len(INTERP_BLOB) 482 off_shstr := off_text + len(TEXT_BLOB) 483 off_strtab := off_shstr + len(SHSTRTAB_BLOB) 484 off_dynstr := off_strtab + len(STRTAB_BLOB) 485 off_symtab := align_up(off_dynstr + len(DYNSTR_BLOB), 8) 486 off_dynsym := off_symtab + 4 * f.symsize 487 off_sh := align_up(off_dynsym + 2 * f.symsize, 8) 488 total := off_sh + int(f.shnum) * int(f.shentsize) 489 490 f.phoff = u64(off_ph) 491 f.shoff = u64(off_sh) 492 f.interp_off = u64(off_interp) 493 494 f.segments = [4]Program_Header{ 495 { 496 type = PT_LOAD, flags = PF_R | PF_X, 497 offset = 0, vaddr = 0x08048000, paddr = 0x08048000, 498 filesz = 0x0640, memsz = 0x0640, align = 0x1000, 499 }, 500 { 501 type = PT_LOAD, flags = PF_R | PF_W, 502 offset = 0x0f00, vaddr = 0x0804af00, paddr = 0x0804af10, 503 filesz = 0x0118, memsz = 0x0220, align = 0x1000, 504 }, 505 { 506 type = PT_INTERP, flags = PF_R, 507 offset = u64(off_interp), vaddr = 0x08048154, paddr = 0x08048154, 508 filesz = u64(len(INTERP_BLOB)), memsz = u64(len(INTERP_BLOB)), align = 1, 509 }, 510 { 511 type = PT_DYNAMIC, flags = PF_R | PF_W, 512 offset = 0x0f14, vaddr = 0x0804af14, paddr = 0x0804af18, 513 filesz = 0x00e8, memsz = 0x00e8, align = 4, 514 }, 515 } 516 517 f.sections = [8]Section_Header{ 518 {}, 519 { 520 name = NAME_TEXT, type = SHT_PROGBITS, flags = 0x6, addr = 0x08049000, 521 offset = u64(off_text), size = u64(len(TEXT_BLOB)), addralign = 16, 522 }, 523 { 524 name = NAME_INTERP, type = SHT_PROGBITS, flags = 0x2, addr = 0x08048154, 525 offset = u64(off_interp), size = u64(len(INTERP_BLOB)), addralign = 1, 526 }, 527 { 528 name = NAME_SYMTAB, type = SHT_SYMTAB, 529 offset = u64(off_symtab), size = u64(4 * f.symsize), 530 link = SEC_STRTAB, info = 2, addralign = 8, entsize = u64(f.symsize), 531 }, 532 { 533 name = NAME_STRTAB, type = SHT_STRTAB, 534 offset = u64(off_strtab), size = u64(len(STRTAB_BLOB)), addralign = 1, 535 }, 536 { 537 name = NAME_DYNSYM, type = SHT_DYNSYM, flags = 0x2, addr = 0x080481b0, 538 offset = u64(off_dynsym), size = u64(2 * f.symsize), 539 link = SEC_DYNSTR, info = 1, addralign = 4, entsize = u64(f.symsize), 540 }, 541 { 542 name = NAME_DYNSTR, type = SHT_STRTAB, flags = 0x2, addr = 0x080481f0, 543 offset = u64(off_dynstr), size = u64(len(DYNSTR_BLOB)), addralign = 1, 544 }, 545 { 546 name = NAME_SHSTRTAB, type = SHT_STRTAB, 547 offset = u64(off_shstr), size = u64(len(SHSTRTAB_BLOB)), addralign = 1, 548 }, 549 } 550 551 f.symbols = [4]Raw_Sym{ 552 {}, 553 {name = STR_COUNTER, info = (STB_LOCAL << 4) | STT_OBJECT, value = 0x0804af00, size = 4, shndx = SEC_TEXT}, 554 {name = STR_MAIN, info = (STB_GLOBAL << 4) | STT_FUNC, value = 0x08049000, size = 0x40, shndx = SEC_TEXT}, 555 {name = STR_PRINTF, info = (STB_GLOBAL << 4) | STT_FUNC, value = 0, size = 0, shndx = 0}, 556 } 557 558 f.dynsyms = [2]Raw_Sym{ 559 {}, 560 {name = DYN_PUTS, info = (STB_GLOBAL << 4) | STT_FUNC, value = 0, size = 0, shndx = 0}, 561 } 562 563 data := make([]u8, total) 564 f.data = data 565 566 data[0] = 0x7f 567 data[1] = 'E' 568 data[2] = 'L' 569 data[3] = 'F' 570 data[4] = class 571 data[5] = endian 572 data[6] = 1 573 data[7] = 0 574 575 put_u16(data, 16, f.type, little) 576 put_u16(data, 18, f.machine, little) 577 put_u32(data, 20, 1, little) 578 579 if is64 { 580 put_u64(data, 24, f.entry, little) 581 put_u64(data, 32, f.phoff, little) 582 put_u64(data, 40, f.shoff, little) 583 put_u32(data, 48, f.flags, little) 584 put_u16(data, 52, u16(ehsize), little) 585 put_u16(data, 54, f.phentsize, little) 586 put_u16(data, 56, f.phnum, little) 587 put_u16(data, 58, f.shentsize, little) 588 put_u16(data, 60, f.shnum, little) 589 put_u16(data, 62, f.shstrndx, little) 590 } else { 591 put_u32(data, 24, u32(f.entry), little) 592 put_u32(data, 28, u32(f.phoff), little) 593 put_u32(data, 32, u32(f.shoff), little) 594 put_u32(data, 36, f.flags, little) 595 put_u16(data, 40, u16(ehsize), little) 596 put_u16(data, 42, f.phentsize, little) 597 put_u16(data, 44, f.phnum, little) 598 put_u16(data, 46, f.shentsize, little) 599 put_u16(data, 48, f.shnum, little) 600 put_u16(data, 50, f.shstrndx, little) 601 } 602 603 for seg, i in f.segments { 604 write_phdr(data, off_ph + i * int(f.phentsize), is64, little, seg) 605 } 606 607 copy(data[off_interp:], INTERP_BLOB) 608 copy(data[off_text:], TEXT_BLOB) 609 copy(data[off_shstr:], SHSTRTAB_BLOB) 610 copy(data[off_strtab:], STRTAB_BLOB) 611 copy(data[off_dynstr:], DYNSTR_BLOB) 612 613 for sym, i in f.symbols { 614 write_sym(data, off_symtab + i * f.symsize, is64, little, sym) 615 } 616 for sym, i in f.dynsyms { 617 write_sym(data, off_dynsym + i * f.symsize, is64, little, sym) 618 } 619 for sec, i in f.sections { 620 write_shdr(data, off_sh + i * int(f.shentsize), is64, little, sec) 621 } 622 623 return f 624 } 625 626 SECTION_NAMES :: [8]string{"", ".text", ".interp", ".symtab", ".strtab", ".dynsym", ".dynstr", ".shstrtab"} 627 SYMBOL_NAMES :: [4]string{"", "counter", "main", "printf"} 628 DYNSYM_NAMES :: [2]string{"", "puts"} 629 630 check_full_image :: proc(t: ^testing.T, class: u8, endian: u8) { 631 f := build_elf(class, endian) 632 defer delete(f.data) 633 634 info, err := load(f.data) 635 testing.expect(t, err == "", "load should succeed") 636 defer destroy(&info) 637 638 testing.expect_value(t, info.header.class, class) 639 testing.expect_value(t, info.header.data, endian) 640 testing.expect_value(t, info.is_64bit, class == ELF_CLASS_64) 641 testing.expect_value(t, info.is_pie, false) 642 643 testing.expect_value(t, info.header.type, f.type) 644 testing.expect_value(t, info.header.machine, f.machine) 645 testing.expect_value(t, info.header.entry, f.entry) 646 testing.expect_value(t, info.header.phoff, f.phoff) 647 testing.expect_value(t, info.header.shoff, f.shoff) 648 testing.expect_value(t, info.header.flags, f.flags) 649 testing.expect_value(t, info.header.phentsize, f.phentsize) 650 testing.expect_value(t, info.header.shentsize, f.shentsize) 651 testing.expect_value(t, info.header.phnum, f.phnum) 652 testing.expect_value(t, info.header.shnum, f.shnum) 653 testing.expect_value(t, info.header.shstrndx, f.shstrndx) 654 655 testing.expect_value(t, len(info.program_hdrs), int(f.phnum)) 656 if len(info.program_hdrs) == int(f.phnum) { 657 for want, i in f.segments { 658 testing.expect_value(t, info.program_hdrs[i], want) 659 } 660 } 661 662 testing.expect_value(t, info.is_dynamic, true) 663 testing.expect_value(t, info.interp_offset, f.interp_off) 664 665 testing.expect_value(t, len(info.section_hdrs), int(f.shnum)) 666 if len(info.section_hdrs) == int(f.shnum) { 667 names := SECTION_NAMES 668 for want, i in f.sections { 669 testing.expect_value(t, info.section_hdrs[i], want) 670 testing.expect_value(t, get_section_name(&info, info.section_hdrs[i]), names[i]) 671 } 672 } 673 674 testing.expect_value(t, string(info.shstrtab), SHSTRTAB_BLOB) 675 testing.expect_value(t, string(info.strtab), STRTAB_BLOB) 676 testing.expect_value(t, string(info.dynstr), DYNSTR_BLOB) 677 678 testing.expect_value(t, len(info.symbols), len(f.symbols)) 679 if len(info.symbols) == len(f.symbols) { 680 names := SYMBOL_NAMES 681 for want, i in f.symbols { 682 got := info.symbols[i] 683 testing.expect_value(t, got.name, names[i]) 684 testing.expect_value(t, got.value, want.value) 685 testing.expect_value(t, got.size, want.size) 686 testing.expect_value(t, got.bind, want.info >> 4) 687 testing.expect_value(t, got.type, want.info & 0xf) 688 testing.expect_value(t, got.shndx, want.shndx) 689 } 690 testing.expect_value(t, info.symbols[1].bind, u8(STB_LOCAL)) 691 testing.expect_value(t, info.symbols[1].type, u8(STT_OBJECT)) 692 testing.expect_value(t, info.symbols[2].bind, u8(STB_GLOBAL)) 693 testing.expect_value(t, info.symbols[2].type, u8(STT_FUNC)) 694 testing.expect_value(t, info.symbols[3].shndx, u16(0)) 695 } 696 697 testing.expect_value(t, len(info.dyn_symbols), len(f.dynsyms)) 698 if len(info.dyn_symbols) == len(f.dynsyms) { 699 names := DYNSYM_NAMES 700 for want, i in f.dynsyms { 701 got := info.dyn_symbols[i] 702 testing.expect_value(t, got.name, names[i]) 703 testing.expect_value(t, got.bind, want.info >> 4) 704 testing.expect_value(t, got.type, want.info & 0xf) 705 testing.expect_value(t, got.shndx, want.shndx) 706 } 707 } 708 } 709 710 @(test) 711 test_full_image_elf32_le :: proc(t: ^testing.T) { 712 check_full_image(t, ELF_CLASS_32, ELF_DATA_LSB) 713 } 714 715 @(test) 716 test_full_image_elf32_be :: proc(t: ^testing.T) { 717 check_full_image(t, ELF_CLASS_32, ELF_DATA_MSB) 718 } 719 720 @(test) 721 test_full_image_elf64_le :: proc(t: ^testing.T) { 722 check_full_image(t, ELF_CLASS_64, ELF_DATA_LSB) 723 } 724 725 @(test) 726 test_full_image_elf64_be :: proc(t: ^testing.T) { 727 check_full_image(t, ELF_CLASS_64, ELF_DATA_MSB) 728 } 729 730 // Regression: ELF32 header fields must use the 32-bit layout 731 732 build_elf32_header_only :: proc(little: bool, entry: u32, phoff: u32, shoff: u32, flags: u32) -> []u8 { 733 data := make([]u8, 52) 734 data[0] = 0x7f 735 data[1] = 'E' 736 data[2] = 'L' 737 data[3] = 'F' 738 data[4] = ELF_CLASS_32 739 data[5] = ELF_DATA_LSB if little else ELF_DATA_MSB 740 data[6] = 1 741 742 put_u16(data, 16, ET_EXEC, little) 743 put_u16(data, 18, EM_386, little) 744 put_u32(data, 20, 1, little) 745 put_u32(data, 24, entry, little) 746 put_u32(data, 28, phoff, little) 747 put_u32(data, 32, shoff, little) 748 put_u32(data, 36, flags, little) 749 put_u16(data, 40, 52, little) 750 put_u16(data, 42, 32, little) 751 put_u16(data, 44, 9, little) 752 put_u16(data, 46, 40, little) 753 put_u16(data, 48, 30, little) 754 put_u16(data, 50, 29, little) 755 return data 756 } 757 758 @(test) 759 test_regression_elf32_header_uses_32bit_layout_le :: proc(t: ^testing.T) { 760 data := build_elf32_header_only(true, 0x08049000, 52, 12620, 0) 761 defer delete(data) 762 763 h, err := parse_header(data) 764 testing.expect(t, err == "", "parse should succeed") 765 766 testing.expect_value(t, h.entry, u64(0x08049000)) 767 testing.expect_value(t, h.phoff, u64(52)) 768 testing.expect_value(t, h.shoff, u64(12620)) 769 testing.expect_value(t, h.flags, u32(0)) 770 testing.expect_value(t, h.phentsize, u16(32)) 771 testing.expect_value(t, h.phnum, u16(9)) 772 testing.expect_value(t, h.shentsize, u16(40)) 773 testing.expect_value(t, h.shnum, u16(30)) 774 testing.expect_value(t, h.shstrndx, u16(29)) 775 } 776 777 @(test) 778 test_regression_elf32_header_uses_32bit_layout_be :: proc(t: ^testing.T) { 779 data := build_elf32_header_only(false, 0x08049000, 52, 12620, 0) 780 defer delete(data) 781 782 h, err := parse_header(data) 783 testing.expect(t, err == "", "parse should succeed") 784 785 testing.expect_value(t, h.entry, u64(0x08049000)) 786 testing.expect_value(t, h.phoff, u64(52)) 787 testing.expect_value(t, h.shoff, u64(12620)) 788 testing.expect_value(t, h.flags, u32(0)) 789 testing.expect_value(t, h.phentsize, u16(32)) 790 testing.expect_value(t, h.phnum, u16(9)) 791 testing.expect_value(t, h.shentsize, u16(40)) 792 testing.expect_value(t, h.shnum, u16(30)) 793 testing.expect_value(t, h.shstrndx, u16(29)) 794 } 795 796 @(test) 797 test_regression_elf32_flags_not_contaminated :: proc(t: ^testing.T) { 798 for little in ([2]bool{true, false}) { 799 data := build_elf32_header_only(little, 0xdeadbeef, 0x11223344, 0x55667788, 0x05000402) 800 defer delete(data) 801 802 h, err := parse_header(data) 803 testing.expect(t, err == "", "parse should succeed") 804 805 testing.expect_value(t, h.entry, u64(0xdeadbeef)) 806 testing.expect_value(t, h.phoff, u64(0x11223344)) 807 testing.expect_value(t, h.shoff, u64(0x55667788)) 808 testing.expect_value(t, h.flags, u32(0x05000402)) 809 } 810 } 811 812 @(test) 813 test_regression_elf32_program_headers_parse :: proc(t: ^testing.T) { 814 f := build_elf(ELF_CLASS_32, ELF_DATA_LSB) 815 defer delete(f.data) 816 817 info, err := load(f.data) 818 testing.expect(t, err == "", "load should succeed") 819 defer destroy(&info) 820 821 testing.expect_value(t, info.header.phoff, u64(52)) 822 testing.expect_value(t, len(info.program_hdrs), 4) 823 if len(info.program_hdrs) == 4 { 824 testing.expect_value(t, info.program_hdrs[0].type, u32(PT_LOAD)) 825 testing.expect_value(t, info.program_hdrs[2].type, u32(PT_INTERP)) 826 testing.expect_value(t, info.program_hdrs[3].type, u32(PT_DYNAMIC)) 827 } 828 } 829 830 @(test) 831 test_in_bounds :: proc(t: ^testing.T) { 832 data := make([]u8, 16) 833 defer delete(data) 834 835 testing.expect_value(t, in_bounds(data, 0, 16), true) 836 testing.expect_value(t, in_bounds(data, 16, 0), true) 837 testing.expect_value(t, in_bounds(data, 8, 8), true) 838 testing.expect_value(t, in_bounds(data, 15, 1), true) 839 840 testing.expect_value(t, in_bounds(data, 17, 0), false) 841 testing.expect_value(t, in_bounds(data, 16, 1), false) 842 testing.expect_value(t, in_bounds(data, 8, 9), false) 843 testing.expect_value(t, in_bounds(data, 0, 17), false) 844 testing.expect_value(t, in_bounds(data, 0, 0xFFFFFFFFFFFFFFFF), false) 845 testing.expect_value(t, in_bounds(data, 0xFFFFFFFFFFFFFFFF, 0), false) 846 testing.expect_value(t, in_bounds(data, 0xFFFFFFFFFFFFFFFF, 1), false) 847 testing.expect_value(t, in_bounds(data, 0xFFFFFFFFFFFFFFF8, 8), false) 848 testing.expect_value(t, in_bounds(data, 0xFF00000000000000, 0), false) 849 testing.expect_value(t, in_bounds(data, 0x8000000000000000, 0), false) 850 } 851 852 @(test) 853 test_in_bounds_empty_slice :: proc(t: ^testing.T) { 854 empty: []u8 855 testing.expect_value(t, in_bounds(empty, 0, 0), true) 856 testing.expect_value(t, in_bounds(empty, 0, 1), false) 857 testing.expect_value(t, in_bounds(empty, 1, 0), false) 858 } 859 860 @(test) 861 test_getters_reject_negative_offsets :: proc(t: ^testing.T) { 862 data := []u8{1, 2, 3, 4, 5, 6, 7, 8} 863 864 for off in ([3]int{-1, -8, -1024}) { 865 testing.expect_value(t, read_u16(data, off, .Little), u16(0)) 866 testing.expect_value(t, read_u32(data, off, .Little), u32(0)) 867 testing.expect_value(t, read_u64(data, off, .Little), u64(0)) 868 testing.expect_value(t, read_u16(data, off, .Big), u16(0)) 869 testing.expect_value(t, read_u32(data, off, .Big), u32(0)) 870 testing.expect_value(t, read_u64(data, off, .Big), u64(0)) 871 } 872 } 873 874 @(test) 875 test_getters_reject_past_end :: proc(t: ^testing.T) { 876 data := []u8{1, 2, 3, 4, 5, 6, 7, 8} 877 878 testing.expect_value(t, read_u16(data, 7, .Little), u16(0)) 879 testing.expect_value(t, read_u16(data, 8, .Little), u16(0)) 880 testing.expect_value(t, read_u16(data, 999, .Little), u16(0)) 881 testing.expect_value(t, read_u16(data, 7, .Big), u16(0)) 882 testing.expect_value(t, read_u16(data, 999, .Big), u16(0)) 883 884 testing.expect_value(t, read_u32(data, 5, .Little), u32(0)) 885 testing.expect_value(t, read_u32(data, 8, .Little), u32(0)) 886 testing.expect_value(t, read_u32(data, 5, .Big), u32(0)) 887 testing.expect_value(t, read_u32(data, 999, .Big), u32(0)) 888 889 testing.expect_value(t, read_u64(data, 1, .Little), u64(0)) 890 testing.expect_value(t, read_u64(data, 8, .Little), u64(0)) 891 testing.expect_value(t, read_u64(data, 1, .Big), u64(0)) 892 testing.expect_value(t, read_u64(data, 999, .Big), u64(0)) 893 } 894 895 @(test) 896 test_getters_exact_fit :: proc(t: ^testing.T) { 897 data := []u8{1, 2, 3, 4, 5, 6, 7, 8} 898 899 testing.expect_value(t, read_u16(data, 6, .Little), u16(0x0807)) 900 testing.expect_value(t, read_u16(data, 6, .Big), u16(0x0708)) 901 testing.expect_value(t, read_u32(data, 4, .Little), u32(0x08070605)) 902 testing.expect_value(t, read_u32(data, 4, .Big), u32(0x05060708)) 903 testing.expect_value(t, read_u64(data, 0, .Little), u64(0x0807060504030201)) 904 testing.expect_value(t, read_u64(data, 0, .Big), u64(0x0102030405060708)) 905 } 906 907 @(test) 908 test_getters_on_empty_slice :: proc(t: ^testing.T) { 909 empty: []u8 910 testing.expect_value(t, read_u16(empty, 0, .Little), u16(0)) 911 testing.expect_value(t, read_u32(empty, 0, .Little), u32(0)) 912 testing.expect_value(t, read_u64(empty, 0, .Little), u64(0)) 913 testing.expect_value(t, read_u16(empty, 0, .Big), u16(0)) 914 testing.expect_value(t, read_u32(empty, 0, .Big), u32(0)) 915 testing.expect_value(t, read_u64(empty, 0, .Big), u64(0)) 916 } 917 918 build_elf64_with_segments :: proc(segs: []Program_Header) -> []u8 { 919 ehsize := 64 920 total := ehsize + len(segs) * 56 921 data := make([]u8, total) 922 923 data[0] = 0x7f 924 data[1] = 'E' 925 data[2] = 'L' 926 data[3] = 'F' 927 data[4] = ELF_CLASS_64 928 data[5] = ELF_DATA_LSB 929 data[6] = 1 930 931 put_u16(data, 16, ET_DYN, true) 932 put_u16(data, 18, EM_X86_64, true) 933 put_u64(data, 32, u64(ehsize), true) 934 put_u16(data, 52, u16(ehsize), true) 935 put_u16(data, 54, 56, true) 936 put_u16(data, 56, u16(len(segs)), true) 937 put_u16(data, 58, 64, true) 938 939 for seg, i in segs { 940 write_phdr(data, ehsize + i * 56, true, true, seg) 941 } 942 return data 943 } 944 945 @(test) 946 test_is_dynamic_with_pt_dynamic_and_no_interp :: proc(t: ^testing.T) { 947 segs := [2]Program_Header{ 948 {type = PT_LOAD, offset = 0, filesz = 0x100, memsz = 0x100}, 949 {type = PT_DYNAMIC, offset = 0x2000, filesz = 0xe0, memsz = 0xe0}, 950 } 951 data := build_elf64_with_segments(segs[:]) 952 defer delete(data) 953 954 info, err := load(data) 955 testing.expect(t, err == "", "load should succeed") 956 defer destroy(&info) 957 958 testing.expect_value(t, info.is_dynamic, true) 959 testing.expect_value(t, info.interp_offset, u64(0)) 960 testing.expect_value(t, info.is_pie, true) 961 } 962 963 @(test) 964 test_is_dynamic_with_interp_only :: proc(t: ^testing.T) { 965 segs := [2]Program_Header{ 966 {type = PT_LOAD, offset = 0, filesz = 0x100, memsz = 0x100}, 967 {type = PT_INTERP, offset = 0x318, filesz = 0x1c, memsz = 0x1c}, 968 } 969 data := build_elf64_with_segments(segs[:]) 970 defer delete(data) 971 972 info, err := load(data) 973 testing.expect(t, err == "", "load should succeed") 974 defer destroy(&info) 975 976 testing.expect_value(t, info.is_dynamic, true) 977 testing.expect_value(t, info.interp_offset, u64(0x318)) 978 } 979 980 @(test) 981 test_is_dynamic_false_for_static :: proc(t: ^testing.T) { 982 segs := [2]Program_Header{ 983 {type = PT_LOAD, offset = 0, filesz = 0x100, memsz = 0x100}, 984 {type = PT_GNU_STACK, offset = 0, filesz = 0, memsz = 0}, 985 } 986 data := build_elf64_with_segments(segs[:]) 987 defer delete(data) 988 989 info, err := load(data) 990 testing.expect(t, err == "", "load should succeed") 991 defer destroy(&info) 992 993 testing.expect_value(t, info.is_dynamic, false) 994 testing.expect_value(t, info.interp_offset, u64(0)) 995 } 996 997 @(test) 998 test_load_destroy_leaks_nothing :: proc(t: ^testing.T) { 999 track: mem.Tracking_Allocator 1000 mem.tracking_allocator_init(&track, context.allocator) 1001 defer mem.tracking_allocator_destroy(&track) 1002 1003 { 1004 context.allocator = mem.tracking_allocator(&track) 1005 1006 classes := [2]u8{ELF_CLASS_32, ELF_CLASS_64} 1007 endians := [2]u8{ELF_DATA_LSB, ELF_DATA_MSB} 1008 for class in classes { 1009 for endian in endians { 1010 f := build_elf(class, endian) 1011 info, err := load(f.data) 1012 testing.expect(t, err == "", "load should succeed") 1013 testing.expect(t, len(info.symbols) > 0, "fixture should carry symbols") 1014 destroy(&info) 1015 delete(f.data) 1016 } 1017 } 1018 } 1019 1020 testing.expect_value(t, len(track.allocation_map), 0) 1021 testing.expect_value(t, len(track.bad_free_array), 0) 1022 } 1023 1024 POISON :: [7]u64{ 1025 0xFF00000000000000, 1026 0x8000000000000000, 1027 0xFFFFFFFFFFFFFFFF, 1028 0x7FFFFFFFFFFFFFFF, 1029 0x00000000FFFFFFFF, 1030 0x0000000080000000, 1031 0xFFFFFFFFFFFFFFF0, 1032 } 1033 1034 // Every string the parser hands back must alias the caller's buffer. 1035 inside :: proc(ptr: rawptr, size: int, data: []u8) -> bool { 1036 if size == 0 { return true } 1037 if size < 0 { return false } 1038 lo := uintptr(raw_data(data)) 1039 p := uintptr(ptr) 1040 return p >= lo && p - lo <= uintptr(len(data)) && uintptr(len(data)) - (p - lo) >= uintptr(size) 1041 } 1042 1043 expect_sane_load :: proc(t: ^testing.T, data: []u8, label: string) { 1044 info, err := load(data) 1045 if err != "" { return } 1046 defer destroy(&info) 1047 1048 phentsize := 56 if info.is_64bit else 32 1049 shentsize := 64 if info.is_64bit else 40 1050 1051 // e_phnum == PN_XNUM and e_shnum == 0 are escape hatches; the real counts 1052 // come from section[0], so the header field is not an upper bound there. 1053 if info.header.phnum != PN_XNUM { 1054 testing.expect(t, len(info.program_hdrs) <= int(info.header.phnum), label) 1055 } 1056 if info.header.shnum != 0 { 1057 testing.expect(t, len(info.section_hdrs) <= int(info.header.shnum), label) 1058 } 1059 testing.expect(t, len(info.program_hdrs) * phentsize <= len(data), label) 1060 testing.expect(t, len(info.section_hdrs) * shentsize <= len(data), label) 1061 1062 testing.expect(t, inside(raw_data(info.shstrtab), len(info.shstrtab), data), label) 1063 testing.expect(t, inside(raw_data(info.strtab), len(info.strtab), data), label) 1064 testing.expect(t, inside(raw_data(info.dynstr), len(info.dynstr), data), label) 1065 1066 for shdr in info.section_hdrs { 1067 name := get_section_name(&info, shdr) 1068 if name == "<unknown>" { continue } 1069 testing.expect(t, inside(raw_data(name), len(name), data), label) 1070 } 1071 for sym in info.symbols { 1072 testing.expect(t, inside(raw_data(sym.name), len(sym.name), data), label) 1073 } 1074 for sym in info.dyn_symbols { 1075 testing.expect(t, inside(raw_data(sym.name), len(sym.name), data), label) 1076 } 1077 } 1078 1079 poison_header_offsets :: proc(f: Fixture, v: u64) { 1080 little := f.endian == ELF_DATA_LSB 1081 if f.class == ELF_CLASS_64 { 1082 put_u64(f.data, 32, v, little) 1083 put_u64(f.data, 40, v, little) 1084 } else { 1085 put_u32(f.data, 28, u32(v), little) 1086 put_u32(f.data, 32, u32(v), little) 1087 } 1088 } 1089 1090 @(test) 1091 test_malformed_poisoned_phoff_shoff :: proc(t: ^testing.T) { 1092 classes := [2]u8{ELF_CLASS_32, ELF_CLASS_64} 1093 endians := [2]u8{ELF_DATA_LSB, ELF_DATA_MSB} 1094 poisons := POISON 1095 1096 for class in classes { 1097 for endian in endians { 1098 for p in poisons { 1099 f := build_elf(class, endian) 1100 poison_header_offsets(f, p) 1101 expect_sane_load(t, f.data, "poisoned phoff/shoff") 1102 delete(f.data) 1103 } 1104 1105 f := build_elf(class, endian) 1106 poison_header_offsets(f, u64(len(f.data)) + 1) 1107 expect_sane_load(t, f.data, "phoff/shoff just past end") 1108 delete(f.data) 1109 1110 g := build_elf(class, endian) 1111 poison_header_offsets(g, u64(len(g.data))) 1112 expect_sane_load(t, g.data, "phoff/shoff exactly at end") 1113 delete(g.data) 1114 } 1115 } 1116 } 1117 1118 @(test) 1119 test_malformed_saturated_counts :: proc(t: ^testing.T) { 1120 classes := [2]u8{ELF_CLASS_32, ELF_CLASS_64} 1121 endians := [2]u8{ELF_DATA_LSB, ELF_DATA_MSB} 1122 1123 for class in classes { 1124 for endian in endians { 1125 f := build_elf(class, endian) 1126 little := endian == ELF_DATA_LSB 1127 if class == ELF_CLASS_64 { 1128 put_u16(f.data, 56, 0xFFFF, little) 1129 put_u16(f.data, 60, 0xFFFF, little) 1130 put_u16(f.data, 62, 0xFFFF, little) 1131 } else { 1132 put_u16(f.data, 44, 0xFFFF, little) 1133 put_u16(f.data, 48, 0xFFFF, little) 1134 put_u16(f.data, 50, 0xFFFF, little) 1135 } 1136 expect_sane_load(t, f.data, "saturated phnum/shnum/shstrndx") 1137 delete(f.data) 1138 } 1139 } 1140 } 1141 1142 @(test) 1143 test_malformed_saturated_entsizes :: proc(t: ^testing.T) { 1144 classes := [2]u8{ELF_CLASS_32, ELF_CLASS_64} 1145 1146 for class in classes { 1147 f := build_elf(class, ELF_DATA_LSB) 1148 if class == ELF_CLASS_64 { 1149 put_u16(f.data, 54, 0xFFFF, true) 1150 put_u16(f.data, 58, 0xFFFF, true) 1151 } else { 1152 put_u16(f.data, 42, 0xFFFF, true) 1153 put_u16(f.data, 46, 0xFFFF, true) 1154 } 1155 expect_sane_load(t, f.data, "saturated phentsize/shentsize") 1156 delete(f.data) 1157 } 1158 } 1159 1160 @(test) 1161 test_malformed_poisoned_section_offsets :: proc(t: ^testing.T) { 1162 classes := [2]u8{ELF_CLASS_32, ELF_CLASS_64} 1163 endians := [2]u8{ELF_DATA_LSB, ELF_DATA_MSB} 1164 poisons := POISON 1165 1166 for class in classes { 1167 for endian in endians { 1168 for p in poisons { 1169 f := build_elf(class, endian) 1170 little := endian == ELF_DATA_LSB 1171 for i in 0 ..< int(f.shnum) { 1172 base := int(f.shoff) + i * int(f.shentsize) 1173 if class == ELF_CLASS_64 { 1174 put_u64(f.data, base+24, p, little) 1175 put_u64(f.data, base+32, p, little) 1176 } else { 1177 put_u32(f.data, base+16, u32(p), little) 1178 put_u32(f.data, base+20, u32(p), little) 1179 } 1180 } 1181 expect_sane_load(t, f.data, "poisoned sh_offset/sh_size") 1182 delete(f.data) 1183 } 1184 } 1185 } 1186 } 1187 1188 // offset + size wraps past 2^64, which a naive `offset + size <= len` check accepts. 1189 @(test) 1190 test_malformed_section_offset_size_wraps :: proc(t: ^testing.T) { 1191 pairs := [5][2]u64{ 1192 {0xFFFFFFFFFFFFFFF0, 0x10}, 1193 {0xFFFFFFFFFFFFFF00, 0x100}, 1194 {0xFFFFFFFFFFFFFFFF, 1}, 1195 {0x8000000000000000, 0x8000000000000000}, 1196 {8, 0xFFFFFFFFFFFFFFF8}, 1197 } 1198 targets := [4]int{SEC_SYMTAB, SEC_STRTAB, SEC_DYNSYM, SEC_SHSTRTAB} 1199 1200 for pair in pairs { 1201 for target in targets { 1202 f := build_elf(ELF_CLASS_64, ELF_DATA_LSB) 1203 base := int(f.shoff) + target * int(f.shentsize) 1204 put_u64(f.data, base+24, pair[0], true) 1205 put_u64(f.data, base+32, pair[1], true) 1206 expect_sane_load(t, f.data, "sh_offset + sh_size wraps around u64") 1207 delete(f.data) 1208 } 1209 } 1210 } 1211 1212 @(test) 1213 test_malformed_poisoned_section_names :: proc(t: ^testing.T) { 1214 classes := [2]u8{ELF_CLASS_32, ELF_CLASS_64} 1215 names := [4]u32{0xFFFFFFFF, 0x80000000, u32(len(SHSTRTAB_BLOB)), u32(len(SHSTRTAB_BLOB)) - 1} 1216 1217 for class in classes { 1218 for n in names { 1219 f := build_elf(class, ELF_DATA_LSB) 1220 for i in 0 ..< int(f.shnum) { 1221 put_u32(f.data, int(f.shoff) + i * int(f.shentsize), n, true) 1222 } 1223 expect_sane_load(t, f.data, "poisoned sh_name") 1224 delete(f.data) 1225 } 1226 } 1227 } 1228 1229 @(test) 1230 test_malformed_symtab_link_out_of_range :: proc(t: ^testing.T) { 1231 classes := [2]u8{ELF_CLASS_32, ELF_CLASS_64} 1232 links := [4]u32{0xFFFFFFFF, 0x7FFFFFFF, 8, 0} 1233 1234 for class in classes { 1235 for link in links { 1236 f := build_elf(class, ELF_DATA_LSB) 1237 link_off := 40 if class == ELF_CLASS_64 else 24 1238 base := int(f.shoff) + SEC_SYMTAB * int(f.shentsize) 1239 put_u32(f.data, base + link_off, link, true) 1240 1241 info, err := load(f.data) 1242 testing.expect(t, err == "", "load should succeed") 1243 1244 if link >= u32(f.shnum) { 1245 testing.expect_value(t, len(info.symbols), 0) 1246 } 1247 for sym in info.symbols { 1248 testing.expect(t, len(sym.name) <= len(info.strtab), "symbol name must stay inside strtab") 1249 } 1250 destroy(&info) 1251 delete(f.data) 1252 } 1253 } 1254 } 1255 1256 @(test) 1257 test_malformed_truncated_images :: proc(t: ^testing.T) { 1258 classes := [2]u8{ELF_CLASS_32, ELF_CLASS_64} 1259 endians := [2]u8{ELF_DATA_LSB, ELF_DATA_MSB} 1260 1261 for class in classes { 1262 for endian in endians { 1263 f := build_elf(class, endian) 1264 for n in 0 ..= len(f.data) { 1265 expect_sane_load(t, f.data[:n], "truncated image") 1266 } 1267 delete(f.data) 1268 } 1269 } 1270 } 1271 1272 @(test) 1273 test_malformed_header_byte_sweep :: proc(t: ^testing.T) { 1274 patterns := [4]u8{0x00, 0x01, 0x7f, 0xff} 1275 1276 for pat in patterns { 1277 for i in 0 ..< 64 { 1278 f := build_elf(ELF_CLASS_64, ELF_DATA_LSB) 1279 f.data[i] = pat 1280 expect_sane_load(t, f.data, "single byte flip in ELF64 header") 1281 delete(f.data) 1282 1283 g := build_elf(ELF_CLASS_32, ELF_DATA_MSB) 1284 if i < len(g.data) { 1285 g.data[i] = pat 1286 } 1287 expect_sane_load(t, g.data, "single byte flip in ELF32 header") 1288 delete(g.data) 1289 } 1290 } 1291 } 1292 1293 @(test) 1294 test_parse_header_unknown_class :: proc(t: ^testing.T) { 1295 data := make([]u8, 64) 1296 defer delete(data) 1297 make_elf64_header(data, ET_EXEC, EM_X86_64) 1298 data[4] = 7 1299 1300 _, err := parse_header(data) 1301 testing.expect(t, err != "", "unknown class must be rejected") 1302 1303 info, load_err := load(data) 1304 testing.expect(t, load_err != "", "load must propagate the class error") 1305 testing.expect_value(t, len(info.program_hdrs), 0) 1306 } 1307 1308 @(test) 1309 test_load_rejects_non_elf :: proc(t: ^testing.T) { 1310 data := []u8{'M', 'Z', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 1311 _, err := load(data) 1312 testing.expect(t, err != "", "non-ELF data must be rejected") 1313 } 1314 1315 @(test) 1316 test_get_section_name_without_shstrtab :: proc(t: ^testing.T) { 1317 info: ELF_Info 1318 shdr := Section_Header{name = 4} 1319 testing.expect_value(t, get_section_name(&info, shdr), "<unknown>") 1320 1321 empty: []u8 1322 info.shstrtab = empty 1323 testing.expect_value(t, get_section_name(&info, shdr), "<unknown>") 1324 } 1325 1326 @(test) 1327 test_get_symbol_name_bounds :: proc(t: ^testing.T) { 1328 strtab := transmute([]u8)string(STRTAB_BLOB) 1329 1330 testing.expect_value(t, get_symbol_name(strtab, STR_MAIN), "main") 1331 testing.expect_value(t, get_symbol_name(strtab, STR_COUNTER), "counter") 1332 testing.expect_value(t, get_symbol_name(strtab, STR_PRINTF), "printf") 1333 testing.expect_value(t, get_symbol_name(strtab, 0), "") 1334 testing.expect_value(t, get_symbol_name(strtab, u32(len(STRTAB_BLOB))), "") 1335 testing.expect_value(t, get_symbol_name(strtab, 0xFFFFFFFF), "") 1336 1337 unterminated := []u8{'a', 'b', 'c'} 1338 testing.expect_value(t, get_symbol_name(unterminated, 0), "") 1339 1340 empty: []u8 1341 testing.expect_value(t, get_symbol_name(empty, 0), "") 1342 testing.expect_value(t, get_symbol_name(nil, 5), "") 1343 } 1344 1345 @(test) 1346 test_printer_strings :: proc(t: ^testing.T) { 1347 defer free_all(context.temp_allocator) 1348 1349 testing.expect_value(t, type_to_str(ET_REL), "REL (Relocatable)") 1350 testing.expect_value(t, type_to_str(ET_EXEC), "EXEC (Executable)") 1351 testing.expect_value(t, type_to_str(ET_DYN), "DYN (Shared Object/PIE)") 1352 testing.expect_value(t, type_to_str(ET_CORE), "CORE") 1353 testing.expect_value(t, type_to_str(999), "UNKNOWN (999)") 1354 1355 testing.expect_value(t, machine_to_str(EM_386), "x86 (i386)") 1356 testing.expect_value(t, machine_to_str(EM_X86_64), "x86-64") 1357 testing.expect_value(t, machine_to_str(EM_ARM), "ARM") 1358 testing.expect_value(t, machine_to_str(EM_AARCH64), "AArch64") 1359 testing.expect_value(t, machine_to_str(EM_RISCV), "RISC-V") 1360 testing.expect_value(t, machine_to_str(1234), "Unknown (1234)") 1361 1362 testing.expect_value(t, section_type_str(SHT_NULL), "NULL") 1363 testing.expect_value(t, section_type_str(SHT_PROGBITS), "PROGBITS") 1364 testing.expect_value(t, section_type_str(SHT_SYMTAB), "SYMTAB") 1365 testing.expect_value(t, section_type_str(SHT_STRTAB), "STRTAB") 1366 testing.expect_value(t, section_type_str(SHT_DYNSYM), "DYNSYM") 1367 testing.expect_value(t, section_type_str(0x6ffffff6), "GNU_HASH") 1368 testing.expect_value(t, section_type_str(0x1234), "0x1234") 1369 1370 testing.expect_value(t, symbol_type_str(STT_NOTYPE), "NOTYPE") 1371 testing.expect_value(t, symbol_type_str(STT_OBJECT), "OBJECT") 1372 testing.expect_value(t, symbol_type_str(STT_FUNC), "FUNC") 1373 testing.expect_value(t, symbol_type_str(STT_SECTION), "SECTION") 1374 testing.expect_value(t, symbol_type_str(STT_FILE), "FILE") 1375 testing.expect_value(t, symbol_type_str(13), "0xd") 1376 1377 testing.expect_value(t, symbol_bind_str(STB_LOCAL), "LOCAL") 1378 testing.expect_value(t, symbol_bind_str(STB_GLOBAL), "GLOBAL") 1379 testing.expect_value(t, symbol_bind_str(STB_WEAK), "WEAK") 1380 testing.expect_value(t, symbol_bind_str(10), "0xa") 1381 1382 testing.expect_value(t, segment_type_str(PT_NULL), "NULL") 1383 testing.expect_value(t, segment_type_str(PT_LOAD), "LOAD") 1384 testing.expect_value(t, segment_type_str(PT_DYNAMIC), "DYNAMIC") 1385 testing.expect_value(t, segment_type_str(PT_INTERP), "INTERP") 1386 testing.expect_value(t, segment_type_str(PT_GNU_RELRO), "GNU_RELRO") 1387 testing.expect_value(t, segment_type_str(0xabc), "0xabc") 1388 } 1389 1390 @(test) 1391 test_flags_to_str :: proc(t: ^testing.T) { 1392 testing.expect_value(t, flags_to_str(0), " ") 1393 testing.expect_value(t, flags_to_str(PF_X), " X") 1394 testing.expect_value(t, flags_to_str(PF_W), " W ") 1395 testing.expect_value(t, flags_to_str(PF_R), "R ") 1396 testing.expect_value(t, flags_to_str(PF_R | PF_X), "R X") 1397 testing.expect_value(t, flags_to_str(PF_R | PF_W), "RW ") 1398 testing.expect_value(t, flags_to_str(PF_R | PF_W | PF_X), "RWX") 1399 testing.expect_value(t, flags_to_str(0xFFFFFFFFFFFFFFFF), "RWX") 1400 }