use std::collections::{BTreeMap, HashMap, HashSet};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::path::Path;
pub const ELF_MAGIC_BYTES: [u8; 4] = [0x7f, b'E', b'L', b'F'];
pub const EI_NIDENT: usize = 16;
pub const EI_MAG0: usize = 0;
pub const EI_MAG1: usize = 1;
pub const EI_MAG2: usize = 2;
pub const EI_MAG3: usize = 3;
pub const EI_CLASS: usize = 4;
pub const EI_DATA: usize = 5;
pub const EI_VERSION: usize = 6;
pub const EI_OSABI: usize = 7;
pub const EI_ABIVERSION: usize = 8;
pub const EI_PAD: usize = 9;
pub const ELFCLASS32: u8 = 1;
pub const ELFCLASS64: u8 = 2;
pub const ELFDATA2LSB: u8 = 1;
pub const ELFDATA2MSB: u8 = 2;
pub const EV_CURRENT: u32 = 1;
pub const ET_NONE: u16 = 0;
pub const ET_REL: u16 = 1;
pub const ET_EXEC: u16 = 2;
pub const ET_DYN: u16 = 3;
pub const ET_CORE: u16 = 4;
pub const EM_386: u16 = 3;
pub const EM_X86_64: u16 = 62;
pub const ELFOSABI_NONE: u8 = 0;
pub const ELFOSABI_SYSV: u8 = 0;
pub const ELFOSABI_HPUX: u8 = 1;
pub const ELFOSABI_NETBSD: u8 = 2;
pub const ELFOSABI_LINUX: u8 = 3;
pub const ELFOSABI_GNU: u8 = 3;
pub const ELFOSABI_SOLARIS: u8 = 6;
pub const ELFOSABI_AIX: u8 = 7;
pub const ELFOSABI_FREEBSD: u8 = 9;
pub const ELFOSABI_OPENBSD: u8 = 12;
pub const ELFOSABI_STANDALONE: u8 = 255;
pub const PT_NULL: u32 = 0;
pub const PT_LOAD: u32 = 1;
pub const PT_DYNAMIC: u32 = 2;
pub const PT_INTERP: u32 = 3;
pub const PT_NOTE: u32 = 4;
pub const PT_SHLIB: u32 = 5;
pub const PT_PHDR: u32 = 6;
pub const PT_TLS: u32 = 7;
pub const PT_LOOS: u32 = 0x60000000;
pub const PT_HIOS: u32 = 0x6FFFFFFF;
pub const PT_LOPROC: u32 = 0x70000000;
pub const PT_HIPROC: u32 = 0x7FFFFFFF;
pub const PT_GNU_EH_FRAME: u32 = 0x6474e550;
pub const PT_GNU_STACK: u32 = 0x6474e551;
pub const PT_GNU_RELRO: u32 = 0x6474e552;
pub const PT_GNU_PROPERTY: u32 = 0x6474e553;
pub const PF_X: u32 = 1;
pub const PF_W: u32 = 2;
pub const PF_R: u32 = 4;
pub const PF_MASKOS: u32 = 0x0ff00000;
pub const PF_MASKPROC: u32 = 0xf0000000;
pub const SHT_NULL: u32 = 0;
pub const SHT_PROGBITS: u32 = 1;
pub const SHT_SYMTAB: u32 = 2;
pub const SHT_STRTAB: u32 = 3;
pub const SHT_RELA: u32 = 4;
pub const SHT_HASH: u32 = 5;
pub const SHT_DYNAMIC: u32 = 6;
pub const SHT_NOTE: u32 = 7;
pub const SHT_NOBITS: u32 = 8;
pub const SHT_REL: u32 = 9;
pub const SHT_SHLIB: u32 = 10;
pub const SHT_DYNSYM: u32 = 11;
pub const SHT_INIT_ARRAY: u32 = 14;
pub const SHT_FINI_ARRAY: u32 = 15;
pub const SHT_PREINIT_ARRAY: u32 = 16;
pub const SHT_GROUP: u32 = 17;
pub const SHT_SYMTAB_SHNDX: u32 = 18;
pub const SHT_LOOS: u32 = 0x60000000;
pub const SHT_GNU_ATTRIBUTES: u32 = 0x6ffffff5;
pub const SHT_GNU_HASH: u32 = 0x6ffffff6;
pub const SHT_GNU_LIBLIST: u32 = 0x6ffffff7;
pub const SHT_GNU_VERDEF: u32 = 0x6ffffffd;
pub const SHT_GNU_VERNEED: u32 = 0x6ffffffe;
pub const SHT_GNU_VERSYM: u32 = 0x6fffffff;
pub const SHF_WRITE: u64 = 0x1;
pub const SHF_ALLOC: u64 = 0x2;
pub const SHF_EXECINSTR: u64 = 0x4;
pub const SHF_MERGE: u64 = 0x10;
pub const SHF_STRINGS: u64 = 0x20;
pub const SHF_INFO_LINK: u64 = 0x40;
pub const SHF_LINK_ORDER: u64 = 0x80;
pub const SHF_OS_NONCONFORMING: u64 = 0x100;
pub const SHF_GROUP: u64 = 0x200;
pub const SHF_TLS: u64 = 0x400;
pub const SHF_COMPRESSED: u64 = 0x800;
pub const SHF_MASKOS: u64 = 0x0ff00000;
pub const SHF_MASKPROC: u64 = 0xf0000000;
pub const SHF_EXCLUDE: u64 = 0x80000000;
pub const ALL_SHFLAGS: &[(u64, &str)] = &[
(SHF_WRITE, "W"),
(SHF_ALLOC, "A"),
(SHF_EXECINSTR, "X"),
(SHF_MERGE, "M"),
(SHF_STRINGS, "S"),
(SHF_INFO_LINK, "I"),
(SHF_LINK_ORDER, "L"),
(SHF_OS_NONCONFORMING, "O"),
(SHF_GROUP, "G"),
(SHF_TLS, "T"),
(SHF_COMPRESSED, "C"),
(SHF_EXCLUDE, "E"),
];
pub const STN_UNDEF: u16 = 0;
pub const STB_LOCAL: u8 = 0;
pub const STB_GLOBAL: u8 = 1;
pub const STB_WEAK: u8 = 2;
pub const STB_GNU_UNIQUE: u8 = 10;
pub const STB_LOOS: u8 = 10;
pub const STB_HIOS: u8 = 12;
pub const STB_LOPROC: u8 = 13;
pub const STB_HIPROC: u8 = 15;
pub const STT_NOTYPE: u8 = 0;
pub const STT_OBJECT: u8 = 1;
pub const STT_FUNC: u8 = 2;
pub const STT_SECTION: u8 = 3;
pub const STT_FILE: u8 = 4;
pub const STT_COMMON: u8 = 5;
pub const STT_TLS: u8 = 6;
pub const STT_GNU_IFUNC: u8 = 10;
pub const STT_LOOS: u8 = 10;
pub const STT_HIOS: u8 = 12;
pub const STT_LOPROC: u8 = 13;
pub const STT_HIPROC: u8 = 15;
pub const STV_DEFAULT: u8 = 0;
pub const STV_INTERNAL: u8 = 1;
pub const STV_HIDDEN: u8 = 2;
pub const STV_PROTECTED: u8 = 3;
pub const SHN_UNDEF: u16 = 0;
pub const SHN_ABS: u16 = 0xfff1;
pub const SHN_COMMON: u16 = 0xfff2;
pub const SHN_XINDEX: u16 = 0xffff;
pub const R_X86_64_NONE: u32 = 0;
pub const R_X86_64_64: u32 = 1;
pub const R_X86_64_PC32: u32 = 2;
pub const R_X86_64_GOT32: u32 = 3;
pub const R_X86_64_PLT32: u32 = 4;
pub const R_X86_64_COPY: u32 = 5;
pub const R_X86_64_GLOB_DAT: u32 = 6;
pub const R_X86_64_JUMP_SLOT: u32 = 7;
pub const R_X86_64_RELATIVE: u32 = 8;
pub const R_X86_64_GOTPCREL: u32 = 9;
pub const R_X86_64_32: u32 = 10;
pub const R_X86_64_32S: u32 = 11;
pub const R_X86_64_16: u32 = 12;
pub const R_X86_64_PC16: u32 = 13;
pub const R_X86_64_8: u32 = 14;
pub const R_X86_64_PC8: u32 = 15;
pub const R_X86_64_DTPMOD64: u32 = 16;
pub const R_X86_64_DTPOFF64: u32 = 17;
pub const R_X86_64_TPOFF64: u32 = 18;
pub const R_X86_64_TLSGD: u32 = 19;
pub const R_X86_64_TLSLD: u32 = 20;
pub const R_X86_64_DTPOFF32: u32 = 21;
pub const R_X86_64_GOTTPOFF: u32 = 22;
pub const R_X86_64_TPOFF32: u32 = 23;
pub const R_X86_64_PC64: u32 = 24;
pub const R_X86_64_GOTOFF64: u32 = 25;
pub const R_X86_64_GOTPC32: u32 = 26;
pub const R_X86_64_GOT64: u32 = 27;
pub const R_X86_64_GOTPCREL64: u32 = 28;
pub const R_X86_64_GOTPC64: u32 = 29;
pub const R_X86_64_GOTPLT64: u32 = 30;
pub const R_X86_64_PLTOFF64: u32 = 31;
pub const R_X86_64_SIZE32: u32 = 32;
pub const R_X86_64_SIZE64: u32 = 33;
pub const R_X86_64_GOTPC32_TLSDESC: u32 = 34;
pub const R_X86_64_TLSDESC_CALL: u32 = 35;
pub const R_X86_64_TLSDESC: u32 = 36;
pub const R_X86_64_IRELATIVE: u32 = 37;
pub const R_X86_64_RELATIVE64: u32 = 38;
pub const R_X86_64_PC32_BND: u32 = 39;
pub const R_X86_64_PLT32_BND: u32 = 40;
pub const R_X86_64_GOTPCRELX: u32 = 41;
pub const R_X86_64_REX_GOTPCRELX: u32 = 42;
pub fn x86_64_reloc_name(r_type: u32) -> &'static str {
match r_type {
R_X86_64_NONE => "R_X86_64_NONE",
R_X86_64_64 => "R_X86_64_64",
R_X86_64_PC32 => "R_X86_64_PC32",
R_X86_64_GOT32 => "R_X86_64_GOT32",
R_X86_64_PLT32 => "R_X86_64_PLT32",
R_X86_64_COPY => "R_X86_64_COPY",
R_X86_64_GLOB_DAT => "R_X86_64_GLOB_DAT",
R_X86_64_JUMP_SLOT => "R_X86_64_JUMP_SLOT",
R_X86_64_RELATIVE => "R_X86_64_RELATIVE",
R_X86_64_GOTPCREL => "R_X86_64_GOTPCREL",
R_X86_64_32 => "R_X86_64_32",
R_X86_64_32S => "R_X86_64_32S",
R_X86_64_16 => "R_X86_64_16",
R_X86_64_PC16 => "R_X86_64_PC16",
R_X86_64_8 => "R_X86_64_8",
R_X86_64_PC8 => "R_X86_64_PC8",
R_X86_64_DTPMOD64 => "R_X86_64_DTPMOD64",
R_X86_64_DTPOFF64 => "R_X86_64_DTPOFF64",
R_X86_64_TPOFF64 => "R_X86_64_TPOFF64",
R_X86_64_TLSGD => "R_X86_64_TLSGD",
R_X86_64_TLSLD => "R_X86_64_TLSLD",
R_X86_64_DTPOFF32 => "R_X86_64_DTPOFF32",
R_X86_64_GOTTPOFF => "R_X86_64_GOTTPOFF",
R_X86_64_TPOFF32 => "R_X86_64_TPOFF32",
R_X86_64_PC64 => "R_X86_64_PC64",
R_X86_64_GOTOFF64 => "R_X86_64_GOTOFF64",
R_X86_64_GOTPC32 => "R_X86_64_GOTPC32",
R_X86_64_GOT64 => "R_X86_64_GOT64",
R_X86_64_GOTPCREL64 => "R_X86_64_GOTPCREL64",
R_X86_64_GOTPC64 => "R_X86_64_GOTPC64",
R_X86_64_GOTPLT64 => "R_X86_64_GOTPLT64",
R_X86_64_PLTOFF64 => "R_X86_64_PLTOFF64",
R_X86_64_SIZE32 => "R_X86_64_SIZE32",
R_X86_64_SIZE64 => "R_X86_64_SIZE64",
R_X86_64_GOTPC32_TLSDESC => "R_X86_64_GOTPC32_TLSDESC",
R_X86_64_TLSDESC_CALL => "R_X86_64_TLSDESC_CALL",
R_X86_64_TLSDESC => "R_X86_64_TLSDESC",
R_X86_64_IRELATIVE => "R_X86_64_IRELATIVE",
R_X86_64_RELATIVE64 => "R_X86_64_RELATIVE64",
R_X86_64_PC32_BND => "R_X86_64_PC32_BND",
R_X86_64_PLT32_BND => "R_X86_64_PLT32_BND",
R_X86_64_GOTPCRELX => "R_X86_64_GOTPCRELX",
R_X86_64_REX_GOTPCRELX => "R_X86_64_REX_GOTPCRELX",
_ => "R_X86_64_UNKNOWN",
}
}
pub const R_386_NONE: u32 = 0;
pub const R_386_32: u32 = 1;
pub const R_386_PC32: u32 = 2;
pub const R_386_GOT32: u32 = 3;
pub const R_386_PLT32: u32 = 4;
pub const R_386_COPY: u32 = 5;
pub const R_386_GLOB_DAT: u32 = 6;
pub const R_386_JUMP_SLOT: u32 = 7;
pub const R_386_RELATIVE: u32 = 8;
pub const R_386_GOTOFF: u32 = 9;
pub const R_386_GOTPC: u32 = 10;
pub const R_386_32PLT: u32 = 11;
pub const R_386_TLS_TPOFF: u32 = 14;
pub const R_386_TLS_IE: u32 = 15;
pub const R_386_TLS_GOTIE: u32 = 16;
pub const R_386_TLS_LE: u32 = 17;
pub const R_386_TLS_GD: u32 = 18;
pub const R_386_TLS_LDM: u32 = 19;
pub const R_386_16: u32 = 20;
pub const R_386_PC16: u32 = 21;
pub const R_386_8: u32 = 22;
pub const R_386_PC8: u32 = 23;
pub const R_386_TLS_GD_32: u32 = 24;
pub const R_386_TLS_GD_PUSH: u32 = 25;
pub const R_386_TLS_GD_CALL: u32 = 26;
pub const R_386_TLS_GD_POP: u32 = 27;
pub const R_386_TLS_LDM_32: u32 = 28;
pub const R_386_TLS_LDM_PUSH: u32 = 29;
pub const R_386_TLS_LDM_CALL: u32 = 30;
pub const R_386_TLS_LDM_POP: u32 = 31;
pub const R_386_TLS_LDO_32: u32 = 32;
pub const R_386_TLS_IE_32: u32 = 33;
pub const R_386_TLS_LE_32: u32 = 34;
pub const R_386_TLS_DTPMOD32: u32 = 35;
pub const R_386_TLS_DTPOFF32: u32 = 36;
pub const R_386_TLS_TPOFF32: u32 = 37;
pub const R_386_SIZE32: u32 = 38;
pub const R_386_TLS_GOTDESC: u32 = 39;
pub const R_386_TLS_DESC_CALL: u32 = 40;
pub const R_386_TLS_DESC: u32 = 41;
pub const R_386_IRELATIVE: u32 = 42;
pub const R_386_GOT32X: u32 = 43;
pub fn i386_reloc_name(r_type: u32) -> &'static str {
match r_type {
R_386_NONE => "R_386_NONE",
R_386_32 => "R_386_32",
R_386_PC32 => "R_386_PC32",
R_386_GOT32 => "R_386_GOT32",
R_386_PLT32 => "R_386_PLT32",
R_386_COPY => "R_386_COPY",
R_386_GLOB_DAT => "R_386_GLOB_DAT",
R_386_JUMP_SLOT => "R_386_JUMP_SLOT",
R_386_RELATIVE => "R_386_RELATIVE",
R_386_GOTOFF => "R_386_GOTOFF",
R_386_GOTPC => "R_386_GOTPC",
R_386_32PLT => "R_386_32PLT",
R_386_TLS_TPOFF => "R_386_TLS_TPOFF",
R_386_TLS_IE => "R_386_TLS_IE",
R_386_TLS_GOTIE => "R_386_TLS_GOTIE",
R_386_TLS_LE => "R_386_TLS_LE",
R_386_TLS_GD => "R_386_TLS_GD",
R_386_TLS_LDM => "R_386_TLS_LDM",
R_386_16 => "R_386_16",
R_386_PC16 => "R_386_PC16",
R_386_8 => "R_386_8",
R_386_PC8 => "R_386_PC8",
R_386_TLS_GD_32 => "R_386_TLS_GD_32",
R_386_TLS_GD_PUSH => "R_386_TLS_GD_PUSH",
R_386_TLS_GD_CALL => "R_386_TLS_GD_CALL",
R_386_TLS_GD_POP => "R_386_TLS_GD_POP",
R_386_TLS_LDM_32 => "R_386_TLS_LDM_32",
R_386_TLS_LDM_PUSH => "R_386_TLS_LDM_PUSH",
R_386_TLS_LDM_CALL => "R_386_TLS_LDM_CALL",
R_386_TLS_LDM_POP => "R_386_TLS_LDM_POP",
R_386_TLS_LDO_32 => "R_386_TLS_LDO_32",
R_386_TLS_IE_32 => "R_386_TLS_IE_32",
R_386_TLS_LE_32 => "R_386_TLS_LE_32",
R_386_TLS_DTPMOD32 => "R_386_TLS_DTPMOD32",
R_386_TLS_DTPOFF32 => "R_386_TLS_DTPOFF32",
R_386_TLS_TPOFF32 => "R_386_TLS_TPOFF32",
R_386_SIZE32 => "R_386_SIZE32",
R_386_TLS_GOTDESC => "R_386_TLS_GOTDESC",
R_386_TLS_DESC_CALL => "R_386_TLS_DESC_CALL",
R_386_TLS_DESC => "R_386_TLS_DESC",
R_386_IRELATIVE => "R_386_IRELATIVE",
R_386_GOT32X => "R_386_GOT32X",
_ => "R_386_UNKNOWN",
}
}
pub const DT_NULL: u64 = 0;
pub const DT_NEEDED: u64 = 1;
pub const DT_PLTRELSZ: u64 = 2;
pub const DT_PLTGOT: u64 = 3;
pub const DT_HASH: u64 = 4;
pub const DT_STRTAB: u64 = 5;
pub const DT_SYMTAB: u64 = 6;
pub const DT_RELA: u64 = 7;
pub const DT_RELASZ: u64 = 8;
pub const DT_RELAENT: u64 = 9;
pub const DT_STRSZ: u64 = 10;
pub const DT_SYMENT: u64 = 11;
pub const DT_INIT: u64 = 12;
pub const DT_FINI: u64 = 13;
pub const DT_SONAME: u64 = 14;
pub const DT_RPATH: u64 = 15;
pub const DT_SYMBOLIC: u64 = 16;
pub const DT_REL: u64 = 17;
pub const DT_RELSZ: u64 = 18;
pub const DT_RELENT: u64 = 19;
pub const DT_PLTREL: u64 = 20;
pub const DT_DEBUG: u64 = 21;
pub const DT_TEXTREL: u64 = 22;
pub const DT_JMPREL: u64 = 23;
pub const DT_BIND_NOW: u64 = 24;
pub const DT_INIT_ARRAY: u64 = 25;
pub const DT_FINI_ARRAY: u64 = 26;
pub const DT_INIT_ARRAYSZ: u64 = 27;
pub const DT_FINI_ARRAYSZ: u64 = 28;
pub const DT_RUNPATH: u64 = 29;
pub const DT_FLAGS: u64 = 30;
pub const DT_ENCODING: u64 = 32;
pub const DT_PREINIT_ARRAY: u64 = 32;
pub const DT_PREINIT_ARRAYSZ: u64 = 33;
pub const DT_SYMTAB_SHNDX: u64 = 34;
pub const DT_GNU_HASH: u64 = 0x6ffffef5;
pub const DT_TLSDESC_PLT: u64 = 0x6ffffef6;
pub const DT_TLSDESC_GOT: u64 = 0x6ffffef7;
pub const DT_GNU_CONFLICT: u64 = 0x6ffffef8;
pub const DT_GNU_LIBLIST: u64 = 0x6ffffef9;
pub const DT_CONFIG: u64 = 0x6ffffefa;
pub const DT_DEPAUDIT: u64 = 0x6ffffefb;
pub const DT_AUDIT: u64 = 0x6ffffefc;
pub const DT_PLTPAD: u64 = 0x6ffffefd;
pub const DT_MOVETAB: u64 = 0x6ffffefe;
pub const DT_SYMINFO: u64 = 0x6ffffeff;
pub const DT_RELACOUNT: u64 = 0x6ffffff9;
pub const DT_RELCOUNT: u64 = 0x6ffffffa;
pub const DT_FLAGS_1: u64 = 0x6ffffffb;
pub const DT_VERDEF: u64 = 0x6ffffffc;
pub const DT_VERDEFNUM: u64 = 0x6ffffffd;
pub const DT_VERNEED: u64 = 0x6ffffffe;
pub const DT_VERNEEDNUM: u64 = 0x6fffffff;
pub const DT_VERSYM: u64 = 0x6ffffff0;
pub const ALL_DYNAMIC_TAGS: &[(u64, &str)] = &[
(DT_NULL, "DT_NULL"),
(DT_NEEDED, "DT_NEEDED"),
(DT_PLTRELSZ, "DT_PLTRELSZ"),
(DT_PLTGOT, "DT_PLTGOT"),
(DT_HASH, "DT_HASH"),
(DT_STRTAB, "DT_STRTAB"),
(DT_SYMTAB, "DT_SYMTAB"),
(DT_RELA, "DT_RELA"),
(DT_RELASZ, "DT_RELASZ"),
(DT_RELAENT, "DT_RELAENT"),
(DT_STRSZ, "DT_STRSZ"),
(DT_SYMENT, "DT_SYMENT"),
(DT_INIT, "DT_INIT"),
(DT_FINI, "DT_FINI"),
(DT_SONAME, "DT_SONAME"),
(DT_RPATH, "DT_RPATH"),
(DT_SYMBOLIC, "DT_SYMBOLIC"),
(DT_REL, "DT_REL"),
(DT_RELSZ, "DT_RELSZ"),
(DT_RELENT, "DT_RELENT"),
(DT_PLTREL, "DT_PLTREL"),
(DT_DEBUG, "DT_DEBUG"),
(DT_TEXTREL, "DT_TEXTREL"),
(DT_JMPREL, "DT_JMPREL"),
(DT_BIND_NOW, "DT_BIND_NOW"),
(DT_INIT_ARRAY, "DT_INIT_ARRAY"),
(DT_FINI_ARRAY, "DT_FINI_ARRAY"),
(DT_INIT_ARRAYSZ, "DT_INIT_ARRAYSZ"),
(DT_FINI_ARRAYSZ, "DT_FINI_ARRAYSZ"),
(DT_RUNPATH, "DT_RUNPATH"),
(DT_FLAGS, "DT_FLAGS"),
(DT_ENCODING, "DT_ENCODING"),
(DT_PREINIT_ARRAY, "DT_PREINIT_ARRAY"),
(DT_PREINIT_ARRAYSZ, "DT_PREINIT_ARRAYSZ"),
(DT_SYMTAB_SHNDX, "DT_SYMTAB_SHNDX"),
(DT_GNU_HASH, "DT_GNU_HASH"),
(DT_TLSDESC_PLT, "DT_TLSDESC_PLT"),
(DT_TLSDESC_GOT, "DT_TLSDESC_GOT"),
(DT_GNU_CONFLICT, "DT_GNU_CONFLICT"),
(DT_GNU_LIBLIST, "DT_GNU_LIBLIST"),
(DT_CONFIG, "DT_CONFIG"),
(DT_DEPAUDIT, "DT_DEPAUDIT"),
(DT_AUDIT, "DT_AUDIT"),
(DT_PLTPAD, "DT_PLTPAD"),
(DT_MOVETAB, "DT_MOVETAB"),
(DT_SYMINFO, "DT_SYMINFO"),
(DT_RELACOUNT, "DT_RELACOUNT"),
(DT_RELCOUNT, "DT_RELCOUNT"),
(DT_FLAGS_1, "DT_FLAGS_1"),
(DT_VERDEF, "DT_VERDEF"),
(DT_VERDEFNUM, "DT_VERDEFNUM"),
(DT_VERNEED, "DT_VERNEED"),
(DT_VERNEEDNUM, "DT_VERNEEDNUM"),
(DT_VERSYM, "DT_VERSYM"),
];
pub const DF_ORIGIN: u64 = 0x1;
pub const DF_SYMBOLIC: u64 = 0x2;
pub const DF_TEXTREL: u64 = 0x4;
pub const DF_BIND_NOW: u64 = 0x8;
pub const DF_STATIC_TLS: u64 = 0x10;
pub const DF_1_NOW: u64 = 0x1;
pub const DF_1_GLOBAL: u64 = 0x2;
pub const DF_1_NODELETE: u64 = 0x8;
pub const DF_1_INITFIRST: u64 = 0x20;
pub const DF_1_NOOPEN: u64 = 0x40;
pub const DF_1_ORIGIN: u64 = 0x80;
pub const DF_1_INTERPOSE: u64 = 0x400;
pub const DF_1_NODEFLIB: u64 = 0x800;
pub const DF_1_NODUMP: u64 = 0x1000;
pub const DF_1_CONFALT: u64 = 0x2000;
pub const DF_1_PIE: u64 = 0x8000000;
pub const NT_GNU_BUILD_ID: u32 = 3;
pub const NT_GNU_GOLD_VERSION: u32 = 4;
pub const NT_GNU_PROPERTY_TYPE_0: u32 = 5;
pub const NT_GNU_ABI_TAG: u32 = 1;
pub const ELF_NOTE_GNU: &str = "GNU";
pub const ELF_NOTE_LINUX: &str = "Linux";
pub const ELFCOMPRESS_ZLIB: u32 = 1;
pub const ELFCOMPRESS_ZSTD: u32 = 2;
pub const ELFCOMPRESS_LOOS: u32 = 0x60000000;
pub const ELFCOMPRESS_HIOS: u32 = 0x6fffffff;
pub const CH_TYPE_ZLIB: u32 = 1;
pub const CH_TYPE_ZSTD: u32 = 2;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ElfIdent {
pub magic: [u8; 4],
pub class: u8,
pub data: u8,
pub version: u8,
pub osabi: u8,
pub abiversion: u8,
pub padding: [u8; 7],
}
impl Default for ElfIdent {
fn default() -> Self {
let mut ident = ElfIdent {
magic: ELF_MAGIC_BYTES,
class: ELFCLASS64,
data: ELFDATA2LSB,
version: EV_CURRENT as u8,
osabi: ELFOSABI_NONE,
abiversion: 0,
padding: [0; 7],
};
ident
}
}
impl ElfIdent {
pub fn new_x86_64() -> Self {
ElfIdent {
magic: ELF_MAGIC_BYTES,
class: ELFCLASS64,
data: ELFDATA2LSB,
version: 1,
osabi: ELFOSABI_SYSV,
abiversion: 0,
padding: [0; 7],
}
}
pub fn new_x86_32() -> Self {
ElfIdent {
magic: ELF_MAGIC_BYTES,
class: ELFCLASS32,
data: ELFDATA2LSB,
version: 1,
osabi: ELFOSABI_SYSV,
abiversion: 0,
padding: [0; 7],
}
}
pub fn is_64bit(&self) -> bool {
self.class == ELFCLASS64
}
pub fn is_32bit(&self) -> bool {
self.class == ELFCLASS32
}
pub fn is_valid_magic(&self) -> bool {
self.magic == ELF_MAGIC_BYTES
}
pub fn to_bytes(&self) -> [u8; 16] {
let mut buf = [0u8; 16];
buf[EI_MAG0..EI_MAG0 + 4].copy_from_slice(&self.magic);
buf[EI_CLASS] = self.class;
buf[EI_DATA] = self.data;
buf[EI_VERSION] = self.version;
buf[EI_OSABI] = self.osabi;
buf[EI_ABIVERSION] = self.abiversion;
buf[EI_PAD..].copy_from_slice(&self.padding);
buf
}
pub fn from_bytes(bytes: &[u8; 16]) -> Self {
let mut magic = [0u8; 4];
magic.copy_from_slice(&bytes[0..4]);
let mut padding = [0u8; 7];
padding.copy_from_slice(&bytes[9..16]);
ElfIdent {
magic,
class: bytes[EI_CLASS],
data: bytes[EI_DATA],
version: bytes[EI_VERSION],
osabi: bytes[EI_OSABI],
abiversion: bytes[EI_ABIVERSION],
padding,
}
}
}
#[derive(Debug, Clone)]
pub struct X86ElfHeader {
pub ident: ElfIdent,
pub e_type: u16,
pub e_machine: u16,
pub e_version: u32,
pub e_entry: u64,
pub e_phoff: u64,
pub e_shoff: u64,
pub e_flags: u32,
pub e_ehsize: u16,
pub e_phentsize: u16,
pub e_phnum: u16,
pub e_shentsize: u16,
pub e_shnum: u16,
pub e_shstrndx: u16,
}
impl Default for X86ElfHeader {
fn default() -> Self {
X86ElfHeader {
ident: ElfIdent::new_x86_64(),
e_type: ET_REL,
e_machine: EM_X86_64,
e_version: 1,
e_entry: 0,
e_phoff: 0,
e_shoff: 0,
e_flags: 0,
e_ehsize: 64,
e_phentsize: 56,
e_phnum: 0,
e_shentsize: 64,
e_shnum: 0,
e_shstrndx: 0,
}
}
}
impl X86ElfHeader {
pub fn new_x86_64() -> Self {
X86ElfHeader {
ident: ElfIdent::new_x86_64(),
e_type: ET_REL,
e_machine: EM_X86_64,
e_version: 1,
e_entry: 0,
e_phoff: 0,
e_shoff: 0,
e_flags: 0,
e_ehsize: 64,
e_phentsize: 56,
e_phnum: 0,
e_shentsize: 64,
e_shnum: 0,
e_shstrndx: 0,
}
}
pub fn new_x86_32() -> Self {
X86ElfHeader {
ident: ElfIdent::new_x86_32(),
e_type: ET_REL,
e_machine: EM_386,
e_version: 1,
e_entry: 0,
e_phoff: 0,
e_shoff: 0,
e_flags: 0,
e_ehsize: 52,
e_phentsize: 32,
e_phnum: 0,
e_shentsize: 40,
e_shnum: 0,
e_shstrndx: 0,
}
}
pub fn is_64bit(&self) -> bool {
self.ident.is_64bit()
}
pub fn is_shared_object(&self) -> bool {
self.e_type == ET_DYN
}
pub fn is_executable(&self) -> bool {
self.e_type == ET_EXEC
}
pub fn is_relocatable(&self) -> bool {
self.e_type == ET_REL
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct X86ProgramHeader {
pub p_type: u32,
pub p_flags: u32,
pub p_offset: u64,
pub p_vaddr: u64,
pub p_paddr: u64,
pub p_filesz: u64,
pub p_memsz: u64,
pub p_align: u64,
}
impl X86ProgramHeader {
pub fn new_load(vaddr: u64, offset: u64, filesz: u64, memsz: u64, flags: u32) -> Self {
X86ProgramHeader {
p_type: PT_LOAD,
p_flags: flags,
p_offset: offset,
p_vaddr: vaddr,
p_paddr: vaddr,
p_filesz: filesz,
p_memsz: memsz,
p_align: 0x1000,
}
}
pub fn new_dynamic(offset: u64, size: u64) -> Self {
X86ProgramHeader {
p_type: PT_DYNAMIC,
p_flags: PF_R | PF_W,
p_offset: offset,
p_vaddr: 0,
p_paddr: 0,
p_filesz: size,
p_memsz: size,
p_align: 8,
}
}
pub fn new_interp(offset: u64, size: u64) -> Self {
X86ProgramHeader {
p_type: PT_INTERP,
p_flags: PF_R,
p_offset: offset,
p_vaddr: 0,
p_paddr: 0,
p_filesz: size,
p_memsz: size,
p_align: 1,
}
}
pub fn new_note(offset: u64, size: u64) -> Self {
X86ProgramHeader {
p_type: PT_NOTE,
p_flags: PF_R,
p_offset: offset,
p_vaddr: 0,
p_paddr: 0,
p_filesz: size,
p_memsz: size,
p_align: 4,
}
}
pub fn new_tls(vaddr: u64, offset: u64, filesz: u64, memsz: u64) -> Self {
X86ProgramHeader {
p_type: PT_TLS,
p_flags: PF_R,
p_offset: offset,
p_vaddr: vaddr,
p_paddr: vaddr,
p_filesz: filesz,
p_memsz: memsz,
p_align: 1,
}
}
pub fn new_gnu_stack(flags: u32, size: u64) -> Self {
X86ProgramHeader {
p_type: PT_GNU_STACK,
p_flags: flags,
p_offset: 0,
p_vaddr: 0,
p_paddr: 0,
p_filesz: 0,
p_memsz: size,
p_align: 0,
}
}
pub fn new_gnu_relro(offset: u64, size: u64) -> Self {
X86ProgramHeader {
p_type: PT_GNU_RELRO,
p_flags: PF_R,
p_offset: offset,
p_vaddr: 0,
p_paddr: 0,
p_filesz: size,
p_memsz: size,
p_align: 1,
}
}
pub fn new_gnu_eh_frame(offset: u64, size: u64) -> Self {
X86ProgramHeader {
p_type: PT_GNU_EH_FRAME,
p_flags: PF_R,
p_offset: offset,
p_vaddr: 0,
p_paddr: 0,
p_filesz: size,
p_memsz: size,
p_align: 4,
}
}
pub fn new_gnu_property(offset: u64, size: u64) -> Self {
X86ProgramHeader {
p_type: PT_GNU_PROPERTY,
p_flags: PF_R,
p_offset: offset,
p_vaddr: 0,
p_paddr: 0,
p_filesz: size,
p_memsz: size,
p_align: 8,
}
}
pub fn type_name(&self) -> &'static str {
match self.p_type {
PT_NULL => "PT_NULL",
PT_LOAD => "PT_LOAD",
PT_DYNAMIC => "PT_DYNAMIC",
PT_INTERP => "PT_INTERP",
PT_NOTE => "PT_NOTE",
PT_SHLIB => "PT_SHLIB",
PT_PHDR => "PT_PHDR",
PT_TLS => "PT_TLS",
PT_GNU_EH_FRAME => "PT_GNU_EH_FRAME",
PT_GNU_STACK => "PT_GNU_STACK",
PT_GNU_RELRO => "PT_GNU_RELRO",
PT_GNU_PROPERTY => "PT_GNU_PROPERTY",
_ => {
if self.p_type >= PT_LOOS && self.p_type <= PT_HIOS {
"PT_LOOS+"
} else if self.p_type >= PT_LOPROC && self.p_type <= PT_HIPROC {
"PT_LOPROC+"
} else {
"PT_UNKNOWN"
}
}
}
}
pub fn flags_string(&self) -> String {
let mut s = String::new();
if self.p_flags & PF_R != 0 {
s.push('R');
}
if self.p_flags & PF_W != 0 {
s.push('W');
}
if self.p_flags & PF_X != 0 {
s.push('X');
}
if s.is_empty() {
s.push_str("---");
}
s
}
}
#[derive(Debug, Clone)]
pub struct X86SectionHeader {
pub sh_name: u32,
pub sh_type: u32,
pub sh_flags: u64,
pub sh_addr: u64,
pub sh_offset: u64,
pub sh_size: u64,
pub sh_link: u32,
pub sh_info: u32,
pub sh_addralign: u64,
pub sh_entsize: u64,
pub name: String,
}
impl X86SectionHeader {
pub fn new(name: &str, sh_type: u32, sh_flags: u64, sh_offset: u64, sh_size: u64) -> Self {
X86SectionHeader {
sh_name: 0,
sh_type,
sh_flags,
sh_addr: 0,
sh_offset,
sh_size,
sh_link: 0,
sh_info: 0,
sh_addralign: 1,
sh_entsize: 0,
name: name.to_string(),
}
}
pub fn type_name(&self) -> &'static str {
match self.sh_type {
SHT_NULL => "NULL",
SHT_PROGBITS => "PROGBITS",
SHT_SYMTAB => "SYMTAB",
SHT_STRTAB => "STRTAB",
SHT_RELA => "RELA",
SHT_HASH => "HASH",
SHT_DYNAMIC => "DYNAMIC",
SHT_NOTE => "NOTE",
SHT_NOBITS => "NOBITS",
SHT_REL => "REL",
SHT_SHLIB => "SHLIB",
SHT_DYNSYM => "DYNSYM",
SHT_INIT_ARRAY => "INIT_ARRAY",
SHT_FINI_ARRAY => "FINI_ARRAY",
SHT_PREINIT_ARRAY => "PREINIT_ARRAY",
SHT_GROUP => "GROUP",
SHT_SYMTAB_SHNDX => "SYMTAB_SHNDX",
SHT_GNU_ATTRIBUTES => "GNU_ATTRIBUTES",
SHT_GNU_HASH => "GNU_HASH",
SHT_GNU_LIBLIST => "GNU_LIBLIST",
SHT_GNU_VERDEF => "GNU_VERDEF",
SHT_GNU_VERNEED => "GNU_VERNEED",
SHT_GNU_VERSYM => "GNU_VERSYM",
_ => "UNKNOWN",
}
}
pub fn flags_string(&self) -> String {
let mut flags = String::new();
if self.sh_flags & SHF_WRITE != 0 {
flags.push('W');
}
if self.sh_flags & SHF_ALLOC != 0 {
flags.push('A');
}
if self.sh_flags & SHF_EXECINSTR != 0 {
flags.push('X');
}
if self.sh_flags & SHF_MERGE != 0 {
flags.push('M');
}
if self.sh_flags & SHF_STRINGS != 0 {
flags.push('S');
}
if self.sh_flags & SHF_GROUP != 0 {
flags.push('G');
}
if self.sh_flags & SHF_TLS != 0 {
flags.push('T');
}
if self.sh_flags & SHF_COMPRESSED != 0 {
flags.push('C');
}
if self.sh_flags & SHF_EXCLUDE != 0 {
flags.push('E');
}
if flags.is_empty() {
flags.push_str("-");
}
flags
}
}
#[derive(Debug, Clone)]
pub struct X86Symbol {
pub st_name: u32,
pub st_info: u8,
pub st_other: u8,
pub st_shndx: u16,
pub st_value: u64,
pub st_size: u64,
pub name: String,
}
impl X86Symbol {
pub fn new(name: &str, st_info: u8, st_shndx: u16, st_value: u64, st_size: u64) -> Self {
X86Symbol {
st_name: 0,
st_info,
st_other: 0,
st_shndx,
st_value,
st_size,
name: name.to_string(),
}
}
pub fn bind(&self) -> u8 {
self.st_info >> 4
}
pub fn st_type(&self) -> u8 {
self.st_info & 0x0f
}
pub fn visibility(&self) -> u8 {
self.st_other & 0x03
}
pub fn bind_name(&self) -> &'static str {
match self.bind() {
STB_LOCAL => "LOCAL",
STB_GLOBAL => "GLOBAL",
STB_WEAK => "WEAK",
STB_GNU_UNIQUE => "GNU_UNIQUE",
_ => "UNKNOWN",
}
}
pub fn type_name(&self) -> &'static str {
match self.st_type() {
STT_NOTYPE => "NOTYPE",
STT_OBJECT => "OBJECT",
STT_FUNC => "FUNC",
STT_SECTION => "SECTION",
STT_FILE => "FILE",
STT_COMMON => "COMMON",
STT_TLS => "TLS",
STT_GNU_IFUNC => "GNU_IFUNC",
_ => "UNKNOWN",
}
}
pub fn visibility_name(&self) -> &'static str {
match self.visibility() {
STV_DEFAULT => "DEFAULT",
STV_INTERNAL => "INTERNAL",
STV_HIDDEN => "HIDDEN",
STV_PROTECTED => "PROTECTED",
_ => "UNKNOWN",
}
}
pub fn is_defined(&self) -> bool {
self.st_shndx != SHN_UNDEF
}
}
#[derive(Debug, Clone)]
pub struct X86Relocation {
pub r_offset: u64,
pub r_type: u32,
pub r_sym: u32,
pub r_addend: i64,
pub symbol_name: Option<String>,
}
impl X86Relocation {
pub fn new(offset: u64, r_type: u32, sym: u32, addend: i64) -> Self {
X86Relocation {
r_offset: offset,
r_type,
r_sym: sym,
r_addend: addend,
symbol_name: None,
}
}
pub fn type_name(&self, machine: u16) -> &'static str {
match machine {
EM_X86_64 => x86_64_reloc_name(self.r_type),
EM_386 => i386_reloc_name(self.r_type),
_ => "UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86DynamicEntry {
pub d_tag: u64,
pub d_val: u64,
}
impl X86DynamicEntry {
pub fn new(tag: u64, val: u64) -> Self {
X86DynamicEntry {
d_tag: tag,
d_val: val,
}
}
pub fn tag_name(&self) -> &'static str {
for &(tag, name) in ALL_DYNAMIC_TAGS {
if tag == self.d_tag {
return name;
}
}
"UNKNOWN"
}
}
#[derive(Debug, Clone)]
pub struct X86Verdef {
pub vd_version: u16,
pub vd_flags: u16,
pub vd_ndx: u16,
pub vd_cnt: u16,
pub vd_hash: u32,
pub vd_aux: u32,
pub vd_next: u32,
pub version_name: String,
}
impl X86Verdef {
pub fn new(index: u16, hash: u32, name: &str) -> Self {
X86Verdef {
vd_version: 1,
vd_flags: 0,
vd_ndx: index,
vd_cnt: 1,
vd_hash: hash,
vd_aux: 20,
vd_next: 0,
version_name: name.to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct X86Verneed {
pub vn_version: u16,
pub vn_cnt: u16,
pub vn_file: u32,
pub vn_aux: u32,
pub vn_next: u32,
pub file_name: String,
pub needed_versions: Vec<X86Vernaux>,
}
impl X86Verneed {
pub fn new(file: &str) -> Self {
X86Verneed {
vn_version: 1,
vn_cnt: 0,
vn_file: 0,
vn_aux: 16,
vn_next: 0,
file_name: file.to_string(),
needed_versions: Vec::new(),
}
}
pub fn add_needed(&mut self, hash: u32, flags: u16, other: u16, name: &str) {
self.needed_versions.push(X86Vernaux {
vna_hash: hash,
vna_flags: flags,
vna_other: other,
vna_name: name.to_string(),
});
self.vn_cnt += 1;
}
}
#[derive(Debug, Clone)]
pub struct X86Vernaux {
pub vna_hash: u32,
pub vna_flags: u16,
pub vna_other: u16,
pub vna_name: String,
}
#[derive(Debug, Clone)]
pub struct X86Note {
pub n_type: u32,
pub n_name: String,
pub n_desc: Vec<u8>,
}
impl X86Note {
pub fn new(n_type: u32, name: &str, desc: Vec<u8>) -> Self {
X86Note {
n_type,
n_name: name.to_string(),
n_desc: desc,
}
}
pub fn new_build_id(build_id: &[u8]) -> Self {
X86Note::new(NT_GNU_BUILD_ID, ELF_NOTE_GNU, build_id.to_vec())
}
pub fn new_abi_tag(os: u32, major: u32, minor: u32, patch: u32) -> Self {
let mut desc = Vec::with_capacity(16);
desc.extend_from_slice(&os.to_le_bytes());
desc.extend_from_slice(&major.to_le_bytes());
desc.extend_from_slice(&minor.to_le_bytes());
desc.extend_from_slice(&patch.to_le_bytes());
X86Note::new(NT_GNU_ABI_TAG, ELF_NOTE_GNU, desc)
}
pub fn new_gold_version(version: &str) -> Self {
X86Note::new(NT_GNU_GOLD_VERSION, ELF_NOTE_GNU, version.as_bytes().to_vec())
}
pub fn type_name(&self) -> &'static str {
match self.n_type {
NT_GNU_BUILD_ID => "NT_GNU_BUILD_ID",
NT_GNU_GOLD_VERSION => "NT_GNU_GOLD_VERSION",
NT_GNU_PROPERTY_TYPE_0 => "NT_GNU_PROPERTY_TYPE_0",
NT_GNU_ABI_TAG => "NT_GNU_ABI_TAG",
_ => "UNKNOWN",
}
}
}
#[derive(Debug, Clone)]
pub struct X86HashTable {
pub nbucket: u32,
pub nchain: u32,
pub buckets: Vec<u32>,
pub chains: Vec<u32>,
}
impl X86HashTable {
pub fn new(nbucket: u32, nchain: u32) -> Self {
X86HashTable {
nbucket,
nchain,
buckets: vec![0u32; nbucket as usize],
chains: vec![0u32; nchain as usize],
}
}
pub fn lookup(&self, name_hash: u32, get_sym_hash: impl Fn(usize) -> Option<u32>) -> Option<usize> {
let bucket_idx = (name_hash % self.nbucket) as usize;
if bucket_idx >= self.buckets.len() {
return None;
}
let mut sym_idx = self.buckets[bucket_idx] as usize;
while sym_idx != 0 && sym_idx < self.chains.len() {
if let Some(sh) = get_sym_hash(sym_idx) {
if sh == name_hash {
return Some(sym_idx);
}
}
sym_idx = self.chains[sym_idx] as usize;
}
None
}
}
#[derive(Debug, Clone)]
pub struct X86GnuHashTable {
pub nbuckets: u32,
pub symoffset: u32,
pub bloom_size: u32,
pub bloom_shift: u32,
pub bloom: Vec<u64>,
pub buckets: Vec<u32>,
pub chain: Vec<u32>,
}
impl X86GnuHashTable {
pub fn new(nbuckets: u32, symoffset: u32, bloom_size: u32, bloom_shift: u32) -> Self {
X86GnuHashTable {
nbuckets,
symoffset,
bloom_size,
bloom_shift,
bloom: vec![0u64; bloom_size as usize],
buckets: vec![0u32; nbuckets as usize],
chain: Vec::new(),
}
}
pub fn lookup(&self, name_hash: u32, get_hash: impl Fn(usize) -> Option<u32>) -> Option<usize> {
if self.bloom_size == 0 {
return None;
}
let bm = name_hash;
let bit0 = (bm / 64) % self.bloom_size;
let bit1 = ((bm >> self.bloom_shift) / 64) % self.bloom_size;
let mask0 = 1u64 << (bm % 64);
let mask1 = 1u64 << ((bm >> self.bloom_shift) % 64);
if (self.bloom[bit0 as usize] & mask0) == 0 || (self.bloom[bit1 as usize] & mask1) == 0 {
return None;
}
let bucket_idx = (name_hash % self.nbuckets) as usize;
if bucket_idx >= self.buckets.len() {
return None;
}
let mut sym_idx = self.buckets[bucket_idx] as usize;
loop {
if sym_idx >= self.chain.len() {
return None;
}
let h = self.chain[sym_idx];
if (h | 1) == (name_hash | 1) {
if let Some(sh) = get_hash(sym_idx + self.symoffset as usize) {
if sh == name_hash {
return Some(sym_idx + self.symoffset as usize);
}
}
}
if h & 1 != 0 {
break;
}
sym_idx += 1;
}
None
}
}
#[derive(Debug, Clone)]
pub struct X86ComdatGroup {
pub name: String,
pub signature: u32,
pub flags: u32,
pub member_sections: Vec<u32>,
}
impl X86ComdatGroup {
pub fn new(name: &str, signature: u32) -> Self {
X86ComdatGroup {
name: name.to_string(),
signature,
flags: 0,
member_sections: Vec::new(),
}
}
pub fn add_member(&mut self, section_index: u32) {
self.member_sections.push(section_index);
}
}
#[derive(Debug, Clone)]
pub struct X86CompressedSection {
pub ch_type: u32,
pub ch_size: u64,
pub ch_addralign: u64,
pub decompressed_data: Vec<u8>,
}
impl X86CompressedSection {
pub fn new(ch_type: u32, original_size: u64, alignment: u64, data: Vec<u8>) -> Self {
X86CompressedSection {
ch_type,
ch_size: original_size,
ch_addralign: alignment,
decompressed_data: data,
}
}
pub fn is_zlib(&self) -> bool {
self.ch_type == ELFCOMPRESS_ZLIB
}
pub fn is_zstd(&self) -> bool {
self.ch_type == ELFCOMPRESS_ZSTD
}
}
#[derive(Debug, Clone)]
pub struct ArmExidxEntry {
pub addr: u64,
pub data: u32,
}
impl ArmExidxEntry {
pub fn new(addr: u64, data: u32) -> Self {
ArmExidxEntry { addr, data }
}
}
#[derive(Debug)]
pub struct X86ELFDeep {
pub header: X86ElfHeader,
pub program_headers: Vec<X86ProgramHeader>,
pub section_headers: Vec<X86SectionHeader>,
pub symbols: Vec<X86Symbol>,
pub dynamic_symbols: Vec<X86Symbol>,
pub relocations: Vec<X86Relocation>,
pub dynamic_entries: Vec<X86DynamicEntry>,
pub section_data: BTreeMap<String, Vec<u8>>,
pub string_tables: HashMap<String, Vec<u8>>,
pub notes: Vec<X86Note>,
pub verdefs: Vec<X86Verdef>,
pub verneeds: Vec<X86Verneed>,
pub hash_table: Option<X86HashTable>,
pub gnu_hash_table: Option<X86GnuHashTable>,
pub comdat_groups: Vec<X86ComdatGroup>,
pub compressed_sections: BTreeMap<String, X86CompressedSection>,
pub arm_exidx_entries: Vec<ArmExidxEntry>,
pub shstrtab: Vec<u8>,
pub strtab: Vec<u8>,
pub dynstr: Vec<u8>,
pub file_size: u64,
pub valid: bool,
pub errors: Vec<String>,
pub warnings: Vec<String>,
}
impl X86ELFDeep {
pub fn new() -> Self {
X86ELFDeep {
header: X86ElfHeader::new_x86_64(),
program_headers: Vec::new(),
section_headers: Vec::new(),
symbols: Vec::new(),
dynamic_symbols: Vec::new(),
relocations: Vec::new(),
dynamic_entries: Vec::new(),
section_data: BTreeMap::new(),
string_tables: HashMap::new(),
notes: Vec::new(),
verdefs: Vec::new(),
verneeds: Vec::new(),
hash_table: None,
gnu_hash_table: None,
comdat_groups: Vec::new(),
compressed_sections: BTreeMap::new(),
arm_exidx_entries: Vec::new(),
shstrtab: Vec::new(),
strtab: Vec::new(),
dynstr: Vec::new(),
file_size: 0,
valid: true,
errors: Vec::new(),
warnings: Vec::new(),
}
}
pub fn new_i386() -> Self {
let mut elf = Self::new();
elf.header = X86ElfHeader::new_x86_32();
elf
}
pub fn validate(&mut self) -> bool {
self.errors.clear();
self.warnings.clear();
self.valid = true;
if !self.header.ident.is_valid_magic() {
self.errors.push("Invalid ELF magic".to_string());
self.valid = false;
}
if self.header.ident.version != 1 {
self.warnings.push(format!(
"Unexpected ELF version {} (expected 1)",
self.header.ident.version
));
}
if self.header.e_machine != EM_X86_64 && self.header.e_machine != EM_386 {
self.warnings.push(format!(
"Unexpected machine type 0x{:x} for X86 (expected 0x{:x} or 0x{:x})",
self.header.e_machine, EM_X86_64, EM_386
));
}
for (i, ph) in self.program_headers.iter().enumerate() {
if ph.p_type == PT_LOAD {
if ph.p_filesz > ph.p_memsz {
self.errors.push(format!(
"Program header {}: p_filesz ({}) > p_memsz ({})",
i, ph.p_filesz, ph.p_memsz
));
self.valid = false;
}
if ph.p_align > 0 && ph.p_vaddr % ph.p_align != ph.p_offset % ph.p_align {
self.warnings.push(format!(
"Program header {}: vaddr/offset alignment mismatch",
i
));
}
}
}
for (i, sh) in self.section_headers.iter().enumerate() {
if sh.sh_type == SHT_SYMTAB || sh.sh_type == SHT_DYNSYM {
if sh.sh_entsize == 0 {
self.warnings.push(format!(
"Section {} ({}) is a symbol table but sh_entsize is 0",
i, sh.name
));
}
}
if sh.sh_type == SHT_RELA || sh.sh_type == SHT_REL {
if sh.sh_entsize == 0 {
self.warnings.push(format!(
"Section {} ({}) is a relocation table but sh_entsize is 0",
i, sh.name
));
}
}
if sh.sh_flags & SHF_ALLOC != 0 && sh.sh_addr == 0 && sh.sh_type != SHT_NOBITS {
self.warnings.push(format!(
"Section {} ({}) has ALLOC flag but address is 0",
i, sh.name
));
}
}
if self.header.e_type == ET_EXEC || self.header.e_type == ET_DYN {
let has_interp = self
.program_headers
.iter()
.any(|ph| ph.p_type == PT_INTERP);
let has_load = self
.program_headers
.iter()
.any(|ph| ph.p_type == PT_LOAD);
if !has_load {
self.errors
.push("Executable/shared object has no PT_LOAD segment".to_string());
self.valid = false;
}
if self.header.e_type == ET_EXEC && !has_interp {
self.warnings
.push("Executable has no PT_INTERP (no dynamic linker)".to_string());
}
}
let seen_names: HashSet<&str> = self.symbols.iter().map(|s| s.name.as_str()).collect();
if seen_names.len() != self.symbols.len() {
self.warnings
.push("Duplicate symbol names found in symbol table".to_string());
}
let mut seen_tags = HashSet::new();
for entry in &self.dynamic_entries {
if !seen_tags.insert(entry.d_tag) && entry.d_tag != DT_NULL {
self.warnings.push(format!(
"Duplicate dynamic tag {} found",
entry.tag_name()
));
}
}
self.valid
}
pub fn parse(&mut self, data: &[u8]) -> Result<(), String> {
if data.len() < 64 {
return Err("ELF data too short (minimum 64 bytes)".to_string());
}
self.file_size = data.len() as u64;
let mut ident_bytes = [0u8; 16];
if data.len() < 16 {
return Err("ELF data too short for e_ident".to_string());
}
ident_bytes.copy_from_slice(&data[..16]);
self.header.ident = ElfIdent::from_bytes(&ident_bytes);
if !self.header.ident.is_valid_magic() {
return Err("Invalid ELF magic bytes".to_string());
}
let is_64bit = self.header.ident.is_64bit();
if is_64bit {
self.parse_header_64(data)?;
} else {
self.parse_header_32(data)?;
}
self.parse_program_headers(data, is_64bit)?;
self.parse_section_headers(data, is_64bit)?;
self.parse_string_tables(data)?;
self.parse_symbol_tables(data, is_64bit)?;
self.parse_dynamic_entries(data, is_64bit)?;
self.parse_relocations(data, is_64bit)?;
self.parse_notes(data)?;
self.validate();
Ok(())
}
fn parse_header_64(&mut self, data: &[u8]) -> Result<(), String> {
if data.len() < 64 {
return Err("ELF64 header requires at least 64 bytes".to_string());
}
self.header.e_type = u16::from_le_bytes([data[16], data[17]]);
self.header.e_machine = u16::from_le_bytes([data[18], data[19]]);
self.header.e_version = u32::from_le_bytes([data[20], data[21], data[22], data[23]]);
self.header.e_entry = u64::from_le_bytes([
data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31],
]);
self.header.e_phoff = u64::from_le_bytes([
data[32], data[33], data[34], data[35], data[36], data[37], data[38], data[39],
]);
self.header.e_shoff = u64::from_le_bytes([
data[40], data[41], data[42], data[43], data[44], data[45], data[46], data[47],
]);
self.header.e_flags = u32::from_le_bytes([data[48], data[49], data[50], data[51]]);
self.header.e_ehsize = u16::from_le_bytes([data[52], data[53]]);
self.header.e_phentsize = u16::from_le_bytes([data[54], data[55]]);
self.header.e_phnum = u16::from_le_bytes([data[56], data[57]]);
self.header.e_shentsize = u16::from_le_bytes([data[58], data[59]]);
self.header.e_shnum = u16::from_le_bytes([data[60], data[61]]);
self.header.e_shstrndx = u16::from_le_bytes([data[62], data[63]]);
Ok(())
}
fn parse_header_32(&mut self, data: &[u8]) -> Result<(), String> {
if data.len() < 52 {
return Err("ELF32 header requires at least 52 bytes".to_string());
}
self.header.e_type = u16::from_le_bytes([data[16], data[17]]);
self.header.e_machine = u16::from_le_bytes([data[18], data[19]]);
self.header.e_version = u32::from_le_bytes([data[20], data[21], data[22], data[23]]);
self.header.e_entry = u32::from_le_bytes([data[24], data[25], data[26], data[27]]) as u64;
self.header.e_phoff = u32::from_le_bytes([data[28], data[29], data[30], data[31]]) as u64;
self.header.e_shoff = u32::from_le_bytes([data[32], data[33], data[34], data[35]]) as u64;
self.header.e_flags = u32::from_le_bytes([data[36], data[37], data[38], data[39]]);
self.header.e_ehsize = u16::from_le_bytes([data[40], data[41]]);
self.header.e_phentsize = u16::from_le_bytes([data[42], data[43]]);
self.header.e_phnum = u16::from_le_bytes([data[44], data[45]]);
self.header.e_shentsize = u16::from_le_bytes([data[46], data[47]]);
self.header.e_shnum = u16::from_le_bytes([data[48], data[49]]);
self.header.e_shstrndx = u16::from_le_bytes([data[50], data[51]]);
Ok(())
}
fn parse_program_headers(&mut self, data: &[u8], is_64bit: bool) -> Result<(), String> {
let phoff = self.header.e_phoff as usize;
let phnum = self.header.e_phnum as usize;
let phentsize = self.header.e_phentsize as usize;
if phoff == 0 || phnum == 0 {
return Ok(());
}
if phentsize == 0 {
return Err("Program header entry size is zero".to_string());
}
for i in 0..phnum {
let offset = phoff + i * phentsize;
if offset + phentsize > data.len() {
self.warnings.push(format!(
"Program header {} exceeds file bounds",
i
));
break;
}
let ph = if is_64bit {
X86ProgramHeader {
p_type: u32::from_le_bytes([
data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
]),
p_flags: u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]),
p_offset: u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]),
p_vaddr: u64::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]),
p_paddr: u64::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]),
p_filesz: u64::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]),
p_memsz: u64::from_le_bytes([
data[offset + 40],
data[offset + 41],
data[offset + 42],
data[offset + 43],
data[offset + 44],
data[offset + 45],
data[offset + 46],
data[offset + 47],
]),
p_align: u64::from_le_bytes([
data[offset + 48],
data[offset + 49],
data[offset + 50],
data[offset + 51],
data[offset + 52],
data[offset + 53],
data[offset + 54],
data[offset + 55],
]),
}
} else {
X86ProgramHeader {
p_type: u32::from_le_bytes([
data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
]),
p_offset: u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]) as u64,
p_vaddr: u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]) as u64,
p_paddr: u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]) as u64,
p_filesz: u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]) as u64,
p_memsz: u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]) as u64,
p_flags: u32::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
]),
p_align: u32::from_le_bytes([
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]) as u64,
}
};
self.program_headers.push(ph);
}
Ok(())
}
fn parse_section_headers(&mut self, data: &[u8], is_64bit: bool) -> Result<(), String> {
let shoff = self.header.e_shoff as usize;
let shnum = self.header.e_shnum as usize;
let shentsize = self.header.e_shentsize as usize;
if shoff == 0 || shnum == 0 {
return Ok(());
}
if shentsize == 0 {
return Err("Section header entry size is zero".to_string());
}
for i in 0..shnum {
let offset = shoff + i * shentsize;
if offset + shentsize > data.len() {
self.warnings.push(format!("Section header {} exceeds file bounds", i));
break;
}
let sh = if is_64bit {
X86SectionHeader {
sh_name: u32::from_le_bytes([
data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
]),
sh_type: u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]),
sh_flags: u64::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]),
sh_addr: u64::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]),
sh_offset: u64::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]),
sh_size: u64::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]),
sh_link: u32::from_le_bytes([
data[offset + 40],
data[offset + 41],
data[offset + 42],
data[offset + 43],
]),
sh_info: u32::from_le_bytes([
data[offset + 44],
data[offset + 45],
data[offset + 46],
data[offset + 47],
]),
sh_addralign: u64::from_le_bytes([
data[offset + 48],
data[offset + 49],
data[offset + 50],
data[offset + 51],
data[offset + 52],
data[offset + 53],
data[offset + 54],
data[offset + 55],
]),
sh_entsize: u64::from_le_bytes([
data[offset + 56],
data[offset + 57],
data[offset + 58],
data[offset + 59],
data[offset + 60],
data[offset + 61],
data[offset + 62],
data[offset + 63],
]),
name: String::new(),
}
} else {
X86SectionHeader {
sh_name: u32::from_le_bytes([
data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
]),
sh_type: u32::from_le_bytes([
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]),
sh_flags: u32::from_le_bytes([
data[offset + 8],
data[offset + 9],
data[offset + 10],
data[offset + 11],
]) as u64,
sh_addr: u32::from_le_bytes([
data[offset + 12],
data[offset + 13],
data[offset + 14],
data[offset + 15],
]) as u64,
sh_offset: u32::from_le_bytes([
data[offset + 16],
data[offset + 17],
data[offset + 18],
data[offset + 19],
]) as u64,
sh_size: u32::from_le_bytes([
data[offset + 20],
data[offset + 21],
data[offset + 22],
data[offset + 23],
]) as u64,
sh_link: u32::from_le_bytes([
data[offset + 24],
data[offset + 25],
data[offset + 26],
data[offset + 27],
]),
sh_info: u32::from_le_bytes([
data[offset + 28],
data[offset + 29],
data[offset + 30],
data[offset + 31],
]),
sh_addralign: u32::from_le_bytes([
data[offset + 32],
data[offset + 33],
data[offset + 34],
data[offset + 35],
]) as u64,
sh_entsize: u32::from_le_bytes([
data[offset + 36],
data[offset + 37],
data[offset + 38],
data[offset + 39],
]) as u64,
name: String::new(),
}
};
self.section_headers.push(sh);
}
Ok(())
}
fn parse_string_tables(&mut self, data: &[u8]) -> Result<(), String> {
let shstrndx = self.header.e_shstrndx as usize;
if shstrndx > 0 && shstrndx < self.section_headers.len() {
let sh = &self.section_headers[shstrndx - 1]; let off = sh.sh_offset as usize;
let size = sh.sh_size as usize;
if off + size <= data.len() {
self.shstrtab = data[off..off + size].to_vec();
for section in self.section_headers.iter_mut() {
let name_offset = section.sh_name as usize;
if name_offset < self.shstrtab.len() {
let end = self.shstrtab[name_offset..]
.iter()
.position(|&b| b == 0)
.unwrap_or(self.shstrtab.len() - name_offset);
section.name = String::from_utf8_lossy(
&self.shstrtab[name_offset..name_offset + end],
)
.to_string();
}
}
}
}
for i in 0..self.section_headers.len() {
let sh = &self.section_headers[i];
let name_off = sh.sh_name as usize;
if !self.shstrtab.is_empty() && name_off < self.shstrtab.len() {
let end = self.shstrtab[name_off..]
.iter()
.position(|&b| b == 0)
.unwrap_or(self.shstrtab.len() - name_off);
let name = String::from_utf8_lossy(&self.shstrtab[name_off..name_off + end]).to_string();
self.section_headers[i].name = name;
}
}
if let Some(strtab_idx) = self.find_section_index(".strtab") {
let sh = &self.section_headers[strtab_idx];
if sh.sh_type == SHT_STRTAB {
let off = sh.sh_offset as usize;
let size = sh.sh_size as usize;
if off + size <= data.len() {
self.strtab = data[off..off + size].to_vec();
}
}
}
if let Some(dynstr_idx) = self.find_section_index(".dynstr") {
let sh = &self.section_headers[dynstr_idx];
if sh.sh_type == SHT_STRTAB {
let off = sh.sh_offset as usize;
let size = sh.sh_size as usize;
if off + size <= data.len() {
self.dynstr = data[off..off + size].to_vec();
}
}
}
Ok(())
}
fn find_section_index(&self, name: &str) -> Option<usize> {
self.section_headers.iter().position(|sh| sh.name == name)
}
fn parse_symbol_tables(&mut self, data: &[u8], is_64bit: bool) -> Result<(), String> {
if let Some(symtab_idx) = self.find_section_index(".symtab") {
let sh = &self.section_headers[symtab_idx];
self.symbols = self.parse_sym_entries(data, sh, is_64bit, &self.strtab);
}
if let Some(dynsym_idx) = self.find_section_index(".dynsym") {
let sh = &self.section_headers[dynsym_idx];
self.dynamic_symbols = self.parse_sym_entries(data, sh, is_64bit, &self.dynstr);
}
Ok(())
}
fn parse_sym_entries(
&self,
data: &[u8],
sh: &X86SectionHeader,
is_64bit: bool,
strtab: &[u8],
) -> Vec<X86Symbol> {
let mut symbols = Vec::new();
let off = sh.sh_offset as usize;
let size = sh.sh_size as usize;
let entsize = if sh.sh_entsize > 0 {
sh.sh_entsize as usize
} else if is_64bit {
24
} else {
16
};
let count = if entsize > 0 { size / entsize } else { 0 };
for i in 0..count {
let entry_off = off + i * entsize;
if entry_off + entsize > data.len() {
break;
}
let (sym, name) = if is_64bit {
let st_name = u32::from_le_bytes([
data[entry_off],
data[entry_off + 1],
data[entry_off + 2],
data[entry_off + 3],
]);
let st_info = data[entry_off + 4];
let st_other = data[entry_off + 5];
let st_shndx =
u16::from_le_bytes([data[entry_off + 6], data[entry_off + 7]]);
let st_value = u64::from_le_bytes([
data[entry_off + 8],
data[entry_off + 9],
data[entry_off + 10],
data[entry_off + 11],
data[entry_off + 12],
data[entry_off + 13],
data[entry_off + 14],
data[entry_off + 15],
]);
let st_size = u64::from_le_bytes([
data[entry_off + 16],
data[entry_off + 17],
data[entry_off + 18],
data[entry_off + 19],
data[entry_off + 20],
data[entry_off + 21],
data[entry_off + 22],
data[entry_off + 23],
]);
let name = read_strtab(strtab, st_name as usize);
(X86Symbol {
st_name,
st_info,
st_other,
st_shndx,
st_value,
st_size,
name: name.clone(),
}, name)
} else {
let st_name = u32::from_le_bytes([
data[entry_off],
data[entry_off + 1],
data[entry_off + 2],
data[entry_off + 3],
]);
let st_value = u32::from_le_bytes([
data[entry_off + 4],
data[entry_off + 5],
data[entry_off + 6],
data[entry_off + 7],
]) as u64;
let st_size = u32::from_le_bytes([
data[entry_off + 8],
data[entry_off + 9],
data[entry_off + 10],
data[entry_off + 11],
]) as u64;
let st_info = data[entry_off + 12];
let st_other = data[entry_off + 13];
let st_shndx =
u16::from_le_bytes([data[entry_off + 14], data[entry_off + 15]]);
let name = read_strtab(strtab, st_name as usize);
(X86Symbol {
st_name,
st_info,
st_other,
st_shndx,
st_value,
st_size,
name: name.clone(),
}, name)
};
symbols.push(sym);
}
symbols
}
fn parse_dynamic_entries(&mut self, data: &[u8], is_64bit: bool) -> Result<(), String> {
if let Some(dyn_idx) = self.find_section_index(".dynamic") {
let sh = &self.section_headers[dyn_idx];
let off = sh.sh_offset as usize;
let size = sh.sh_size as usize;
let entsize = if sh.sh_entsize > 0 {
sh.sh_entsize as usize
} else if is_64bit {
16
} else {
8
};
let count = if entsize > 0 { size / entsize } else { 0 };
for i in 0..count {
let entry_off = off + i * entsize;
if entry_off + entsize > data.len() {
break;
}
let entry = if is_64bit {
X86DynamicEntry {
d_tag: u64::from_le_bytes([
data[entry_off],
data[entry_off + 1],
data[entry_off + 2],
data[entry_off + 3],
data[entry_off + 4],
data[entry_off + 5],
data[entry_off + 6],
data[entry_off + 7],
]),
d_val: u64::from_le_bytes([
data[entry_off + 8],
data[entry_off + 9],
data[entry_off + 10],
data[entry_off + 11],
data[entry_off + 12],
data[entry_off + 13],
data[entry_off + 14],
data[entry_off + 15],
]),
}
} else {
X86DynamicEntry {
d_tag: u32::from_le_bytes([
data[entry_off],
data[entry_off + 1],
data[entry_off + 2],
data[entry_off + 3],
]) as u64,
d_val: u32::from_le_bytes([
data[entry_off + 4],
data[entry_off + 5],
data[entry_off + 6],
data[entry_off + 7],
]) as u64,
}
};
if entry.d_tag == DT_NULL {
break;
}
self.dynamic_entries.push(entry);
}
}
Ok(())
}
fn parse_relocations(&mut self, data: &[u8], is_64bit: bool) -> Result<(), String> {
for idx in 0..self.section_headers.len() {
let sh = &self.section_headers[idx];
if sh.sh_type != SHT_RELA && sh.sh_type != SHT_REL {
continue;
}
let has_addend = sh.sh_type == SHT_RELA;
let off = sh.sh_offset as usize;
let size = sh.sh_size as usize;
let entsize = sh.sh_entsize as usize;
let count = if entsize > 0 { size / entsize } else { 0 };
for i in 0..count {
let entry_off = off + i * entsize;
if entry_off + entsize > data.len() {
break;
}
let reloc = if is_64bit && has_addend {
X86Relocation::new(
u64::from_le_bytes([
data[entry_off],
data[entry_off + 1],
data[entry_off + 2],
data[entry_off + 3],
data[entry_off + 4],
data[entry_off + 5],
data[entry_off + 6],
data[entry_off + 7],
]),
u32::from_le_bytes([
data[entry_off + 8],
data[entry_off + 9],
data[entry_off + 10],
data[entry_off + 11],
]),
u32::from_le_bytes([
data[entry_off + 12],
data[entry_off + 13],
data[entry_off + 14],
data[entry_off + 15],
]),
i64::from_le_bytes([
data[entry_off + 16],
data[entry_off + 17],
data[entry_off + 18],
data[entry_off + 19],
data[entry_off + 20],
data[entry_off + 21],
data[entry_off + 22],
data[entry_off + 23],
]),
)
} else if is_64bit && !has_addend {
X86Relocation::new(
u64::from_le_bytes([
data[entry_off],
data[entry_off + 1],
data[entry_off + 2],
data[entry_off + 3],
data[entry_off + 4],
data[entry_off + 5],
data[entry_off + 6],
data[entry_off + 7],
]),
u32::from_le_bytes([
data[entry_off + 8],
data[entry_off + 9],
data[entry_off + 10],
data[entry_off + 11],
]),
u32::from_le_bytes([
data[entry_off + 12],
data[entry_off + 13],
data[entry_off + 14],
data[entry_off + 15],
]),
0,
)
} else if has_addend {
X86Relocation::new(
u32::from_le_bytes([
data[entry_off],
data[entry_off + 1],
data[entry_off + 2],
data[entry_off + 3],
]) as u64,
u32::from_le_bytes([
data[entry_off + 4],
data[entry_off + 5],
data[entry_off + 6],
data[entry_off + 7],
]),
u32::from_le_bytes([
data[entry_off + 4],
data[entry_off + 5],
data[entry_off + 6],
data[entry_off + 7],
]) >> 8,
i32::from_le_bytes([
data[entry_off + 8],
data[entry_off + 9],
data[entry_off + 10],
data[entry_off + 11],
]) as i64,
)
} else {
X86Relocation::new(
u32::from_le_bytes([
data[entry_off],
data[entry_off + 1],
data[entry_off + 2],
data[entry_off + 3],
]) as u64,
u32::from_le_bytes([
data[entry_off + 4],
data[entry_off + 5],
data[entry_off + 6],
data[entry_off + 7],
]),
u32::from_le_bytes([
data[entry_off + 4],
data[entry_off + 5],
data[entry_off + 6],
data[entry_off + 7],
]) >> 8,
0,
)
};
let r_sym = reloc.r_sym;
self.relocations.push(reloc);
}
}
Ok(())
}
fn parse_notes(&mut self, data: &[u8]) -> Result<(), String> {
for idx in 0..self.section_headers.len() {
let sh = &self.section_headers[idx];
if sh.sh_type != SHT_NOTE {
continue;
}
let off = sh.sh_offset as usize;
let end = off + sh.sh_size as usize;
let mut pos = off;
while pos + 12 <= end.min(data.len()) {
let n_namesz = u32::from_le_bytes([
data[pos], data[pos + 1], data[pos + 2], data[pos + 3],
]) as usize;
let n_descsz = u32::from_le_bytes([
data[pos + 4], data[pos + 5], data[pos + 6], data[pos + 7],
]) as usize;
let n_type = u32::from_le_bytes([
data[pos + 8], data[pos + 9], data[pos + 10], data[pos + 11],
]);
let name_start = pos + 12;
let name_end = name_start + n_namesz;
let desc_start = (name_end + 3) & !3; let desc_end = desc_start + n_descsz;
if name_end > data.len() || desc_end > data.len() {
break;
}
let name = String::from_utf8_lossy(&data[name_start..name_end])
.trim_end_matches('\0')
.to_string();
let desc = data[desc_start..desc_end].to_vec();
self.notes.push(X86Note::new(n_type, &name, desc));
pos = (desc_end + 3) & !3;
}
}
Ok(())
}
pub fn to_bytes(&self) -> Result<Vec<u8>, String> {
let is_64bit = self.header.ident.is_64bit();
let mut buf = Vec::new();
buf.extend_from_slice(&self.header.ident.to_bytes());
self.write_ehdr_fields(&mut buf, is_64bit)?;
Ok(buf)
}
fn write_ehdr_fields(&self, buf: &mut Vec<u8>, is_64bit: bool) -> Result<(), String> {
buf.extend_from_slice(&self.header.e_type.to_le_bytes());
buf.extend_from_slice(&self.header.e_machine.to_le_bytes());
buf.extend_from_slice(&self.header.e_version.to_le_bytes());
if is_64bit {
buf.extend_from_slice(&self.header.e_entry.to_le_bytes());
buf.extend_from_slice(&self.header.e_phoff.to_le_bytes());
buf.extend_from_slice(&self.header.e_shoff.to_le_bytes());
} else {
buf.extend_from_slice(&(self.header.e_entry as u32).to_le_bytes());
buf.extend_from_slice(&(self.header.e_phoff as u32).to_le_bytes());
buf.extend_from_slice(&(self.header.e_shoff as u32).to_le_bytes());
}
buf.extend_from_slice(&self.header.e_flags.to_le_bytes());
buf.extend_from_slice(&self.header.e_ehsize.to_le_bytes());
buf.extend_from_slice(&self.header.e_phentsize.to_le_bytes());
buf.extend_from_slice(&self.header.e_phnum.to_le_bytes());
buf.extend_from_slice(&self.header.e_shentsize.to_le_bytes());
buf.extend_from_slice(&self.header.e_shnum.to_le_bytes());
buf.extend_from_slice(&self.header.e_shstrndx.to_le_bytes());
Ok(())
}
pub fn get_string_from_tab(&self, tab: &[u8], offset: usize) -> String {
read_strtab(tab, offset)
}
pub fn find_symbol(&self, name: &str) -> Option<&X86Symbol> {
self.symbols.iter().find(|s| s.name == name)
}
pub fn find_dynamic_symbol(&self, name: &str) -> Option<&X86Symbol> {
self.dynamic_symbols.iter().find(|s| s.name == name)
}
pub fn symbols_by_type(&self, st_type: u8) -> Vec<&X86Symbol> {
self.symbols
.iter()
.filter(|s| s.st_type() == st_type)
.collect()
}
pub fn symbols_by_binding(&self, bind: u8) -> Vec<&X86Symbol> {
self.symbols
.iter()
.filter(|s| s.bind() == bind)
.collect()
}
pub fn exported_symbols(&self) -> Vec<&X86Symbol> {
self.symbols
.iter()
.filter(|s| s.bind() == STB_GLOBAL || s.bind() == STB_WEAK)
.filter(|s| s.is_defined())
.collect()
}
pub fn get_section_data(&self, name: &str) -> Option<&[u8]> {
self.section_data.get(name).map(|v| v.as_slice())
}
pub fn relocation_types_used(&self) -> HashSet<u32> {
self.relocations.iter().map(|r| r.r_type).collect()
}
pub fn relocation_type_counts(&self) -> HashMap<u32, usize> {
let mut counts = HashMap::new();
for r in &self.relocations {
*counts.entry(r.r_type).or_insert(0) += 1;
}
counts
}
pub fn get_dynamic(&self, tag: u64) -> Option<u64> {
self.dynamic_entries
.iter()
.find(|e| e.d_tag == tag)
.map(|e| e.d_val)
}
pub fn get_dynamic_all(&self, tag: u64) -> Vec<u64> {
self.dynamic_entries
.iter()
.filter(|e| e.d_tag == tag)
.map(|e| e.d_val)
.collect()
}
pub fn has_dynamic_flag(&self, flag: u64) -> bool {
if let Some(flags) = self.get_dynamic(DT_FLAGS) {
return flags & flag != 0;
}
false
}
pub fn has_dynamic_flag_1(&self, flag: u64) -> bool {
if let Some(flags) = self.get_dynamic(DT_FLAGS_1) {
return flags & flag != 0;
}
false
}
pub fn total_load_size(&self) -> u64 {
self.program_headers
.iter()
.filter(|ph| ph.p_type == PT_LOAD)
.map(|ph| ph.p_memsz)
.sum()
}
pub fn build_gnu_hash(&mut self, nbuckets: u32, symoffset: u32, bloom_shift: u32) {
let dynsym_count = self.dynamic_symbols.len() as u32;
if dynsym_count <= symoffset {
return;
}
let ndynsyms = dynsym_count - symoffset;
let bloom_size = if nbuckets > 0 {
(ndynsyms / nbuckets.max(1)).max(1).next_power_of_two()
} else {
1
};
let mut table = X86GnuHashTable::new(nbuckets, symoffset, bloom_size, bloom_shift);
table.chain = vec![0u32; ndynsyms as usize];
for i in 0..ndynsyms as usize {
let sym_idx = symoffset as usize + i;
if sym_idx >= self.dynamic_symbols.len() {
break;
}
let h = elf_gnu_hash(&self.dynamic_symbols[sym_idx].name);
let bm = h;
let bit0 = (bm / 64) % bloom_size as u32;
let bit1 = ((bm >> bloom_shift) / 64) % bloom_size as u32;
table.bloom[bit0 as usize] |= 1u64 << (bm % 64);
table.bloom[bit1 as usize] |= 1u64 << ((bm >> bloom_shift) % 64);
table.chain[i] = h & !1;
if i == ndynsyms as usize - 1
|| self.sym_gnu_hash(symoffset as usize + i + 1) % nbuckets
!= h % nbuckets
{
table.chain[i] |= 1;
}
}
for i in 0..ndynsyms as usize {
let h = elf_gnu_hash(&self.dynamic_symbols[symoffset as usize + i].name);
let bucket = (h % nbuckets) as usize;
if table.buckets[bucket] == 0 {
table.buckets[bucket] = i as u32;
}
}
self.gnu_hash_table = Some(table);
}
fn sym_gnu_hash(&self, idx: usize) -> u32 {
if idx < self.dynamic_symbols.len() {
elf_gnu_hash(&self.dynamic_symbols[idx].name)
} else {
0
}
}
pub fn report(&self) -> String {
let mut r = String::new();
r.push_str("=== X86 ELF Deep Report ===\n\n");
r.push_str(&format!(
"Class: {}\n",
if self.header.ident.is_64bit() {
"ELF64"
} else {
"ELF32"
}
));
r.push_str(&format!(
"Machine: {}\n",
match self.header.e_machine {
EM_X86_64 => "x86_64",
EM_386 => "i386",
_ => "Unknown",
}
));
r.push_str(&format!(
"Type: {}\n",
match self.header.e_type {
ET_REL => "REL (relocatable)",
ET_EXEC => "EXEC (executable)",
ET_DYN => "DYN (shared object)",
ET_CORE => "CORE (core dump)",
_ => "Unknown",
}
));
r.push_str(&format!("Entry point: 0x{:x}\n", self.header.e_entry));
r.push_str(&format!("Program headers: {}\n", self.program_headers.len()));
r.push_str(&format!("Section headers: {}\n", self.section_headers.len()));
r.push_str(&format!("Symbols: {}\n", self.symbols.len()));
r.push_str(&format!("Dynamic symbols: {}\n", self.dynamic_symbols.len()));
r.push_str(&format!("Relocations: {}\n", self.relocations.len()));
r.push_str(&format!("Dynamic entries: {}\n", self.dynamic_entries.len()));
r.push_str(&format!("Notes: {}\n", self.notes.len()));
r.push_str(&format!("Valid: {}\n", self.valid));
if !self.errors.is_empty() {
r.push_str("\nErrors:\n");
for e in &self.errors {
r.push_str(&format!(" - {}\n", e));
}
}
if !self.warnings.is_empty() {
r.push_str("\nWarnings:\n");
for w in &self.warnings {
r.push_str(&format!(" - {}\n", w));
}
}
r
}
}
impl Default for X86ELFDeep {
fn default() -> Self {
Self::new()
}
}
pub fn read_strtab(tab: &[u8], offset: usize) -> String {
if offset >= tab.len() {
return String::new();
}
let end = tab[offset..]
.iter()
.position(|&b| b == 0)
.unwrap_or(tab.len() - offset);
String::from_utf8_lossy(&tab[offset..offset + end]).to_string()
}
pub fn elf_gnu_hash(name: &str) -> u32 {
let mut h: u32 = 5381;
for b in name.as_bytes() {
h = h.wrapping_mul(33).wrapping_add(*b as u32);
}
h
}
pub fn elf_hash(name: &str) -> u32 {
let mut h: u32 = 0;
for b in name.as_bytes() {
h = (h << 4).wrapping_add(*b as u32);
let g = h & 0xf0000000;
if g != 0 {
h ^= g >> 24;
}
h &= !g;
}
h
}
pub fn x86_st_info(bind: u8, st_type: u8) -> u8 {
((bind & 0x0f) << 4) | (st_type & 0x0f)
}
pub fn x86_st_other(visibility: u8) -> u8 {
visibility & 0x03
}
pub fn x86_elf64_r_info(r_info: u64) -> (u32, u32) {
((r_info >> 32) as u32, (r_info & 0xffffffff) as u32)
}
pub fn x86_elf64_r_info_encode(sym: u32, r_type: u32) -> u64 {
((sym as u64) << 32) | (r_type as u64)
}
pub fn x86_elf32_r_info(r_info: u32) -> (u32, u32) {
((r_info >> 8) as u32, (r_info & 0xff) as u32)
}
pub fn x86_elf32_r_info_encode(sym: u32, r_type: u32) -> u32 {
((sym as u32) << 8) | (r_type as u32 & 0xff)
}
pub fn x86_reloc_needs_got_x86_64(r_type: u32) -> bool {
matches!(
r_type,
R_X86_64_GOT32
| R_X86_64_GOTPCREL
| R_X86_64_GOTTPOFF
| R_X86_64_GOTPCRELX
| R_X86_64_REX_GOTPCRELX
| R_X86_64_GOTPC32_TLSDESC
)
}
pub fn x86_reloc_is_tls(r_type: u32, machine: u16) -> bool {
match machine {
EM_X86_64 => matches!(
r_type,
R_X86_64_DTPMOD64
| R_X86_64_DTPOFF64
| R_X86_64_TPOFF64
| R_X86_64_TLSGD
| R_X86_64_TLSLD
| R_X86_64_DTPOFF32
| R_X86_64_GOTTPOFF
| R_X86_64_TPOFF32
| R_X86_64_TLSDESC
| R_X86_64_TLSDESC_CALL
| R_X86_64_GOTPC32_TLSDESC
),
EM_386 => matches!(
r_type,
R_386_TLS_TPOFF
| R_386_TLS_IE
| R_386_TLS_GOTIE
| R_386_TLS_LE
| R_386_TLS_GD
| R_386_TLS_LDM
| R_386_TLS_GD_32
| R_386_TLS_GD_PUSH
| R_386_TLS_GD_CALL
| R_386_TLS_GD_POP
| R_386_TLS_LDM_32
| R_386_TLS_LDM_PUSH
| R_386_TLS_LDM_CALL
| R_386_TLS_LDM_POP
| R_386_TLS_LDO_32
| R_386_TLS_IE_32
| R_386_TLS_LE_32
| R_386_TLS_DTPMOD32
| R_386_TLS_DTPOFF32
| R_386_TLS_TPOFF32
| R_386_TLS_GOTDESC
| R_386_TLS_DESC_CALL
| R_386_TLS_DESC
),
_ => false,
}
}
pub fn x86_reloc_is_pc_relative(r_type: u32, machine: u16) -> bool {
match machine {
EM_X86_64 => matches!(
r_type,
R_X86_64_PC32
| R_X86_64_PC16
| R_X86_64_PC8
| R_X86_64_PC64
| R_X86_64_PLT32
| R_X86_64_GOTPCREL
| R_X86_64_GOTPCRELX
| R_X86_64_REX_GOTPCRELX
| R_X86_64_PC32_BND
| R_X86_64_PLT32_BND
),
EM_386 => matches!(
r_type,
R_386_PC32 | R_386_PC16 | R_386_PC8 | R_386_PLT32 | R_386_GOTPC
),
_ => false,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ElfClass {
Elf32,
Elf64,
}
impl X86ElfClass {
pub fn from_ident(class: u8) -> Self {
match class {
ELFCLASS32 => X86ElfClass::Elf32,
ELFCLASS64 => X86ElfClass::Elf64,
_ => X86ElfClass::Elf64, }
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_minimal_elf64_data() -> Vec<u8> {
let mut data = vec![0u8; 120];
data[0..4].copy_from_slice(&ELF_MAGIC_BYTES);
data[EI_CLASS] = ELFCLASS64;
data[EI_DATA] = ELFDATA2LSB;
data[EI_VERSION] = 1;
data[EI_OSABI] = ELFOSABI_SYSV;
data[16..18].copy_from_slice(&1u16.to_le_bytes());
data[18..20].copy_from_slice(&62u16.to_le_bytes());
data[20..24].copy_from_slice(&1u32.to_le_bytes());
data[52..54].copy_from_slice(&64u16.to_le_bytes());
data[54..56].copy_from_slice(&56u16.to_le_bytes());
data[58..60].copy_from_slice(&64u16.to_le_bytes());
data
}
fn make_minimal_elf32_data() -> Vec<u8> {
let mut data = vec![0u8; 52];
data[0..4].copy_from_slice(&ELF_MAGIC_BYTES);
data[EI_CLASS] = ELFCLASS32;
data[EI_DATA] = ELFDATA2LSB;
data[EI_VERSION] = 1;
data[16..18].copy_from_slice(&1u16.to_le_bytes()); data[18..20].copy_from_slice(&3u16.to_le_bytes()); data[20..24].copy_from_slice(&1u32.to_le_bytes());
data[40..42].copy_from_slice(&52u16.to_le_bytes());
data[42..44].copy_from_slice(&32u16.to_le_bytes());
data[46..48].copy_from_slice(&40u16.to_le_bytes());
data
}
#[test]
fn test_ident_default() {
let ident = ElfIdent::default();
assert_eq!(ident.magic, ELF_MAGIC_BYTES);
assert_eq!(ident.class, ELFCLASS64);
assert_eq!(ident.data, ELFDATA2LSB);
}
#[test]
fn test_ident_x86_64() {
let ident = ElfIdent::new_x86_64();
assert!(ident.is_64bit());
assert!(!ident.is_32bit());
assert!(ident.is_valid_magic());
assert_eq!(ident.osabi, ELFOSABI_SYSV);
}
#[test]
fn test_ident_x86_32() {
let ident = ElfIdent::new_x86_32();
assert!(ident.is_32bit());
assert!(!ident.is_64bit());
assert!(ident.is_valid_magic());
}
#[test]
fn test_ident_roundtrip() {
let ident = ElfIdent::new_x86_64();
let bytes = ident.to_bytes();
let parsed = ElfIdent::from_bytes(&bytes);
assert_eq!(ident, parsed);
}
#[test]
fn test_ident_roundtrip_32() {
let ident = ElfIdent::new_x86_32();
let bytes = ident.to_bytes();
let parsed = ElfIdent::from_bytes(&bytes);
assert_eq!(ident, parsed);
}
#[test]
fn test_ident_invalid_magic() {
let mut ident = ElfIdent::new_x86_64();
ident.magic = [0, 0, 0, 0];
assert!(!ident.is_valid_magic());
}
#[test]
fn test_elf_header_default() {
let hdr = X86ElfHeader::default();
assert_eq!(hdr.e_machine, EM_X86_64);
assert_eq!(hdr.e_type, ET_REL);
assert!(hdr.is_64bit());
assert!(hdr.is_relocatable());
}
#[test]
fn test_elf_header_x86_64() {
let hdr = X86ElfHeader::new_x86_64();
assert!(hdr.is_64bit());
assert_eq!(hdr.e_machine, EM_X86_64);
}
#[test]
fn test_elf_header_x86_32() {
let hdr = X86ElfHeader::new_x86_32();
assert!(!hdr.is_64bit());
assert_eq!(hdr.e_machine, EM_386);
}
#[test]
fn test_elf_header_type_checks() {
let mut hdr = X86ElfHeader::new_x86_64();
hdr.e_type = ET_EXEC;
assert!(hdr.is_executable());
assert!(!hdr.is_shared_object());
hdr.e_type = ET_DYN;
assert!(hdr.is_shared_object());
assert!(!hdr.is_executable());
}
#[test]
fn test_phdr_load() {
let ph = X86ProgramHeader::new_load(0x400000, 0x1000, 0x5000, 0x6000, PF_R | PF_X);
assert_eq!(ph.p_type, PT_LOAD);
assert_eq!(ph.p_vaddr, 0x400000);
assert_eq!(ph.p_flags, PF_R | PF_X);
assert_eq!(ph.type_name(), "PT_LOAD");
assert!(ph.flags_string().contains('R'));
assert!(ph.flags_string().contains('X'));
}
#[test]
fn test_phdr_dynamic() {
let ph = X86ProgramHeader::new_dynamic(0x2000, 0x100);
assert_eq!(ph.p_type, PT_DYNAMIC);
assert_eq!(ph.p_flags, PF_R | PF_W);
}
#[test]
fn test_phdr_interp() {
let ph = X86ProgramHeader::new_interp(0x200, 0x20);
assert_eq!(ph.p_type, PT_INTERP);
assert_eq!(ph.p_align, 1);
}
#[test]
fn test_phdr_note() {
let ph = X86ProgramHeader::new_note(0x300, 0x80);
assert_eq!(ph.p_type, PT_NOTE);
}
#[test]
fn test_phdr_tls() {
let ph = X86ProgramHeader::new_tls(0x600000, 0x4000, 0x100, 0x200);
assert_eq!(ph.p_type, PT_TLS);
assert_eq!(ph.p_memsz, 0x200);
}
#[test]
fn test_phdr_gnu_stack() {
let ph = X86ProgramHeader::new_gnu_stack(PF_R | PF_W, 0x100000);
assert_eq!(ph.p_type, PT_GNU_STACK);
assert_eq!(ph.p_flags, PF_R | PF_W);
}
#[test]
fn test_phdr_gnu_relro() {
let ph = X86ProgramHeader::new_gnu_relro(0x5000, 0x2000);
assert_eq!(ph.p_type, PT_GNU_RELRO);
}
#[test]
fn test_phdr_gnu_eh_frame() {
let ph = X86ProgramHeader::new_gnu_eh_frame(0x6000, 0x1000);
assert_eq!(ph.p_type, PT_GNU_EH_FRAME);
}
#[test]
fn test_phdr_gnu_property() {
let ph = X86ProgramHeader::new_gnu_property(0x7000, 0x100);
assert_eq!(ph.p_type, PT_GNU_PROPERTY);
}
#[test]
fn test_phdr_flags_string() {
let ph = X86ProgramHeader::new_load(0, 0, 0, 0, PF_R | PF_W | PF_X);
assert_eq!(ph.flags_string(), "RWX");
let ph2 = X86ProgramHeader::new_load(0, 0, 0, 0, 0);
assert_eq!(ph2.flags_string(), "---");
}
#[test]
fn test_section_header_new() {
let sh = X86SectionHeader::new(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, 100);
assert_eq!(sh.name, ".text");
assert_eq!(sh.sh_type, SHT_PROGBITS);
assert_eq!(sh.type_name(), "PROGBITS");
assert_eq!(sh.sh_flags, SHF_ALLOC | SHF_EXECINSTR);
}
#[test]
fn test_section_header_flags_string() {
let sh = X86SectionHeader::new(".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE, 0, 100);
assert!(sh.flags_string().contains('W'));
assert!(sh.flags_string().contains('A'));
let sh2 = X86SectionHeader::new(".tbss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0, 100);
assert!(sh2.flags_string().contains('T'));
}
#[test]
fn test_section_type_names() {
let sh = X86SectionHeader::new("", SHT_SYMTAB, 0, 0, 0);
assert_eq!(sh.type_name(), "SYMTAB");
let sh2 = X86SectionHeader::new("", SHT_DYNSYM, 0, 0, 0);
assert_eq!(sh2.type_name(), "DYNSYM");
let sh3 = X86SectionHeader::new("", SHT_GNU_HASH, 0, 0, 0);
assert_eq!(sh3.type_name(), "GNU_HASH");
let sh4 = X86SectionHeader::new("", SHT_GNU_VERDEF, 0, 0, 0);
assert_eq!(sh4.type_name(), "GNU_VERDEF");
let sh5 = X86SectionHeader::new("", 0x9999, 0, 0, 0);
assert_eq!(sh5.type_name(), "UNKNOWN");
}
#[test]
fn test_symbol_new() {
let sym = X86Symbol::new("main", x86_st_info(STB_GLOBAL, STT_FUNC), 1, 0x4000, 0x50);
assert_eq!(sym.name, "main");
assert_eq!(sym.bind(), STB_GLOBAL);
assert_eq!(sym.st_type(), STT_FUNC);
assert_eq!(sym.visibility(), STV_DEFAULT);
assert!(sym.is_defined());
}
#[test]
fn test_symbol_bind_names() {
let sym = X86Symbol::new("foo", x86_st_info(STB_LOCAL, STT_OBJECT), 0, 0, 0);
assert_eq!(sym.bind_name(), "LOCAL");
let sym2 = X86Symbol::new("bar", x86_st_info(STB_WEAK, STT_FUNC), 0, 0, 0);
assert_eq!(sym2.bind_name(), "WEAK");
let sym3 = X86Symbol::new("baz", x86_st_info(STB_GNU_UNIQUE, STT_OBJECT), 0, 0, 0);
assert_eq!(sym3.bind_name(), "GNU_UNIQUE");
}
#[test]
fn test_symbol_type_names() {
let sym = X86Symbol::new("a", x86_st_info(STB_GLOBAL, STT_NOTYPE), 0, 0, 0);
assert_eq!(sym.type_name(), "NOTYPE");
let sym2 = X86Symbol::new("b", x86_st_info(STB_GLOBAL, STT_FILE), 0, 0, 0);
assert_eq!(sym2.type_name(), "FILE");
let sym3 = X86Symbol::new("c", x86_st_info(STB_GLOBAL, STT_TLS), 0, 0, 0);
assert_eq!(sym3.type_name(), "TLS");
let sym4 = X86Symbol::new("d", x86_st_info(STB_GLOBAL, STT_GNU_IFUNC), 0, 0, 0);
assert_eq!(sym4.type_name(), "GNU_IFUNC");
}
#[test]
fn test_symbol_visibility_names() {
let sym = X86Symbol::new("a", x86_st_info(STB_GLOBAL, STT_FUNC), 0, 0, 0);
sym.st_other = 0; let sym2 = X86Symbol::new("b", x86_st_info(STB_GLOBAL, STT_FUNC), 0, 0, 0);
sym2.st_other = 0;
assert_eq!(sym2.visibility_name(), "DEFAULT");
let sym3 = X86Symbol::new("c", x86_st_info(STB_GLOBAL, STT_FUNC), 0, 0, 0);
sym3.st_other = 0;
assert_eq!(sym3.visibility_name(), "DEFAULT");
}
#[test]
fn test_symbol_undefined() {
let sym = X86Symbol::new("undef", x86_st_info(STB_GLOBAL, STT_NOTYPE), SHN_UNDEF, 0, 0);
assert!(!sym.is_defined());
}
#[test]
fn test_symbol_visibility_encoding() {
let sym = X86Symbol::new(
"hidden_sym",
x86_st_info(STB_GLOBAL, STT_FUNC),
1,
0x1000,
0x10,
);
let sym = X86Symbol {
st_other: x86_st_other(STV_HIDDEN),
..sym
};
assert_eq!(sym.visibility(), STV_HIDDEN);
assert_eq!(sym.visibility_name(), "HIDDEN");
}
#[test]
fn test_relocation_new() {
let reloc = X86Relocation::new(0x100, R_X86_64_PC32, 5, -4);
assert_eq!(reloc.r_offset, 0x100);
assert_eq!(reloc.r_type, R_X86_64_PC32);
assert_eq!(reloc.r_sym, 5);
assert_eq!(reloc.r_addend, -4);
}
#[test]
fn test_x86_64_reloc_names() {
assert_eq!(x86_64_reloc_name(R_X86_64_NONE), "R_X86_64_NONE");
assert_eq!(x86_64_reloc_name(R_X86_64_64), "R_X86_64_64");
assert_eq!(x86_64_reloc_name(R_X86_64_GOTPCREL), "R_X86_64_GOTPCREL");
assert_eq!(x86_64_reloc_name(R_X86_64_IRELATIVE), "R_X86_64_IRELATIVE");
assert_eq!(x86_64_reloc_name(R_X86_64_GOTPCRELX), "R_X86_64_GOTPCRELX");
assert_eq!(x86_64_reloc_name(99999), "R_X86_64_UNKNOWN");
}
#[test]
fn test_i386_reloc_names() {
assert_eq!(i386_reloc_name(R_386_NONE), "R_386_NONE");
assert_eq!(i386_reloc_name(R_386_32), "R_386_32");
assert_eq!(i386_reloc_name(R_386_PC32), "R_386_PC32");
assert_eq!(i386_reloc_name(R_386_TLS_GD), "R_386_TLS_GD");
assert_eq!(i386_reloc_name(R_386_IRELATIVE), "R_386_IRELATIVE");
assert_eq!(i386_reloc_name(99999), "R_386_UNKNOWN");
}
#[test]
fn test_relocation_type_name() {
let r = X86Relocation::new(0, R_X86_64_PC32, 0, 0);
assert_eq!(r.type_name(EM_X86_64), "R_X86_64_PC32");
let r2 = X86Relocation::new(0, R_386_PC32, 0, 0);
assert_eq!(r2.type_name(EM_386), "R_386_PC32");
}
#[test]
fn test_reloc_needs_got() {
assert!(x86_reloc_needs_got_x86_64(R_X86_64_GOTPCREL));
assert!(x86_reloc_needs_got_x86_64(R_X86_64_GOT32));
assert!(x86_reloc_needs_got_x86_64(R_X86_64_GOTPCRELX));
assert!(!x86_reloc_needs_got_x86_64(R_X86_64_PC32));
assert!(!x86_reloc_needs_got_x86_64(R_X86_64_64));
}
#[test]
fn test_reloc_is_tls() {
assert!(x86_reloc_is_tls(R_X86_64_TLSGD, EM_X86_64));
assert!(x86_reloc_is_tls(R_X86_64_TPOFF64, EM_X86_64));
assert!(!x86_reloc_is_tls(R_X86_64_PC32, EM_X86_64));
assert!(x86_reloc_is_tls(R_386_TLS_GD, EM_386));
assert!(!x86_reloc_is_tls(R_386_32, EM_386));
}
#[test]
fn test_reloc_is_pc_relative() {
assert!(x86_reloc_is_pc_relative(R_X86_64_PC32, EM_X86_64));
assert!(x86_reloc_is_pc_relative(R_X86_64_PLT32, EM_X86_64));
assert!(x86_reloc_is_pc_relative(R_X86_64_GOTPCREL, EM_X86_64));
assert!(!x86_reloc_is_pc_relative(R_X86_64_64, EM_X86_64));
assert!(x86_reloc_is_pc_relative(R_386_PC32, EM_386));
assert!(!x86_reloc_is_pc_relative(R_386_32, EM_386));
}
#[test]
fn test_dynamic_entry_new() {
let e = X86DynamicEntry::new(DT_NEEDED, 5);
assert_eq!(e.d_tag, DT_NEEDED);
assert_eq!(e.d_val, 5);
assert_eq!(e.tag_name(), "DT_NEEDED");
}
#[test]
fn test_all_dynamic_tags_known() {
let e = X86DynamicEntry::new(DT_SONAME, 0);
assert_eq!(e.tag_name(), "DT_SONAME");
let e2 = X86DynamicEntry::new(DT_RUNPATH, 0);
assert_eq!(e2.tag_name(), "DT_RUNPATH");
let e3 = X86DynamicEntry::new(DT_FLAGS_1, 0);
assert_eq!(e3.tag_name(), "DT_FLAGS_1");
let e4 = X86DynamicEntry::new(999999, 0);
assert_eq!(e4.tag_name(), "UNKNOWN");
}
#[test]
fn test_note_build_id() {
let id = vec![0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe];
let note = X86Note::new_build_id(&id);
assert_eq!(note.n_name, ELF_NOTE_GNU);
assert_eq!(note.n_type, NT_GNU_BUILD_ID);
assert_eq!(note.n_desc, id);
assert_eq!(note.type_name(), "NT_GNU_BUILD_ID");
}
#[test]
fn test_note_abi_tag() {
let note = X86Note::new_abi_tag(0, 3, 2, 1);
assert_eq!(note.n_name, ELF_NOTE_GNU);
assert_eq!(note.n_type, NT_GNU_ABI_TAG);
assert_eq!(note.type_name(), "NT_GNU_ABI_TAG");
assert_eq!(note.n_desc.len(), 16);
}
#[test]
fn test_note_gold_version() {
let note = X86Note::new_gold_version("1.16");
assert_eq!(note.n_name, ELF_NOTE_GNU);
assert_eq!(note.n_type, NT_GNU_GOLD_VERSION);
assert_eq!(note.type_name(), "NT_GNU_GOLD_VERSION");
assert_eq!(note.n_desc, b"1.16".to_vec());
}
#[test]
fn test_elf_hash_computation() {
let h1 = elf_hash("main");
let h2 = elf_hash("main");
assert_eq!(h1, h2);
assert!(elf_hash("") == 0 || elf_hash("") != 0);
}
#[test]
fn test_elf_gnu_hash_computation() {
let h1 = elf_gnu_hash("printf");
let h2 = elf_gnu_hash("printf");
assert_eq!(h1, h2);
assert!(elf_gnu_hash("x") != 0);
}
#[test]
fn test_elf_hash_different_names() {
assert_ne!(elf_hash("foo"), elf_hash("bar"));
}
#[test]
fn test_elf_gnu_hash_different_names() {
assert_ne!(elf_gnu_hash("foo"), elf_gnu_hash("bar"));
}
#[test]
fn test_hash_table_new() {
let ht = X86HashTable::new(8, 20);
assert_eq!(ht.nbucket, 8);
assert_eq!(ht.nchain, 20);
assert_eq!(ht.buckets.len(), 8);
assert_eq!(ht.chains.len(), 20);
}
#[test]
fn test_hash_table_lookup_empty() {
let ht = X86HashTable::new(4, 4);
assert_eq!(ht.lookup(100, |_| Some(100)), None);
}
#[test]
fn test_gnu_hash_table_new() {
let ght = X86GnuHashTable::new(8, 0, 4, 5);
assert_eq!(ght.nbuckets, 8);
assert_eq!(ght.symoffset, 0);
assert_eq!(ght.bloom_size, 4);
assert_eq!(ght.bloom_shift, 5);
}
#[test]
fn test_gnu_hash_table_lookup_empty() {
let ght = X86GnuHashTable::new(8, 0, 0, 5);
assert_eq!(ght.lookup(100, |_| Some(100)), None);
}
#[test]
fn test_verdef_new() {
let vd = X86Verdef::new(1, elf_hash("GLIBC_2.0"), "GLIBC_2.0");
assert_eq!(vd.vd_version, 1);
assert_eq!(vd.vd_ndx, 1);
assert_eq!(vd.version_name, "GLIBC_2.0");
}
#[test]
fn test_verneed_new() {
let mut vn = X86Verneed::new("libc.so.6");
assert_eq!(vn.file_name, "libc.so.6");
assert_eq!(vn.vn_cnt, 0);
vn.add_needed(elf_hash("GLIBC_2.0"), 0, 2, "GLIBC_2.0");
assert_eq!(vn.vn_cnt, 1);
assert_eq!(vn.needed_versions.len(), 1);
assert_eq!(vn.needed_versions[0].vna_name, "GLIBC_2.0");
}
#[test]
fn test_comdat_group() {
let mut cg = X86ComdatGroup::new(".group1", 2);
assert_eq!(cg.name, ".group1");
assert_eq!(cg.signature, 2);
cg.add_member(3);
cg.add_member(4);
assert_eq!(cg.member_sections.len(), 2);
}
#[test]
fn test_compressed_section() {
let cs = X86CompressedSection::new(ELFCOMPRESS_ZLIB, 1024, 16, vec![1, 2, 3]);
assert!(cs.is_zlib());
assert!(!cs.is_zstd());
assert_eq!(cs.ch_size, 1024);
assert_eq!(cs.ch_addralign, 16);
assert_eq!(cs.decompressed_data.len(), 3);
}
#[test]
fn test_compressed_section_zstd() {
let cs = X86CompressedSection::new(ELFCOMPRESS_ZSTD, 2048, 8, vec![]);
assert!(cs.is_zstd());
assert!(!cs.is_zlib());
}
#[test]
fn test_x86_elf_deep_new() {
let elf = X86ELFDeep::new();
assert_eq!(elf.header.e_machine, EM_X86_64);
assert!(elf.valid);
assert!(elf.errors.is_empty());
assert!(elf.warnings.is_empty());
}
#[test]
fn test_x86_elf_deep_i386() {
let elf = X86ELFDeep::new_i386();
assert_eq!(elf.header.e_machine, EM_386);
assert!(!elf.header.ident.is_64bit());
}
#[test]
fn test_validate_empty_elf() {
let mut elf = X86ELFDeep::new();
assert!(elf.validate());
}
#[test]
fn test_validate_invalid_magic() {
let mut elf = X86ELFDeep::new();
elf.header.ident.magic = [0x00, 0x00, 0x00, 0x00];
assert!(!elf.validate());
assert!(!elf.errors.is_empty());
}
#[test]
fn test_validate_exec_no_load() {
let mut elf = X86ELFDeep::new();
elf.header.e_type = ET_EXEC;
assert!(!elf.validate());
}
#[test]
fn test_validate_exec_with_load_no_interp() {
let mut elf = X86ELFDeep::new();
elf.header.e_type = ET_EXEC;
elf.program_headers
.push(X86ProgramHeader::new_load(0x400000, 0, 0x100, 0x100, PF_R | PF_X));
assert!(elf.validate());
}
#[test]
fn test_parse_minimal_elf64() {
let data = make_minimal_elf64_data();
let mut elf = X86ELFDeep::new();
assert!(elf.parse(&data).is_ok());
assert_eq!(elf.header.e_machine, EM_X86_64);
}
#[test]
fn test_parse_minimal_elf32() {
let data = make_minimal_elf32_data();
let mut elf = X86ELFDeep::new_i386();
assert!(elf.parse(&data).is_ok());
assert_eq!(elf.header.e_machine, EM_386);
}
#[test]
fn test_parse_too_short() {
let data = vec![0u8; 10];
let mut elf = X86ELFDeep::new();
assert!(elf.parse(&data).is_err());
}
#[test]
fn test_parse_bad_magic() {
let mut data = make_minimal_elf64_data();
data[0] = 0x00;
let mut elf = X86ELFDeep::new();
assert!(elf.parse(&data).is_err());
}
#[test]
fn test_elf_report() {
let elf = X86ELFDeep::new();
let report = elf.report();
assert!(report.contains("X86 ELF Deep Report"));
assert!(report.contains("x86_64"));
assert!(report.contains("Valid: true"));
}
#[test]
fn test_find_symbol() {
let mut elf = X86ELFDeep::new();
elf.symbols
.push(X86Symbol::new("main", x86_st_info(STB_GLOBAL, STT_FUNC), 1, 0x4000, 0x50));
assert!(elf.find_symbol("main").is_some());
assert!(elf.find_symbol("nonexistent").is_none());
}
#[test]
fn test_find_dynamic_symbol() {
let mut elf = X86ELFDeep::new();
elf.dynamic_symbols
.push(X86Symbol::new("printf", x86_st_info(STB_GLOBAL, STT_FUNC), 0, 0, 0));
assert!(elf.find_dynamic_symbol("printf").is_some());
assert!(elf.find_dynamic_symbol("nonexistent").is_none());
}
#[test]
fn test_symbols_by_type() {
let mut elf = X86ELFDeep::new();
elf.symbols.push(X86Symbol::new(
"f1",
x86_st_info(STB_GLOBAL, STT_FUNC),
1,
0x1000,
0x10,
));
elf.symbols.push(X86Symbol::new(
"f2",
x86_st_info(STB_GLOBAL, STT_FUNC),
2,
0x2000,
0x20,
));
elf.symbols.push(X86Symbol::new(
"obj1",
x86_st_info(STB_GLOBAL, STT_OBJECT),
3,
0x3000,
0x30,
));
let funcs = elf.symbols_by_type(STT_FUNC);
assert_eq!(funcs.len(), 2);
let objs = elf.symbols_by_type(STT_OBJECT);
assert_eq!(objs.len(), 1);
}
#[test]
fn test_symbols_by_binding() {
let mut elf = X86ELFDeep::new();
elf.symbols.push(X86Symbol::new(
"local1",
x86_st_info(STB_LOCAL, STT_OBJECT),
0,
0,
0,
));
elf.symbols.push(X86Symbol::new(
"global1",
x86_st_info(STB_GLOBAL, STT_FUNC),
0,
0,
0,
));
assert_eq!(elf.symbols_by_binding(STB_LOCAL).len(), 1);
assert_eq!(elf.symbols_by_binding(STB_GLOBAL).len(), 1);
}
#[test]
fn test_exported_symbols() {
let mut elf = X86ELFDeep::new();
elf.symbols.push(X86Symbol::new(
"exported",
x86_st_info(STB_GLOBAL, STT_FUNC),
1,
0x4000,
0x10,
));
elf.symbols.push(X86Symbol::new(
"hidden",
x86_st_info(STB_LOCAL, STT_OBJECT),
2,
0x5000,
0x10,
));
elf.symbols.push(X86Symbol::new(
"undef_global",
x86_st_info(STB_GLOBAL, STT_NOTYPE),
SHN_UNDEF,
0,
0,
));
let exported = elf.exported_symbols();
assert_eq!(exported.len(), 1);
assert_eq!(exported[0].name, "exported");
}
#[test]
fn test_relocation_types_used() {
let mut elf = X86ELFDeep::new();
elf.relocations
.push(X86Relocation::new(0, R_X86_64_PC32, 0, 0));
elf.relocations
.push(X86Relocation::new(8, R_X86_64_64, 0, 0));
elf.relocations
.push(X86Relocation::new(16, R_X86_64_PC32, 0, 0));
let types = elf.relocation_types_used();
assert_eq!(types.len(), 2);
assert!(types.contains(&R_X86_64_PC32));
assert!(types.contains(&R_X86_64_64));
}
#[test]
fn test_relocation_type_counts() {
let mut elf = X86ELFDeep::new();
elf.relocations
.push(X86Relocation::new(0, R_X86_64_PC32, 0, 0));
elf.relocations
.push(X86Relocation::new(8, R_X86_64_PC32, 0, 0));
elf.relocations
.push(X86Relocation::new(16, R_X86_64_64, 0, 0));
let counts = elf.relocation_type_counts();
assert_eq!(counts.get(&R_X86_64_PC32), Some(&2));
assert_eq!(counts.get(&R_X86_64_64), Some(&1));
}
#[test]
fn test_dynamic_operations() {
let mut elf = X86ELFDeep::new();
elf.dynamic_entries
.push(X86DynamicEntry::new(DT_NEEDED, 1));
elf.dynamic_entries
.push(X86DynamicEntry::new(DT_FLAGS, DF_BIND_NOW | DF_TEXTREL));
elf.dynamic_entries
.push(X86DynamicEntry::new(DT_FLAGS_1, DF_1_PIE));
assert_eq!(elf.get_dynamic(DT_NEEDED), Some(1));
assert!(elf.has_dynamic_flag(DF_BIND_NOW));
assert!(elf.has_dynamic_flag_1(DF_1_PIE));
assert!(!elf.has_dynamic_flag_1(DF_1_NOW));
}
#[test]
fn test_total_load_size() {
let mut elf = X86ELFDeep::new();
elf.program_headers
.push(X86ProgramHeader::new_load(0, 0, 0x100, 0x200, PF_R));
elf.program_headers
.push(X86ProgramHeader::new_load(0, 0x100, 0x300, 0x300, PF_R | PF_W));
assert_eq!(elf.total_load_size(), 0x200 + 0x300);
}
#[test]
fn test_read_strtab() {
let tab = b"hello\0world\0";
assert_eq!(read_strtab(tab, 0), "hello");
assert_eq!(read_strtab(tab, 6), "world");
assert_eq!(read_strtab(tab, 100), "");
}
#[test]
fn test_x86_st_info() {
let info = x86_st_info(STB_GLOBAL, STT_FUNC);
assert_eq!(info >> 4, STB_GLOBAL);
assert_eq!(info & 0x0f, STT_FUNC);
}
#[test]
fn test_x86_st_other() {
assert_eq!(x86_st_other(STV_HIDDEN), STV_HIDDEN);
assert_eq!(x86_st_other(STV_PROTECTED), STV_PROTECTED);
}
#[test]
fn test_elf64_r_info_roundtrip() {
let (sym, typ) = x86_elf64_r_info(x86_elf64_r_info_encode(42, R_X86_64_PC32));
assert_eq!(sym, 42);
assert_eq!(typ, R_X86_64_PC32);
}
#[test]
fn test_elf32_r_info_roundtrip() {
let (sym, typ) = x86_elf32_r_info(x86_elf32_r_info_encode(42, R_386_PC32));
assert_eq!(sym, 42);
assert_eq!(typ, R_386_PC32);
}
#[test]
fn test_elf_class_from_ident() {
assert_eq!(
X86ElfClass::from_ident(ELFCLASS64),
X86ElfClass::Elf64
);
assert_eq!(
X86ElfClass::from_ident(ELFCLASS32),
X86ElfClass::Elf32
);
}
#[test]
fn test_default_elf_deep() {
let elf = X86ELFDeep::default();
assert_eq!(elf.header.e_machine, EM_X86_64);
}
#[test]
fn test_hash_table_lookup_finds_value() {
let mut ht = X86HashTable::new(4, 4);
let h = elf_hash("test");
ht.buckets[(h % 4) as usize] = 0;
ht.chains[0] = 3; assert_eq!(ht.lookup(h, |_| Some(999)), None);
}
#[test]
fn test_all_reloc_names_coverage() {
for rt in 0..50u32 {
let name = x86_64_reloc_name(rt);
assert!(!name.is_empty());
}
for rt in 0..50u32 {
let name = i386_reloc_name(rt);
assert!(!name.is_empty());
}
}
#[test]
fn test_parse_with_section_headers() {
let mut data = vec![0u8; 256];
data[0..4].copy_from_slice(&ELF_MAGIC_BYTES);
data[EI_CLASS] = ELFCLASS64;
data[EI_DATA] = ELFDATA2LSB;
data[EI_VERSION] = 1;
data[16..18].copy_from_slice(&ET_REL.to_le_bytes());
data[18..20].copy_from_slice(&EM_X86_64.to_le_bytes());
data[20..24].copy_from_slice(&1u32.to_le_bytes());
data[52..54].copy_from_slice(&64u16.to_le_bytes());
data[58..60].copy_from_slice(&64u16.to_le_bytes());
data[40..48].copy_from_slice(&120u64.to_le_bytes());
data[60..62].copy_from_slice(&2u16.to_le_bytes());
data[62..64].copy_from_slice(&1u16.to_le_bytes());
data[184..188].copy_from_slice(&3u32.to_le_bytes()); data[188..192].copy_from_slice(&0u32.to_le_bytes()); data[200..208].copy_from_slice(&0u64.to_le_bytes()); data[208..216].copy_from_slice(&10u64.to_le_bytes()); data[216..220].copy_from_slice(&0u32.to_le_bytes());
let mut elf = X86ELFDeep::new();
assert!(elf.parse(&data).is_ok());
}
#[test]
fn test_all_phdr_types_covered() {
let types = [
PT_NULL,
PT_LOAD,
PT_DYNAMIC,
PT_INTERP,
PT_NOTE,
PT_SHLIB,
PT_PHDR,
PT_TLS,
PT_GNU_EH_FRAME,
PT_GNU_STACK,
PT_GNU_RELRO,
PT_GNU_PROPERTY,
];
for t in &types {
let mut ph = X86ProgramHeader::new_load(0, 0, 0, 0, 0);
ph.p_type = *t;
let name = ph.type_name();
assert!(!name.is_empty());
assert_ne!(name, "UNKNOWN");
}
}
#[test]
fn test_section_flags_string_exhaustive() {
let all_flags = SHF_WRITE
| SHF_ALLOC
| SHF_EXECINSTR
| SHF_MERGE
| SHF_STRINGS
| SHF_GROUP
| SHF_TLS
| SHF_COMPRESSED
| SHF_EXCLUDE;
let sh = X86SectionHeader::new("test", SHT_PROGBITS, all_flags, 0, 0);
let fs = sh.flags_string();
assert!(fs.contains('W'));
assert!(fs.contains('A'));
assert!(fs.contains('X'));
assert!(fs.contains('M'));
assert!(fs.contains('S'));
assert!(fs.contains('G'));
assert!(fs.contains('T'));
assert!(fs.contains('C'));
assert!(fs.contains('E'));
}
#[test]
fn test_build_gnu_hash_empty() {
let mut elf = X86ELFDeep::new();
elf.build_gnu_hash(8, 0, 5);
assert!(elf.gnu_hash_table.is_some());
}
#[test]
fn test_build_gnu_hash_with_symbols() {
let mut elf = X86ELFDeep::new();
elf.dynamic_symbols.push(X86Symbol::new(
"sym1",
x86_st_info(STB_GLOBAL, STT_FUNC),
0,
0,
0,
));
elf.dynamic_symbols.push(X86Symbol::new(
"sym2",
x86_st_info(STB_GLOBAL, STT_FUNC),
0,
0,
0,
));
elf.build_gnu_hash(4, 0, 5);
assert!(elf.gnu_hash_table.is_some());
let ght = elf.gnu_hash_table.as_ref().unwrap();
assert_eq!(ght.nbuckets, 4);
}
#[test]
fn test_arm_exidx_entry() {
let entry = ArmExidxEntry::new(0x1000, 0x80b0a8b2);
assert_eq!(entry.addr, 0x1000);
assert_eq!(entry.data, 0x80b0a8b2);
}
#[test]
fn test_all_shflags_constant() {
assert_eq!(ALL_SHFLAGS.len(), 12);
for (flag, name) in ALL_SHFLAGS {
assert!(*flag > 0);
assert!(!name.is_empty());
}
}
#[test]
fn test_all_dynamic_tags_constant() {
assert!(ALL_DYNAMIC_TAGS.len() > 40);
for (tag, name) in ALL_DYNAMIC_TAGS {
assert!(*tag > 0 || *tag == DT_NULL);
assert!(!name.is_empty());
}
}
#[test]
fn test_elf_deep_no_panic_on_edge_cases() {
let mut elf = X86ELFDeep::new();
assert!(elf.find_symbol("foo").is_none());
assert!(elf.find_dynamic_symbol("bar").is_none());
assert_eq!(elf.symbols_by_type(STT_FUNC).len(), 0);
assert_eq!(elf.exported_symbols().len(), 0);
assert_eq!(elf.relocation_types_used().len(), 0);
assert!(elf.get_dynamic(DT_NEEDED).is_none());
assert!(!elf.has_dynamic_flag(DF_BIND_NOW));
assert_eq!(elf.total_load_size(), 0);
}
#[test]
fn test_parse_empty_program_headers() {
let data = make_minimal_elf64_data();
let mut elf = X86ELFDeep::new();
elf.parse(&data).unwrap();
assert!(elf.program_headers.is_empty());
}
#[test]
fn test_all_phdr_constructors() {
let ph = X86ProgramHeader::new_load(0, 0, 0, 0, 0);
assert_eq!(ph.p_type, PT_LOAD);
let ph = X86ProgramHeader::new_dynamic(0, 0);
assert_eq!(ph.p_type, PT_DYNAMIC);
let ph = X86ProgramHeader::new_interp(0, 0);
assert_eq!(ph.p_type, PT_INTERP);
let ph = X86ProgramHeader::new_note(0, 0);
assert_eq!(ph.p_type, PT_NOTE);
let ph = X86ProgramHeader::new_tls(0, 0, 0, 0);
assert_eq!(ph.p_type, PT_TLS);
let ph = X86ProgramHeader::new_gnu_stack(0, 0);
assert_eq!(ph.p_type, PT_GNU_STACK);
let ph = X86ProgramHeader::new_gnu_relro(0, 0);
assert_eq!(ph.p_type, PT_GNU_RELRO);
let ph = X86ProgramHeader::new_gnu_eh_frame(0, 0);
assert_eq!(ph.p_type, PT_GNU_EH_FRAME);
let ph = X86ProgramHeader::new_gnu_property(0, 0);
assert_eq!(ph.p_type, PT_GNU_PROPERTY);
}
#[test]
fn test_reloc_needs_got_exhaustive() {
let got_types = [
R_X86_64_GOT32,
R_X86_64_GOTPCREL,
R_X86_64_GOTTPOFF,
R_X86_64_GOTPCRELX,
R_X86_64_REX_GOTPCRELX,
R_X86_64_GOTPC32_TLSDESC,
];
for t in &got_types {
assert!(x86_reloc_needs_got_x86_64(*t));
}
let non_got_types = [R_X86_64_PC32, R_X86_64_64, R_X86_64_COPY, R_X86_64_JUMP_SLOT];
for t in &non_got_types {
assert!(!x86_reloc_needs_got_x86_64(*t));
}
}
#[test]
fn test_phdr_type_name_many() {
let types = &[
PT_NULL,
PT_LOAD,
PT_DYNAMIC,
PT_INTERP,
PT_NOTE,
PT_SHLIB,
PT_PHDR,
PT_TLS,
PT_GNU_EH_FRAME,
PT_GNU_STACK,
PT_GNU_RELRO,
PT_GNU_PROPERTY,
];
for t in types {
let ph = X86ProgramHeader {
p_type: *t,
p_flags: 0,
p_offset: 0,
p_vaddr: 0,
p_paddr: 0,
p_filesz: 0,
p_memsz: 0,
p_align: 0,
};
let tn = ph.type_name();
assert!(!tn.is_empty(), "Empty type_name for p_type={}", t);
}
}
}