use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::io::{self, Write};
use std::mem;
pub const ELF_TEXT_SECTION: &str = ".text";
pub const ELF_DATA_SECTION: &str = ".data";
pub const ELF_RODATA_SECTION: &str = ".rodata";
pub const ELF_BSS_SECTION: &str = ".bss";
pub const ELF_INIT_ARRAY: &str = ".init_array";
pub const ELF_FINI_ARRAY: &str = ".fini_array";
pub const ELF_GOT_SECTION: &str = ".got";
pub const ELF_PLT_SECTION: &str = ".plt";
pub const ELF_DEBUG_INFO: &str = ".debug_info";
pub const ELF_DEBUG_LINE: &str = ".debug_line";
pub const ELF_DEBUG_FRAME: &str = ".debug_frame";
pub const ELF_DEBUG_ARANGES: &str = ".debug_aranges";
pub const ELF_DEBUG_ABBREV: &str = ".debug_abbrev";
pub const ELF_DEBUG_STR: &str = ".debug_str";
pub const ELF_DEBUG_LOC: &str = ".debug_loc";
pub const ELF_DEBUG_RANGES: &str = ".debug_ranges";
pub const ELF_EH_FRAME: &str = ".eh_frame";
pub const ELF_COMMENT_SECTION: &str = ".comment";
pub const ELF_NOTE_SECTION: &str = ".note";
pub const COFF_TEXT_SECTION: &str = ".text";
pub const COFF_DATA_SECTION: &str = ".data";
pub const COFF_RDATA_SECTION: &str = ".rdata";
pub const COFF_BSS_SECTION: &str = ".bss";
pub const COFF_PDATA_SECTION: &str = ".pdata";
pub const COFF_XDATA_SECTION: &str = ".xdata";
pub const COFF_DEBUG_SECTION: &str = ".debug$S";
pub const MACHO_TEXT_SECTION: &str = "__text";
pub const MACHO_DATA_SECTION: &str = "__data";
pub const MACHO_CSTRING_SECTION: &str = "__cstring";
pub const MACHO_CONST_SECTION: &str = "__const";
pub const MACHO_BSS_SECTION: &str = "__bss";
pub const MACHO_EH_FRAME_SECTION: &str = "__eh_frame";
pub const MACHO_DEBUG_INFO: &str = "__debug_info";
pub const MACHO_DEBUG_LINE: &str = "__debug_line";
pub const MACHO_DEBUG_FRAME: &str = "__debug_frame";
pub const X86_MAX_INSTRUCTION_SIZE: usize = 15;
pub const X86_MAX_PREFIX_BYTES: usize = 4;
pub const X86_MAX_OPCODE_BYTES: usize = 3;
pub const X86_MIN_NOP_SIZE: usize = 1;
pub const X86_MAX_NOP_SIZE: usize = 15;
pub const X86_SHORT_BRANCH_MAX: i32 = 127;
pub const X86_SHORT_BRANCH_MIN: i32 = -128;
pub const X86_NEAR_BRANCH_MAX: i64 = 0x7FFF_FFFF;
pub const X86_NEAR_BRANCH_MIN: i64 = -0x8000_0000;
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_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_GOTPCRELX: u32 = 41;
pub const R_X86_64_REX_GOTPCRELX: u32 = 42;
pub const IMAGE_REL_AMD64_ABSOLUTE: u16 = 0x0000;
pub const IMAGE_REL_AMD64_ADDR64: u16 = 0x0001;
pub const IMAGE_REL_AMD64_ADDR32: u16 = 0x0002;
pub const IMAGE_REL_AMD64_ADDR32NB: u16 = 0x0003;
pub const IMAGE_REL_AMD64_REL32: u16 = 0x0004;
pub const IMAGE_REL_AMD64_REL32_1: u16 = 0x0005;
pub const IMAGE_REL_AMD64_REL32_2: u16 = 0x0006;
pub const IMAGE_REL_AMD64_REL32_3: u16 = 0x0007;
pub const IMAGE_REL_AMD64_REL32_4: u16 = 0x0008;
pub const IMAGE_REL_AMD64_REL32_5: u16 = 0x0009;
pub const IMAGE_REL_AMD64_SECTION: u16 = 0x000A;
pub const IMAGE_REL_AMD64_SECREL: u16 = 0x000B;
pub const IMAGE_REL_AMD64_SECREL7: u16 = 0x000C;
pub const IMAGE_REL_AMD64_TOKEN: u16 = 0x000D;
pub const IMAGE_REL_AMD64_SREL32: u16 = 0x000E;
pub const IMAGE_REL_AMD64_PAIR: u16 = 0x000F;
pub const IMAGE_REL_AMD64_SSPAN32: u16 = 0x0010;
pub const X86_64_RELOC_UNSIGNED: u8 = 0;
pub const X86_64_RELOC_SIGNED: u8 = 1;
pub const X86_64_RELOC_BRANCH: u8 = 2;
pub const X86_64_RELOC_GOT_LOAD: u8 = 3;
pub const X86_64_RELOC_GOT: u8 = 4;
pub const X86_64_RELOC_SUBTRACTOR: u8 = 5;
pub const X86_64_RELOC_SIGNED_1: u8 = 6;
pub const X86_64_RELOC_SIGNED_2: u8 = 7;
pub const X86_64_RELOC_SIGNED_4: u8 = 8;
pub const X86_64_RELOC_TLV: u8 = 9;
const ELF_MAGIC: [u8; 4] = [0x7f, b'E', b'L', b'F'];
const ELF_CLASS_64: u8 = 2;
const ELF_DATA_LSB: u8 = 1;
const ELF_VERSION_CURRENT: u8 = 1;
const ELF_OSABI_SYSV: u8 = 0;
const ELF_OSABI_LINUX: u8 = 3;
const ELF_ET_REL: u16 = 1;
const ELF_ET_EXEC: u16 = 2;
const ELF_ET_DYN: u16 = 3;
const EM_X86_64: u16 = 62;
const EM_386: u16 = 3;
const SHT_NULL: u32 = 0;
const SHT_PROGBITS: u32 = 1;
const SHT_SYMTAB: u32 = 2;
const SHT_STRTAB: u32 = 3;
const SHT_RELA: u32 = 4;
const SHT_HASH: u32 = 5;
const SHT_DYNAMIC: u32 = 6;
const SHT_NOTE: u32 = 7;
const SHT_NOBITS: u32 = 8;
const SHT_REL: u32 = 9;
const SHT_SHLIB: u32 = 10;
const SHT_DYNSYM: u32 = 11;
const SHT_INIT_ARRAY: u32 = 14;
const SHT_FINI_ARRAY: u32 = 15;
const SHT_X86_64_UNWIND: u32 = 0x7000_0001;
const SHF_WRITE: u64 = 0x1;
const SHF_ALLOC: u64 = 0x2;
const SHF_EXECINSTR: u64 = 0x4;
const SHF_MERGE: u64 = 0x10;
const SHF_STRINGS: u64 = 0x20;
const SHF_INFO_LINK: u64 = 0x40;
const SHF_LINK_ORDER: u64 = 0x80;
const STB_LOCAL: u8 = 0;
const STB_GLOBAL: u8 = 1;
const STB_WEAK: u8 = 2;
const STT_NOTYPE: u8 = 0;
const STT_OBJECT: u8 = 1;
const STT_FUNC: u8 = 2;
const STT_SECTION: u8 = 3;
const STT_FILE: u8 = 4;
const STT_COMMON: u8 = 5;
const STT_TLS: u8 = 6;
const STT_GNU_IFUNC: u8 = 10;
const STV_DEFAULT: u8 = 0;
const STV_INTERNAL: u8 = 1;
const STV_HIDDEN: u8 = 2;
const STV_PROTECTED: u8 = 3;
const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664;
const IMAGE_FILE_MACHINE_I386: u16 = 0x014C;
const IMAGE_FILE_RELOCS_STRIPPED: u16 = 0x0001;
const IMAGE_FILE_EXECUTABLE_IMAGE: u16 = 0x0002;
const IMAGE_FILE_LINE_NUMS_STRIPPED: u16 = 0x0004;
const IMAGE_FILE_LARGE_ADDRESS_AWARE: u16 = 0x0020;
const IMAGE_FILE_32BIT_MACHINE: u16 = 0x0100;
const IMAGE_FILE_DLL: u16 = 0x2000;
const COFF_SECTION_HEADER_SIZE: usize = 40;
const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020;
const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040;
const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080;
const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000;
const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000;
const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000;
const IMAGE_SCN_MEM_DISCARDABLE: u32 = 0x0200_0000;
const IMAGE_SCN_LNK_REMOVE: u32 = 0x0000_0800;
const IMAGE_SCN_LNK_COMDAT: u32 = 0x0000_1000;
const IMAGE_SYM_CLASS_END_OF_FUNCTION: u8 = 0xFF;
const IMAGE_SYM_CLASS_NULL: u8 = 0;
const IMAGE_SYM_CLASS_EXTERNAL: u8 = 2;
const IMAGE_SYM_CLASS_STATIC: u8 = 3;
const IMAGE_SYM_CLASS_LABEL: u8 = 6;
const IMAGE_SYM_CLASS_FUNCTION: u8 = 101;
const IMAGE_SYM_CLASS_FILE: u8 = 103;
const IMAGE_SYM_CLASS_SECTION: u8 = 104;
const MH_MAGIC_64: u32 = 0xFEEDFACF;
const MH_CIGAM_64: u32 = 0xCFFAEDFE;
const MH_OBJECT: u32 = 0x1;
const CPU_TYPE_X86_64: u32 = 0x0100_0007;
const CPU_SUBTYPE_X86_64_ALL: u32 = 0x3;
const CPU_TYPE_I386: u32 = 0x7;
const CPU_SUBTYPE_I386_ALL: u32 = 0x3;
const LC_SEGMENT_64: u32 = 0x19;
const LC_SYMTAB: u32 = 0x2;
const LC_DYSYMTAB: u32 = 0xB;
const LC_BUILD_VERSION: u32 = 0x32;
const VM_PROT_READ: u32 = 0x1;
const VM_PROT_WRITE: u32 = 0x2;
const VM_PROT_EXECUTE: u32 = 0x4;
const S_REGULAR: u32 = 0x0;
const S_ZEROFILL: u32 = 0x1;
const S_CSTRING_LITERALS: u32 = 0x2;
const S_4BYTE_LITERALS: u32 = 0x3;
const S_8BYTE_LITERALS: u32 = 0x4;
const S_LITERAL_POINTERS: u32 = 0x5;
const S_NON_LAZY_SYMBOL_POINTERS: u32 = 0x6;
const S_LAZY_SYMBOL_POINTERS: u32 = 0x7;
const S_SYMBOL_STUBS: u32 = 0x8;
const S_MOD_INIT_FUNC_POINTERS: u32 = 0x9;
const S_MOD_TERM_FUNC_POINTERS: u32 = 0xA;
const S_COALESCED: u32 = 0xB;
const S_GB_ZEROFILL: u32 = 0xC;
const S_INTERPOSING: u32 = 0xD;
const S_16BYTE_LITERALS: u32 = 0xE;
const S_DTRACE_DOF: u32 = 0xF;
const S_LAZY_DYLIB_SYMBOL_POINTERS: u32 = 0x10;
const S_THREAD_LOCAL_REGULAR: u32 = 0x11;
const S_THREAD_LOCAL_ZEROFILL: u32 = 0x12;
const S_THREAD_LOCAL_VARIABLES: u32 = 0x13;
const S_THREAD_LOCAL_VARIABLE_POINTERS: u32 = 0x14;
const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: u32 = 0x15;
const S_ATTR_PURE_INSTRUCTIONS: u32 = 0x8000_0000;
const S_ATTR_SOME_INSTRUCTIONS: u32 = 0x0000_0400;
const S_ATTR_DEBUG: u32 = 0x0200_0000;
const S_ATTR_SELF_MODIFYING_CODE: u32 = 0x0400_0000;
const S_ATTR_LIVE_SUPPORT: u32 = 0x0800_0000;
const S_ATTR_NO_DEAD_STRIP: u32 = 0x1000_0000;
const S_ATTR_STRIP_STATIC_SYMS: u32 = 0x2000_0000;
const N_STAB: u8 = 0xE0;
const N_TYPE: u8 = 0x0E;
const N_EXT: u8 = 0x01;
const N_UNDF: u8 = 0x0;
const N_ABS: u8 = 0x2;
const N_SECT: u8 = 0xE;
const N_PBUD: u8 = 0xC;
const N_INDR: u8 = 0xA;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86ObjectFormat {
ELF,
COFF,
MachO,
Binary,
}
impl X86ObjectFormat {
pub fn from_triple(triple: &str) -> Self {
if triple.contains("windows") || triple.contains("win32") || triple.contains("mingw")
|| triple.contains("cygwin") || triple.contains("msvc")
{
return X86ObjectFormat::COFF;
}
if triple.contains("darwin") || triple.contains("macos") || triple.contains("ios")
|| triple.contains("macosx")
{
return X86ObjectFormat::MachO;
}
X86ObjectFormat::ELF
}
pub fn is_elf(&self) -> bool {
matches!(self, X86ObjectFormat::ELF)
}
pub fn is_coff(&self) -> bool {
matches!(self, X86ObjectFormat::COFF)
}
pub fn is_macho(&self) -> bool {
matches!(self, X86ObjectFormat::MachO)
}
pub fn text_section(&self) -> &str {
match self {
X86ObjectFormat::ELF | X86ObjectFormat::COFF | X86ObjectFormat::Binary => ".text",
X86ObjectFormat::MachO => "__text",
}
}
pub fn data_section(&self) -> &str {
match self {
X86ObjectFormat::ELF | X86ObjectFormat::COFF | X86ObjectFormat::Binary => ".data",
X86ObjectFormat::MachO => "__data",
}
}
pub fn rodata_section(&self) -> &str {
match self {
X86ObjectFormat::ELF | X86ObjectFormat::Binary => ".rodata",
X86ObjectFormat::COFF => ".rdata",
X86ObjectFormat::MachO => "__const",
}
}
pub fn bss_section(&self) -> &str {
match self {
X86ObjectFormat::ELF | X86ObjectFormat::COFF | X86ObjectFormat::Binary => ".bss",
X86ObjectFormat::MachO => "__bss",
}
}
}
impl fmt::Display for X86ObjectFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86ObjectFormat::ELF => write!(f, "ELF"),
X86ObjectFormat::COFF => write!(f, "COFF"),
X86ObjectFormat::MachO => write!(f, "Mach-O"),
X86ObjectFormat::Binary => write!(f, "Binary"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86AddrMode {
Mode16,
Mode32,
Mode64,
}
impl X86AddrMode {
pub fn pointer_size(&self) -> u8 {
match self {
X86AddrMode::Mode16 => 2,
X86AddrMode::Mode32 => 4,
X86AddrMode::Mode64 => 8,
}
}
pub fn is_64bit(&self) -> bool {
matches!(self, X86AddrMode::Mode64)
}
}
impl Default for X86AddrMode {
fn default() -> Self {
X86AddrMode::Mode64
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86FixupKind {
None,
Rel8,
Rel16,
Rel32,
Rel64,
Abs8,
Abs16,
Abs32,
Abs64,
RipRel32,
GotPcRel,
Plt32,
GotOff,
TlsGd,
TlsLd,
GotTpOff,
TpOff,
DtpOff,
GotPc32TlsDesc,
PltGOT,
TlsDesc,
GotPcRelX,
RexGotPcRelX,
Size32,
Size64,
}
impl X86FixupKind {
pub fn size_bytes(&self) -> u8 {
match self {
X86FixupKind::None => 0,
X86FixupKind::Rel8 | X86FixupKind::Abs8 => 1,
X86FixupKind::Rel16 | X86FixupKind::Abs16 => 2,
X86FixupKind::Rel32 | X86FixupKind::Abs32 | X86FixupKind::RipRel32
| X86FixupKind::Plt32 | X86FixupKind::GotPcRel | X86FixupKind::Size32
| X86FixupKind::TpOff | X86FixupKind::DtpOff | X86FixupKind::GotTpOff
| X86FixupKind::GotOff | X86FixupKind::GotPc32TlsDesc => 4,
X86FixupKind::Rel64 | X86FixupKind::Abs64 | X86FixupKind::Size64
| X86FixupKind::TlsGd | X86FixupKind::TlsLd | X86FixupKind::TlsDesc
| X86FixupKind::PltGOT | X86FixupKind::GotPcRelX | X86FixupKind::RexGotPcRelX => 8,
}
}
pub fn is_pc_relative(&self) -> bool {
matches!(
self,
X86FixupKind::Rel8
| X86FixupKind::Rel16
| X86FixupKind::Rel32
| X86FixupKind::Rel64
| X86FixupKind::RipRel32
| X86FixupKind::GotPcRel
| X86FixupKind::Plt32
| X86FixupKind::GotPcRelX
| X86FixupKind::RexGotPcRelX
| X86FixupKind::GotPc32TlsDesc
)
}
pub fn to_elf_reloc(&self) -> u32 {
match self {
X86FixupKind::None => R_X86_64_NONE,
X86FixupKind::Rel8 => R_X86_64_PC8,
X86FixupKind::Rel16 => R_X86_64_PC16,
X86FixupKind::Rel32 => R_X86_64_PC32,
X86FixupKind::Rel64 => R_X86_64_PC64,
X86FixupKind::Abs8 => R_X86_64_8,
X86FixupKind::Abs16 => R_X86_64_16,
X86FixupKind::Abs32 => R_X86_64_32,
X86FixupKind::Abs64 => R_X86_64_64,
X86FixupKind::RipRel32 => R_X86_64_PC32,
X86FixupKind::GotPcRel => R_X86_64_GOTPCREL,
X86FixupKind::Plt32 => R_X86_64_PLT32,
X86FixupKind::GotOff => R_X86_64_GOTOFF64,
X86FixupKind::TlsGd => R_X86_64_TLSGD,
X86FixupKind::TlsLd => R_X86_64_TLSLD,
X86FixupKind::GotTpOff => R_X86_64_GOTTPOFF,
X86FixupKind::TpOff => R_X86_64_TPOFF32,
X86FixupKind::DtpOff => R_X86_64_DTPOFF32,
X86FixupKind::Size32 => R_X86_64_SIZE32,
X86FixupKind::Size64 => R_X86_64_SIZE64,
X86FixupKind::GotPc32TlsDesc => R_X86_64_GOTPC32_TLSDESC,
X86FixupKind::TlsDesc => R_X86_64_TLSDESC,
X86FixupKind::GotPcRelX => R_X86_64_GOTPCRELX,
X86FixupKind::RexGotPcRelX => R_X86_64_REX_GOTPCRELX,
X86FixupKind::PltGOT | X86FixupKind::Size32 | X86FixupKind::Size64 => R_X86_64_NONE,
}
}
pub fn to_coff_reloc(&self) -> u16 {
match self {
X86FixupKind::Abs64 => IMAGE_REL_AMD64_ADDR64,
X86FixupKind::Abs32 => IMAGE_REL_AMD64_ADDR32,
X86FixupKind::Rel32 | X86FixupKind::RipRel32 => IMAGE_REL_AMD64_REL32,
X86FixupKind::Rel32 => IMAGE_REL_AMD64_REL32,
_ => IMAGE_REL_AMD64_ABSOLUTE,
}
}
pub fn name(&self) -> &str {
match self {
X86FixupKind::None => "None",
X86FixupKind::Rel8 => "Rel8",
X86FixupKind::Rel16 => "Rel16",
X86FixupKind::Rel32 => "Rel32",
X86FixupKind::Rel64 => "Rel64",
X86FixupKind::Abs8 => "Abs8",
X86FixupKind::Abs16 => "Abs16",
X86FixupKind::Abs32 => "Abs32",
X86FixupKind::Abs64 => "Abs64",
X86FixupKind::RipRel32 => "RipRel32",
X86FixupKind::GotPcRel => "GotPcRel",
X86FixupKind::Plt32 => "Plt32",
X86FixupKind::GotOff => "GotOff",
X86FixupKind::TlsGd => "TlsGd",
X86FixupKind::TlsLd => "TlsLd",
X86FixupKind::GotTpOff => "GotTpOff",
X86FixupKind::TpOff => "TpOff",
X86FixupKind::DtpOff => "DtpOff",
X86FixupKind::GotPc32TlsDesc => "GotPc32TlsDesc",
X86FixupKind::TlsDesc => "TlsDesc",
X86FixupKind::PltGOT => "PltGOT",
X86FixupKind::GotPcRelX => "GotPcRelX",
X86FixupKind::RexGotPcRelX => "RexGotPcRelX",
X86FixupKind::Size32 => "Size32",
X86FixupKind::Size64 => "Size64",
}
}
}
impl fmt::Display for X86FixupKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86SectionType {
Regular,
Bss,
Debug,
ExceptionHandling,
ReadOnly,
ThreadLocal,
Note,
MergeStrings,
MergeConstants,
Got,
Plt,
InitArray,
FiniArray,
EHFrame,
}
impl X86SectionType {
pub fn is_debug(&self) -> bool {
matches!(self, X86SectionType::Debug)
}
pub fn is_bss(&self) -> bool {
matches!(self, X86SectionType::Bss)
}
pub fn is_unwind(&self) -> bool {
matches!(self, X86SectionType::ExceptionHandling | X86SectionType::EHFrame)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct X86SectionFlags {
pub write: bool,
pub alloc: bool,
pub exec: bool,
pub merge: bool,
pub strings: bool,
pub tls: bool,
}
impl X86SectionFlags {
pub fn new() -> Self {
Self::default()
}
pub fn with_write(mut self, w: bool) -> Self {
self.write = w;
self
}
pub fn with_alloc(mut self, a: bool) -> Self {
self.alloc = a;
self
}
pub fn with_exec(mut self, e: bool) -> Self {
self.exec = e;
self
}
pub fn text() -> Self {
Self {
write: false,
alloc: true,
exec: true,
merge: false,
strings: false,
tls: false,
}
}
pub fn data() -> Self {
Self {
write: true,
alloc: true,
exec: false,
merge: false,
strings: false,
tls: false,
}
}
pub fn rodata() -> Self {
Self {
write: false,
alloc: true,
exec: false,
merge: false,
strings: false,
tls: false,
}
}
pub fn bss() -> Self {
Self {
write: true,
alloc: true,
exec: false,
merge: false,
strings: false,
tls: false,
}
}
pub fn to_elf_sh_flags(&self) -> u64 {
let mut flags: u64 = 0;
if self.write {
flags |= SHF_WRITE;
}
if self.alloc {
flags |= SHF_ALLOC;
}
if self.exec {
flags |= SHF_EXECINSTR;
}
if self.merge {
flags |= SHF_MERGE;
}
if self.strings {
flags |= SHF_STRINGS;
}
flags
}
pub fn to_coff_characteristics(&self) -> u32 {
let mut chars: u32 = IMAGE_SCN_MEM_READ;
if self.write {
chars |= IMAGE_SCN_MEM_WRITE;
}
if self.exec {
chars |= IMAGE_SCN_MEM_EXECUTE;
chars |= IMAGE_SCN_CNT_CODE;
} else if self.write {
if self.alloc {
chars |= IMAGE_SCN_CNT_INITIALIZED_DATA;
} else {
chars |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;
}
} else {
chars |= IMAGE_SCN_CNT_INITIALIZED_DATA;
}
chars
}
}
#[derive(Debug, Clone)]
pub struct X86MCSection {
pub name: String,
pub section_type: X86SectionType,
pub flags: X86SectionFlags,
pub fragments: Vec<X86MCFragment>,
pub alignment: u8,
pub entry_size: u32,
pub index: u32,
pub address: u64,
pub size: u64,
pub sub_sections: Vec<X86MCSection>,
pub is_debug: bool,
pub keep: bool,
}
impl X86MCSection {
pub fn new(name: &str, sec_type: X86SectionType, flags: X86SectionFlags) -> Self {
Self {
name: name.to_string(),
section_type: sec_type,
flags,
fragments: Vec::new(),
alignment: 16,
entry_size: 0,
index: 0,
address: 0,
size: 0,
sub_sections: Vec::new(),
is_debug: sec_type.is_debug(),
keep: false,
}
}
pub fn new_text(name: &str) -> Self {
Self::new(name, X86SectionType::Regular, X86SectionFlags::text())
}
pub fn new_data(name: &str) -> Self {
Self::new(name, X86SectionType::Regular, X86SectionFlags::data())
}
pub fn new_rodata(name: &str) -> Self {
Self::new(name, X86SectionType::ReadOnly, X86SectionFlags::rodata())
}
pub fn new_bss(name: &str) -> Self {
Self::new(name, X86SectionType::Bss, X86SectionFlags::bss())
}
pub fn new_debug(name: &str) -> Self {
Self::new(
name,
X86SectionType::Debug,
X86SectionFlags::new().with_alloc(false),
)
}
pub fn add_fragment(&mut self, frag: X86MCFragment) {
self.fragments.push(frag);
}
pub fn fragment_count(&self) -> usize {
self.fragments.len()
}
pub fn fragment_size(&self) -> usize {
self.fragments.iter().map(|f| f.size()).sum()
}
pub fn is_empty(&self) -> bool {
self.fragments.is_empty()
}
pub fn set_alignment(&mut self, align: u8) {
self.alignment = align;
}
pub fn set_debug(&mut self, debug: bool) {
self.is_debug = debug;
}
pub fn set_keep(&mut self, keep: bool) {
self.keep = keep;
}
}
#[derive(Debug, Clone)]
pub enum X86MCFragment {
Data(MCDataFragment),
Instruction(MCInstFragment),
Align(MCAlignFragment),
Fill(MCFillFragment),
Org(MCOrgFragment),
DwarfFrame(MCDwarfFrameFragment),
DwarfLine(MCDwarfLineFragment),
CompactUnwind(MCCompactUnwindFragment),
CVDefRange(MCCVDefRangeFragment),
Relaxable(MCRelaxableFragment),
}
impl X86MCFragment {
pub fn size(&self) -> usize {
match self {
X86MCFragment::Data(f) => f.data.len(),
X86MCFragment::Instruction(f) => f.bytes.len(),
X86MCFragment::Align(f) => f.max_bytes as usize,
X86MCFragment::Fill(f) => (f.size * f.value.len()) as usize,
X86MCFragment::Org(f) => f.fill_data.len(),
X86MCFragment::DwarfFrame(f) => f.data.len(),
X86MCFragment::DwarfLine(f) => f.data.len(),
X86MCFragment::CompactUnwind(f) => f.data.len(),
X86MCFragment::CVDefRange(f) => f.data.len(),
X86MCFragment::Relaxable(f) => f.current_size as usize,
}
}
pub fn kind_name(&self) -> &str {
match self {
X86MCFragment::Data(_) => "Data",
X86MCFragment::Instruction(_) => "Instruction",
X86MCFragment::Align(_) => "Align",
X86MCFragment::Fill(_) => "Fill",
X86MCFragment::Org(_) => "Org",
X86MCFragment::DwarfFrame(_) => "DwarfFrame",
X86MCFragment::DwarfLine(_) => "DwarfLine",
X86MCFragment::CompactUnwind(_) => "CompactUnwind",
X86MCFragment::CVDefRange(_) => "CVDefRange",
X86MCFragment::Relaxable(_) => "Relaxable",
}
}
pub fn is_instruction(&self) -> bool {
matches!(self, X86MCFragment::Instruction(_) | X86MCFragment::Relaxable(_))
}
pub fn is_relaxable(&self) -> bool {
matches!(self, X86MCFragment::Relaxable(_))
}
pub fn is_data(&self) -> bool {
matches!(self, X86MCFragment::Data(_))
}
pub fn get_bytes(&self) -> Vec<u8> {
match self {
X86MCFragment::Data(f) => f.data.clone(),
X86MCFragment::Instruction(f) => f.bytes.clone(),
X86MCFragment::Align(f) => f.fill_data.clone(),
X86MCFragment::Fill(f) => {
let mut out = Vec::with_capacity((f.size * f.value.len()) as usize);
for _ in 0..f.size {
out.extend_from_slice(&f.value);
}
out
}
X86MCFragment::Org(f) => f.fill_data.clone(),
X86MCFragment::DwarfFrame(f) => f.data.clone(),
X86MCFragment::DwarfLine(f) => f.data.clone(),
X86MCFragment::CompactUnwind(f) => f.data.clone(),
X86MCFragment::CVDefRange(f) => f.data.clone(),
X86MCFragment::Relaxable(f) => f.get_bytes(),
}
}
pub fn fixups(&self) -> &[X86Fixup] {
match self {
X86MCFragment::Instruction(f) => &f.fixups,
X86MCFragment::Data(f) => &f.fixups,
X86MCFragment::Relaxable(f) => &f.fixups,
_ => &[],
}
}
}
#[derive(Debug, Clone)]
pub struct MCDataFragment {
pub data: Vec<u8>,
pub fixups: Vec<X86Fixup>,
pub is_read_only: bool,
}
impl MCDataFragment {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
fixups: Vec::new(),
is_read_only: false,
}
}
pub fn new_const(data: Vec<u8>) -> Self {
Self {
data,
fixups: Vec::new(),
is_read_only: true,
}
}
pub fn add_fixup(&mut self, fixup: X86Fixup) {
self.fixups.push(fixup);
}
}
#[derive(Debug, Clone)]
pub struct MCInstFragment {
pub bytes: Vec<u8>,
pub mnemonic: Option<String>,
pub fixups: Vec<X86Fixup>,
pub relaxed: bool,
}
impl MCInstFragment {
pub fn new(bytes: Vec<u8>) -> Self {
Self {
bytes,
mnemonic: None,
fixups: Vec::new(),
relaxed: false,
}
}
pub fn with_mnemonic(mut self, mnemonic: &str) -> Self {
self.mnemonic = Some(mnemonic.to_string());
self
}
pub fn add_fixup(&mut self, fixup: X86Fixup) {
self.fixups.push(fixup);
}
}
#[derive(Debug, Clone)]
pub struct MCAlignFragment {
pub alignment: u32,
pub fill_byte: u8,
pub max_bytes: u32,
pub fill_data: Vec<u8>,
pub emit_optimal_nops: bool,
}
impl MCAlignFragment {
pub fn new(alignment: u32, fill_byte: u8, max_bytes: u32) -> Self {
Self {
alignment,
fill_byte,
max_bytes,
fill_data: Vec::new(),
emit_optimal_nops: true,
}
}
pub fn compute_padding(&self, current_offset: usize) -> usize {
let align = self.alignment as usize;
let misalignment = current_offset & (align - 1);
if misalignment == 0 {
return 0;
}
let padding = align - misalignment;
padding.min(self.max_bytes as usize)
}
pub fn compute_fill(&mut self, current_offset: usize) {
let padding = self.compute_padding(current_offset);
if self.emit_optimal_nops {
self.fill_data = generate_optimal_nops(padding);
} else {
self.fill_data = vec![self.fill_byte; padding];
}
}
}
#[derive(Debug, Clone)]
pub struct MCFillFragment {
pub value: Vec<u8>,
pub size: u64,
}
impl MCFillFragment {
pub fn new(value: Vec<u8>, size: u64) -> Self {
Self { value, size }
}
pub fn total_size(&self) -> u64 {
self.size * self.value.len() as u64
}
}
#[derive(Debug, Clone)]
pub struct MCOrgFragment {
pub address: u64,
pub fill_byte: u8,
pub fill_data: Vec<u8>,
pub expression: Option<String>,
}
impl MCOrgFragment {
pub fn new(address: u64, fill_byte: u8) -> Self {
Self {
address,
fill_byte,
fill_data: Vec::new(),
expression: None,
}
}
}
#[derive(Debug, Clone)]
pub struct MCDwarfFrameFragment {
pub data: Vec<u8>,
pub frame_offset: u64,
pub is_eh_frame: bool,
}
impl MCDwarfFrameFragment {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
frame_offset: 0,
is_eh_frame: false,
}
}
pub fn new_eh_frame(data: Vec<u8>) -> Self {
Self {
data,
frame_offset: 0,
is_eh_frame: true,
}
}
}
#[derive(Debug, Clone)]
pub struct MCDwarfLineFragment {
pub data: Vec<u8>,
pub line_offset: u64,
}
impl MCDwarfLineFragment {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
line_offset: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct MCCompactUnwindFragment {
pub data: Vec<u8>,
pub function_address: u64,
pub function_size: u64,
pub encoding_mode: u32,
}
impl MCCompactUnwindFragment {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
function_address: 0,
function_size: 0,
encoding_mode: 0,
}
}
pub fn new_rbp_frame(function_address: u64, function_size: u64) -> Self {
let mut data = Vec::new();
let header: u32 = 0x0100_0000; data.extend_from_slice(&header.to_le_bytes());
data.extend_from_slice(&(function_address as u32).to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
Self {
data,
function_address,
function_size,
encoding_mode: 1, }
}
}
#[derive(Debug, Clone)]
pub struct MCCVDefRangeFragment {
pub data: Vec<u8>,
pub offset: u32,
pub kind: u32,
}
impl MCCVDefRangeFragment {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
offset: 0,
kind: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct MCRelaxableFragment {
pub bytes: Vec<u8>,
pub mnemonic: Option<String>,
pub fixups: Vec<X86Fixup>,
pub min_size: u8,
pub current_size: u8,
pub max_size: u8,
pub target_label: Option<String>,
pub relaxed: bool,
pub relax_iterations: u32,
}
impl MCRelaxableFragment {
pub fn new(bytes: Vec<u8>, max_size: u8) -> Self {
let size = bytes.len() as u8;
Self {
bytes,
mnemonic: None,
fixups: Vec::new(),
min_size: 2,
current_size: size,
max_size: size.max(max_size),
target_label: None,
relaxed: false,
relax_iterations: 0,
}
}
pub fn new_branch(
bytes: Vec<u8>,
target: String,
min_size: u8,
max_size: u8,
) -> Self {
let size = bytes.len() as u8;
Self {
bytes,
mnemonic: None,
fixups: Vec::new(),
min_size,
current_size: size,
max_size,
target_label: Some(target),
relaxed: false,
relax_iterations: 0,
}
}
pub fn get_bytes(&self) -> Vec<u8> {
let sz = self.current_size as usize;
if sz <= self.bytes.len() {
self.bytes[..sz].to_vec()
} else {
self.bytes.clone()
}
}
pub fn can_relax(&self) -> bool {
self.current_size < self.max_size
}
pub fn relax_to(&mut self, new_size: u8, new_bytes: Vec<u8>) {
if new_size > self.current_size {
self.current_size = new_size;
self.bytes = new_bytes;
self.relaxed = true;
self.relax_iterations += 1;
}
}
pub fn relaxation_overhead(&self) -> u8 {
self.max_size.saturating_sub(self.current_size)
}
pub fn is_within_short_range(&self, distance: i64) -> bool {
distance >= X86_SHORT_BRANCH_MIN as i64 && distance <= X86_SHORT_BRANCH_MAX as i64
}
}
#[derive(Debug, Clone)]
pub struct X86Fixup {
pub offset: usize,
pub kind: X86FixupKind,
pub source: X86FixupSource,
pub addend: i64,
pub is_branch: bool,
pub needs_got: bool,
pub needs_plt: bool,
}
impl X86Fixup {
pub fn new(offset: usize, kind: X86FixupKind, source: X86FixupSource) -> Self {
Self {
offset,
kind,
source,
addend: 0,
is_branch: false,
needs_got: false,
needs_plt: false,
}
}
pub fn with_addend(mut self, addend: i64) -> Self {
self.addend = addend;
self
}
pub fn with_branch(mut self) -> Self {
self.is_branch = true;
self
}
pub fn with_got(mut self) -> Self {
self.needs_got = true;
self
}
pub fn with_plt(mut self) -> Self {
self.needs_plt = true;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum X86FixupSource {
Symbol(String),
Section(String, i64),
SymbolDiff(String, String),
Absolute(i64),
Tls(String),
}
impl fmt::Display for X86FixupSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86FixupSource::Symbol(s) => write!(f, "{}", s),
X86FixupSource::Section(s, o) => write!(f, "{}+{}", s, o),
X86FixupSource::SymbolDiff(a, b) => write!(f, "{}-{}", a, b),
X86FixupSource::Absolute(v) => write!(f, "{}", v),
X86FixupSource::Tls(s) => write!(f, "TLS({})", s),
}
}
}
#[derive(Debug, Clone)]
pub struct X86MCSymbol {
pub name: String,
pub value: u64,
pub section_index: u32,
pub size: u64,
pub binding: X86SymbolBinding,
pub sym_type: X86SymbolType,
pub visibility: X86SymbolVisibility,
pub temporary: bool,
pub defined: bool,
pub absolute: bool,
pub common: bool,
pub common_alignment: u32,
pub fragment_index: Option<usize>,
pub fragment_offset: Option<usize>,
pub referenced: bool,
}
impl X86MCSymbol {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
value: 0,
section_index: 0,
size: 0,
binding: X86SymbolBinding::Local,
sym_type: X86SymbolType::NoType,
visibility: X86SymbolVisibility::Default,
temporary: false,
defined: false,
absolute: false,
common: false,
common_alignment: 0,
fragment_index: None,
fragment_offset: None,
referenced: false,
}
}
pub fn new_defined(name: &str, section_index: u32, value: u64) -> Self {
Self {
name: name.to_string(),
value,
section_index,
size: 0,
binding: X86SymbolBinding::Local,
sym_type: X86SymbolType::NoType,
visibility: X86SymbolVisibility::Default,
temporary: false,
defined: true,
absolute: false,
common: false,
common_alignment: 0,
fragment_index: None,
fragment_offset: None,
referenced: false,
}
}
pub fn new_global(name: &str, section_index: u32, value: u64) -> Self {
Self {
name: name.to_string(),
value,
section_index,
size: 0,
binding: X86SymbolBinding::Global,
sym_type: X86SymbolType::NoType,
visibility: X86SymbolVisibility::Default,
temporary: false,
defined: true,
absolute: false,
common: false,
common_alignment: 0,
fragment_index: None,
fragment_offset: None,
referenced: false,
}
}
pub fn new_absolute(name: &str, value: u64) -> Self {
Self {
name: name.to_string(),
value,
section_index: 0,
size: 0,
binding: X86SymbolBinding::Local,
sym_type: X86SymbolType::NoType,
visibility: X86SymbolVisibility::Default,
temporary: false,
defined: true,
absolute: true,
common: false,
common_alignment: 0,
fragment_index: None,
fragment_offset: None,
referenced: false,
}
}
pub fn new_common(name: &str, size: u64, alignment: u32) -> Self {
Self {
name: name.to_string(),
value: 0,
section_index: 0,
size,
binding: X86SymbolBinding::Global,
sym_type: X86SymbolType::Object,
visibility: X86SymbolVisibility::Default,
temporary: false,
defined: false,
absolute: false,
common: true,
common_alignment: alignment,
fragment_index: None,
fragment_offset: None,
referenced: false,
}
}
pub fn new_temporary(name: &str) -> Self {
let mut sym = Self::new(name);
sym.temporary = true;
sym
}
pub fn with_binding(mut self, binding: X86SymbolBinding) -> Self {
self.binding = binding;
self
}
pub fn with_type(mut self, sym_type: X86SymbolType) -> Self {
self.sym_type = sym_type;
self
}
pub fn with_visibility(mut self, vis: X86SymbolVisibility) -> Self {
self.visibility = vis;
self
}
pub fn with_size(mut self, size: u64) -> Self {
self.size = size;
self
}
pub fn elf_st_info(&self) -> u8 {
let bind: u8 = match self.binding {
X86SymbolBinding::Local => STB_LOCAL,
X86SymbolBinding::Global => STB_GLOBAL,
X86SymbolBinding::Weak => STB_WEAK,
};
let typ: u8 = match self.sym_type {
X86SymbolType::NoType => STT_NOTYPE,
X86SymbolType::Object => STT_OBJECT,
X86SymbolType::Func => STT_FUNC,
X86SymbolType::Section => STT_SECTION,
X86SymbolType::File => STT_FILE,
X86SymbolType::Common => STT_COMMON,
X86SymbolType::Tls => STT_TLS,
X86SymbolType::GnuIfunc => STT_GNU_IFUNC,
};
(bind << 4) | (typ & 0x0F)
}
pub fn elf_st_other(&self) -> u8 {
match self.visibility {
X86SymbolVisibility::Default => STV_DEFAULT,
X86SymbolVisibility::Internal => STV_INTERNAL,
X86SymbolVisibility::Hidden => STV_HIDDEN,
X86SymbolVisibility::Protected => STV_PROTECTED,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86SymbolBinding {
Local,
Global,
Weak,
}
impl X86SymbolBinding {
pub fn as_str(&self) -> &str {
match self {
X86SymbolBinding::Local => "local",
X86SymbolBinding::Global => "global",
X86SymbolBinding::Weak => "weak",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86SymbolType {
NoType,
Object,
Func,
Section,
File,
Common,
Tls,
GnuIfunc,
}
impl X86SymbolType {
pub fn as_str(&self) -> &str {
match self {
X86SymbolType::NoType => "NOTYPE",
X86SymbolType::Object => "OBJECT",
X86SymbolType::Func => "FUNC",
X86SymbolType::Section => "SECTION",
X86SymbolType::File => "FILE",
X86SymbolType::Common => "COMMON",
X86SymbolType::Tls => "TLS",
X86SymbolType::GnuIfunc => "IFUNC",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86SymbolVisibility {
Default,
Internal,
Hidden,
Protected,
}
impl X86SymbolVisibility {
pub fn as_str(&self) -> &str {
match self {
X86SymbolVisibility::Default => "default",
X86SymbolVisibility::Internal => "internal",
X86SymbolVisibility::Hidden => "hidden",
X86SymbolVisibility::Protected => "protected",
}
}
}
#[derive(Debug, Clone)]
pub struct X86MCLabel {
pub name: String,
pub defined: bool,
pub section_index: Option<usize>,
pub fragment_index: Option<usize>,
pub offset: Option<usize>,
pub resolved_address: Option<u64>,
pub temporary: bool,
}
impl X86MCLabel {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
defined: false,
section_index: None,
fragment_index: None,
offset: None,
resolved_address: None,
temporary: false,
}
}
pub fn new_temporary(name: &str) -> Self {
let mut lbl = Self::new(name);
lbl.temporary = true;
lbl
}
pub fn define(
&mut self,
section_index: usize,
fragment_index: usize,
offset: usize,
) {
self.section_index = Some(section_index);
self.fragment_index = Some(fragment_index);
self.offset = Some(offset);
self.defined = true;
}
pub fn is_forward_ref(&self) -> bool {
!self.defined
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum X86ExprValue {
Constant(i64),
Symbol(String),
SymbolOffset(String, i64),
SymbolDiff(String, String),
Label(String),
Unresolved(String),
}
impl X86ExprValue {
pub fn as_constant(&self) -> Option<i64> {
match self {
X86ExprValue::Constant(v) => Some(*v),
_ => None,
}
}
pub fn is_resolved(&self) -> bool {
matches!(self, X86ExprValue::Constant(_))
}
pub fn needs_relocation(&self) -> bool {
matches!(
self,
X86ExprValue::Symbol(_)
| X86ExprValue::SymbolOffset(_, _)
| X86ExprValue::SymbolDiff(_, _)
| X86ExprValue::Label(_)
| X86ExprValue::Unresolved(_)
)
}
}
impl fmt::Display for X86ExprValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86ExprValue::Constant(v) => write!(f, "{}", v),
X86ExprValue::Symbol(s) => write!(f, "{}", s),
X86ExprValue::SymbolOffset(s, o) => write!(f, "{}+{}", s, o),
X86ExprValue::SymbolDiff(a, b) => write!(f, "{}-{}", a, b),
X86ExprValue::Label(l) => write!(f, "{}", l),
X86ExprValue::Unresolved(e) => write!(f, "?{}?", e),
}
}
}
pub fn generate_optimal_nops(size: usize) -> Vec<u8> {
match size {
0 => Vec::new(),
1 => vec![0x90],
2 => vec![0x66, 0x90],
3 => vec![0x0F, 0x1F, 0x00],
4 => vec![0x0F, 0x1F, 0x40, 0x00],
5 => vec![0x0F, 0x1F, 0x44, 0x00, 0x00],
6 => vec![0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00],
7 => vec![0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00],
8 => vec![0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00],
9 => vec![
0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
],
10 => vec![
0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
],
11 => vec![
0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
],
12 => vec![
0x66, 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
],
13 => vec![
0x66, 0x66, 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
],
14 => vec![
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
],
15 => vec![
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00,
0x00,
],
_ => {
let mut out = Vec::with_capacity(size);
let mut remaining = size;
while remaining > 0 {
let chunk = remaining.min(15);
out.extend_from_slice(&generate_optimal_nops(chunk));
remaining -= chunk;
}
out
}
}
}
pub fn generate_x86_nop(size: usize) -> Vec<u8> {
generate_optimal_nops(size.min(X86_MAX_NOP_SIZE))
}
fn push_u16_le(buf: &mut Vec<u8>, v: u16) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_u32_le(buf: &mut Vec<u8>, v: u32) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_u64_le(buf: &mut Vec<u8>, v: u64) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_i32_le(buf: &mut Vec<u8>, v: i32) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_i64_le(buf: &mut Vec<u8>, v: i64) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn align_to(offset: usize, alignment: usize) -> usize {
(offset + alignment - 1) & !(alignment - 1)
}
fn align_to_u64(offset: u64, alignment: u64) -> u64 {
(offset + alignment - 1) & !(alignment - 1)
}
pub trait X86MCStreamerTrait {
fn switch_section(&mut self, name: &str);
fn emit_instruction(&mut self, bytes: &[u8], mnemonic: &str);
fn emit_data(&mut self, bytes: &[u8]);
fn emit_alignment(&mut self, alignment: u32, fill_byte: u8, max_bytes: u32);
fn emit_fill(&mut self, value: &[u8], size: u64);
fn emit_org(&mut self, address: u64, fill_byte: u8);
fn emit_label(&mut self, name: &str);
fn emit_symbol(&mut self, name: &str, binding: X86SymbolBinding, sym_type: X86SymbolType);
fn emit_comment(&mut self, text: &str);
fn emit_blank_line(&mut self);
fn emit_file(&mut self, filename: &str);
fn emit_loc(&mut self, file: u32, line: u32, column: u32);
fn emit_cfi_advance_loc(&mut self, delta: u64);
fn emit_cfi_offset(&mut self, reg: u32, offset: i32);
fn emit_cfi_def_cfa(&mut self, reg: u32, offset: i32);
fn emit_cfi_def_cfa_register(&mut self, reg: u32);
fn emit_cfi_def_cfa_offset(&mut self, offset: i32);
fn emit_cfi_remember_state(&mut self);
fn emit_cfi_restore_state(&mut self);
fn emit_cfi_restore(&mut self, reg: u32);
fn emit_cfi_undefined(&mut self, reg: u32);
fn emit_cfi_register(&mut self, reg1: u32, reg2: u32);
fn emit_cfi_window_save(&mut self);
fn emit_fixup(&mut self, fixup: X86Fixup);
fn emit_fragment(&mut self, fragment: X86MCFragment);
fn finish(&mut self);
}
pub struct X86MCAsmStreamer {
pub output: String,
pub current_section: String,
indent: usize,
current_offset: usize,
labels: HashMap<String, usize>,
pub instruction_count: usize,
pub data_count: usize,
}
impl X86MCAsmStreamer {
pub fn new() -> Self {
let mut s = Self {
output: String::new(),
current_section: ".text".to_string(),
indent: 0,
current_offset: 0,
labels: HashMap::new(),
instruction_count: 0,
data_count: 0,
};
s.output.push_str("\t.text\n");
s
}
pub fn as_str(&self) -> &str {
&self.output
}
pub fn into_string(self) -> String {
self.output
}
pub fn clear(&mut self) {
self.output.clear();
self.current_section = ".text".to_string();
self.indent = 0;
self.current_offset = 0;
self.labels.clear();
self.instruction_count = 0;
self.data_count = 0;
self.output.push_str("\t.text\n");
}
fn write_line(&mut self, line: &str) {
for _ in 0..self.indent {
self.output.push('\t');
}
self.output.push_str(line);
self.output.push('\n');
}
}
impl X86MCStreamerTrait for X86MCAsmStreamer {
fn switch_section(&mut self, name: &str) {
if self.current_section != name {
self.write_line(&format!(".section {}", name));
self.current_section = name.to_string();
}
}
fn emit_instruction(&mut self, bytes: &[u8], mnemonic: &str) {
let hex_str: String = bytes.iter().map(|b| format!("{:02x}", b)).collect::<Vec<_>>().join(" ");
self.write_line(&format!("{} # {}", mnemonic, hex_str));
self.current_offset += bytes.len();
self.instruction_count += 1;
}
fn emit_data(&mut self, bytes: &[u8]) {
for chunk in bytes.chunks(8) {
let vals: Vec<String> = chunk.iter().map(|b| format!("{}", b)).collect();
self.write_line(&format!(".byte {}", vals.join(", ")));
}
self.current_offset += bytes.len();
self.data_count += 1;
}
fn emit_alignment(&mut self, alignment: u32, fill_byte: u8, max_bytes: u32) {
self.write_line(&format!(
".p2align {} , {}, {}",
alignment.trailing_zeros(),
fill_byte,
max_bytes
));
}
fn emit_fill(&mut self, value: &[u8], size: u64) {
let val_str: Vec<String> = value.iter().map(|b| format!("{}", b)).collect();
self.write_line(&format!(".fill {}, {}, {}", size, val_str.len(), val_str.join(",")));
}
fn emit_org(&mut self, address: u64, fill_byte: u8) {
self.write_line(&format!(".org {} , {}", address, fill_byte));
}
fn emit_label(&mut self, name: &str) {
self.labels.insert(name.to_string(), self.current_offset);
self.write_line(&format!("{}:", name));
}
fn emit_symbol(&mut self, name: &str, binding: X86SymbolBinding, sym_type: X86SymbolType) {
match binding {
X86SymbolBinding::Global => self.write_line(&format!(".globl {}", name)),
X86SymbolBinding::Weak => self.write_line(&format!(".weak {}", name)),
X86SymbolBinding::Local => self.write_line(&format!(".local {}", name)),
}
if sym_type != X86SymbolType::NoType {
self.write_line(&format!(".type {}, @{}", name, sym_type.as_str().to_lowercase()));
}
}
fn emit_comment(&mut self, text: &str) {
if text.is_empty() {
self.output.push('\n');
} else {
self.write_line(&format!("# {}", text));
}
}
fn emit_blank_line(&mut self) {
self.output.push('\n');
}
fn emit_file(&mut self, filename: &str) {
self.write_line(&format!(".file \"{}\"", filename));
}
fn emit_loc(&mut self, file: u32, line: u32, column: u32) {
self.write_line(&format!(".loc {} {} {}", file, line, column));
}
fn emit_cfi_advance_loc(&mut self, delta: u64) {
self.write_line(&format!(".cfi_advance_loc {}", delta));
}
fn emit_cfi_offset(&mut self, reg: u32, offset: i32) {
self.write_line(&format!(".cfi_offset {}, {}", reg, offset));
}
fn emit_cfi_def_cfa(&mut self, reg: u32, offset: i32) {
self.write_line(&format!(".cfi_def_cfa {}, {}", reg, offset));
}
fn emit_cfi_def_cfa_register(&mut self, reg: u32) {
self.write_line(&format!(".cfi_def_cfa_register {}", reg));
}
fn emit_cfi_def_cfa_offset(&mut self, offset: i32) {
self.write_line(&format!(".cfi_def_cfa_offset {}", offset));
}
fn emit_cfi_remember_state(&mut self) {
self.write_line(".cfi_remember_state");
}
fn emit_cfi_restore_state(&mut self) {
self.write_line(".cfi_restore_state");
}
fn emit_cfi_restore(&mut self, reg: u32) {
self.write_line(&format!(".cfi_restore {}", reg));
}
fn emit_cfi_undefined(&mut self, reg: u32) {
self.write_line(&format!(".cfi_undefined {}", reg));
}
fn emit_cfi_register(&mut self, reg1: u32, reg2: u32) {
self.write_line(&format!(".cfi_register {}, {}", reg1, reg2));
}
fn emit_cfi_window_save(&mut self) {
self.write_line(".cfi_window_save");
}
fn emit_fixup(&mut self, fixup: X86Fixup) {
self.write_line(&format!(
"# fixup: {:?} offset={} kind={}",
fixup.source, fixup.offset, fixup.kind
));
}
fn emit_fragment(&mut self, fragment: X86MCFragment) {
match &fragment {
X86MCFragment::Data(f) => {
self.write_line(&format!("# data fragment: {} bytes", f.data.len()));
}
X86MCFragment::Instruction(f) => {
self.write_line(&format!(
"# instruction fragment: {} bytes",
f.bytes.len()
));
}
X86MCFragment::Align(f) => {
self.write_line(&format!("# align: {} (max: {})", f.alignment, f.max_bytes));
}
_ => {
self.write_line(&format!("# fragment: {}", fragment.kind_name()));
}
}
}
fn finish(&mut self) {
}
}
impl Default for X86MCAsmStreamer {
fn default() -> Self {
Self::new()
}
}
pub struct X86MCObjectStreamer {
pub section_data: Vec<u8>,
pub current_section: String,
pub sections: Vec<(String, Vec<u8>, X86SectionType)>,
pub fixups: Vec<(usize, X86Fixup)>,
pub all_fixups: Vec<(String, usize, X86Fixup)>,
pub symbols: Vec<X86MCSymbol>,
pub labels: HashMap<String, (String, usize)>,
pub offset: usize,
pub alignment: u32,
section_stack: Vec<(String, Vec<u8>, usize)>,
pub gen_dwarf: bool,
pub use_cfi: bool,
pub format: X86ObjectFormat,
temp_label_counter: u64,
}
impl X86MCObjectStreamer {
pub fn new(format: X86ObjectFormat) -> Self {
Self {
section_data: Vec::new(),
current_section: format.text_section().to_string(),
sections: Vec::new(),
fixups: Vec::new(),
all_fixups: Vec::new(),
symbols: Vec::new(),
labels: HashMap::new(),
offset: 0,
alignment: 16,
section_stack: Vec::new(),
gen_dwarf: false,
use_cfi: false,
format,
temp_label_counter: 0,
}
}
pub fn new_temp_label(&mut self) -> String {
self.temp_label_counter += 1;
format!(".Ltmp{}", self.temp_label_counter)
}
pub fn push_section(&mut self) {
let state = (
self.current_section.clone(),
self.section_data.clone(),
self.offset,
);
self.section_stack.push(state);
}
pub fn pop_section(&mut self) {
if let Some((name, data, offset)) = self.section_stack.pop() {
self.current_section = name;
self.section_data = data;
self.offset = offset;
}
}
fn finalize_current_section(&mut self) {
if !self.section_data.is_empty() || self.offset > 0 {
let data = mem::take(&mut self.section_data);
let name = self.current_section.clone();
let sec_type = if name.contains(".text") {
X86SectionType::Regular
} else if name.contains(".bss") {
X86SectionType::Bss
} else if name.contains(".debug") {
X86SectionType::Debug
} else if name.contains("rodata") || name.contains("__const") {
X86SectionType::ReadOnly
} else {
X86SectionType::Regular
};
self.sections.push((name, data, sec_type));
}
}
}
impl X86MCStreamerTrait for X86MCObjectStreamer {
fn switch_section(&mut self, name: &str) {
if self.current_section != name {
self.finalize_current_section();
self.section_data.clear();
self.offset = 0;
self.current_section = name.to_string();
}
}
fn emit_instruction(&mut self, bytes: &[u8], _mnemonic: &str) {
let start = self.offset;
self.section_data.extend_from_slice(bytes);
self.offset += bytes.len();
let pending: Vec<_> = self
.fixups
.iter()
.filter(|(off, _)| *off >= start && *off < start + bytes.len())
.cloned()
.collect();
for (off, fixup) in pending {
self.all_fixups
.push((self.current_section.clone(), off, fixup));
}
self.fixups.retain(|(off, _)| *off < start || *off >= start + bytes.len());
}
fn emit_data(&mut self, bytes: &[u8]) {
self.section_data.extend_from_slice(bytes);
self.offset += bytes.len();
}
fn emit_alignment(&mut self, alignment: u32, fill_byte: u8, _max_bytes: u32) {
let align = alignment as usize;
let misalignment = self.offset & (align - 1);
if misalignment != 0 {
let padding = align - misalignment;
let nops = generate_optimal_nops(padding);
self.section_data.extend_from_slice(&nops);
self.offset += nops.len();
}
}
fn emit_fill(&mut self, value: &[u8], size: u64) {
for _ in 0..size {
self.section_data.extend_from_slice(value);
}
self.offset += (size as usize) * value.len();
}
fn emit_org(&mut self, address: u64, fill_byte: u8) {
let addr = address as usize;
if addr > self.offset {
let padding = addr - self.offset;
self.section_data.extend(vec![fill_byte; padding]);
self.offset = addr;
}
}
fn emit_label(&mut self, name: &str) {
self.labels.insert(
name.to_string(),
(self.current_section.clone(), self.offset),
);
}
fn emit_symbol(&mut self, name: &str, binding: X86SymbolBinding, sym_type: X86SymbolType) {
let sym = X86MCSymbol::new_defined(name, 0, self.offset as u64)
.with_binding(binding)
.with_type(sym_type);
self.symbols.push(sym);
}
fn emit_comment(&mut self, _text: &str) {
}
fn emit_blank_line(&mut self) {
}
fn emit_file(&mut self, _filename: &str) {
}
fn emit_loc(&mut self, _file: u32, _line: u32, _column: u32) {
}
fn emit_cfi_advance_loc(&mut self, _delta: u64) {}
fn emit_cfi_offset(&mut self, _reg: u32, _offset: i32) {}
fn emit_cfi_def_cfa(&mut self, _reg: u32, _offset: i32) {}
fn emit_cfi_def_cfa_register(&mut self, _reg: u32) {}
fn emit_cfi_def_cfa_offset(&mut self, _offset: i32) {}
fn emit_cfi_remember_state(&mut self) {}
fn emit_cfi_restore_state(&mut self) {}
fn emit_cfi_restore(&mut self, _reg: u32) {}
fn emit_cfi_undefined(&mut self, _reg: u32) {}
fn emit_cfi_register(&mut self, _reg1: u32, _reg2: u32) {}
fn emit_cfi_window_save(&mut self) {}
fn emit_fixup(&mut self, fixup: X86Fixup) {
self.fixups.push((self.offset, fixup));
}
fn emit_fragment(&mut self, fragment: X86MCFragment) {
match &fragment {
X86MCFragment::Data(f) => {
self.section_data.extend_from_slice(&f.data);
self.offset += f.data.len();
}
X86MCFragment::Instruction(f) => {
self.section_data.extend_from_slice(&f.bytes);
self.offset += f.bytes.len();
}
X86MCFragment::Align(f) => {
let padding = f.fill_data.len();
if padding > 0 {
self.section_data.extend_from_slice(&f.fill_data);
self.offset += padding;
}
}
X86MCFragment::Fill(f) => {
for _ in 0..f.size {
self.section_data.extend_from_slice(&f.value);
}
self.offset += (f.size as usize) * f.value.len();
}
X86MCFragment::Org(f) => {
let addr = f.address as usize;
if addr > self.offset {
let padding = addr - self.offset;
self.section_data.extend(vec![f.fill_byte; padding]);
self.offset = addr;
}
}
X86MCFragment::DwarfFrame(f) => {
self.section_data.extend_from_slice(&f.data);
self.offset += f.data.len();
}
X86MCFragment::DwarfLine(f) => {
self.section_data.extend_from_slice(&f.data);
self.offset += f.data.len();
}
X86MCFragment::CompactUnwind(f) => {
self.section_data.extend_from_slice(&f.data);
self.offset += f.data.len();
}
X86MCFragment::CVDefRange(f) => {
self.section_data.extend_from_slice(&f.data);
self.offset += f.data.len();
}
X86MCFragment::Relaxable(f) => {
let bytes = f.get_bytes();
self.section_data.extend_from_slice(&bytes);
self.offset += bytes.len();
}
}
}
fn finish(&mut self) {
self.finalize_current_section();
}
}
impl Default for X86MCObjectStreamer {
fn default() -> Self {
Self::new(X86ObjectFormat::ELF)
}
}
pub struct X86MCNullStreamer {
pub instruction_count: usize,
pub label_count: usize,
pub directive_count: usize,
pub byte_count: usize,
pub fixup_count: usize,
}
impl X86MCNullStreamer {
pub fn new() -> Self {
Self {
instruction_count: 0,
label_count: 0,
directive_count: 0,
byte_count: 0,
fixup_count: 0,
}
}
pub fn reset(&mut self) {
self.instruction_count = 0;
self.label_count = 0;
self.directive_count = 0;
self.byte_count = 0;
self.fixup_count = 0;
}
pub fn total_operations(&self) -> usize {
self.instruction_count + self.label_count + self.directive_count
}
}
impl X86MCStreamerTrait for X86MCNullStreamer {
fn switch_section(&mut self, _name: &str) {}
fn emit_instruction(&mut self, bytes: &[u8], _mnemonic: &str) {
self.instruction_count += 1;
self.byte_count += bytes.len();
}
fn emit_data(&mut self, bytes: &[u8]) {
self.directive_count += 1;
self.byte_count += bytes.len();
}
fn emit_alignment(&mut self, _alignment: u32, _fill_byte: u8, _max_bytes: u32) {
self.directive_count += 1;
}
fn emit_fill(&mut self, value: &[u8], size: u64) {
self.directive_count += 1;
self.byte_count += (size as usize) * value.len();
}
fn emit_org(&mut self, _address: u64, _fill_byte: u8) {
self.directive_count += 1;
}
fn emit_label(&mut self, _name: &str) {
self.label_count += 1;
}
fn emit_symbol(&mut self, _name: &str, _binding: X86SymbolBinding, _sym_type: X86SymbolType) {
self.directive_count += 1;
}
fn emit_comment(&mut self, _text: &str) {}
fn emit_blank_line(&mut self) {}
fn emit_file(&mut self, _filename: &str) {}
fn emit_loc(&mut self, _file: u32, _line: u32, _column: u32) {}
fn emit_cfi_advance_loc(&mut self, _delta: u64) {}
fn emit_cfi_offset(&mut self, _reg: u32, _offset: i32) {}
fn emit_cfi_def_cfa(&mut self, _reg: u32, _offset: i32) {}
fn emit_cfi_def_cfa_register(&mut self, _reg: u32) {}
fn emit_cfi_def_cfa_offset(&mut self, _offset: i32) {}
fn emit_cfi_remember_state(&mut self) {}
fn emit_cfi_restore_state(&mut self) {}
fn emit_cfi_restore(&mut self, _reg: u32) {}
fn emit_cfi_undefined(&mut self, _reg: u32) {}
fn emit_cfi_register(&mut self, _reg1: u32, _reg2: u32) {}
fn emit_cfi_window_save(&mut self) {}
fn emit_fixup(&mut self, _fixup: X86Fixup) {
self.fixup_count += 1;
}
fn emit_fragment(&mut self, fragment: X86MCFragment) {
self.byte_count += fragment.size();
}
fn finish(&mut self) {}
}
impl Default for X86MCNullStreamer {
fn default() -> Self {
Self::new()
}
}
pub struct X86MCDwarfStreamer {
pub emit_abbrev: bool,
pub emit_info: bool,
pub emit_line: bool,
pub emit_frame: bool,
pub emit_aranges: bool,
pub emit_str: bool,
pub emit_loc: bool,
pub emit_ranges: bool,
pub debug_info: Vec<u8>,
pub debug_abbrev: Vec<u8>,
pub debug_line: Vec<u8>,
pub debug_frame: Vec<u8>,
pub debug_aranges: Vec<u8>,
pub debug_str: Vec<u8>,
pub debug_loc: Vec<u8>,
pub debug_ranges: Vec<u8>,
string_table: BTreeMap<String, usize>,
debug_str_offset: usize,
current_cu_offset: usize,
cu_count: usize,
}
impl X86MCDwarfStreamer {
pub fn new() -> Self {
Self {
emit_abbrev: true,
emit_info: true,
emit_line: true,
emit_frame: true,
emit_aranges: true,
emit_str: true,
emit_loc: true,
emit_ranges: true,
debug_info: Vec::new(),
debug_abbrev: Vec::new(),
debug_line: Vec::new(),
debug_frame: Vec::new(),
debug_aranges: Vec::new(),
debug_str: Vec::new(),
debug_loc: Vec::new(),
debug_ranges: Vec::new(),
string_table: BTreeMap::new(),
debug_str_offset: 0,
current_cu_offset: 0,
cu_count: 0,
}
}
pub fn add_debug_string(&mut self, s: &str) -> usize {
if let Some(&offset) = self.string_table.get(s) {
return offset;
}
let offset = self.debug_str_offset;
self.debug_str.extend_from_slice(s.as_bytes());
self.debug_str.push(0);
self.debug_str_offset += s.len() + 1;
self.string_table.insert(s.to_string(), offset);
offset
}
pub fn begin_cu(&mut self, cu_length: u32, version: u16) {
self.current_cu_offset = self.debug_info.len();
self.cu_count += 1;
push_u32_le(&mut self.debug_info, cu_length); push_u16_le(&mut self.debug_info, version); push_u32_le(&mut self.debug_info, 0); self.debug_info.push(8); }
pub fn emit_dwarf_abbrev(&mut self, code: u64, tag: u64, children: u8) {
write_uleb128(&mut self.debug_abbrev, code);
write_uleb128(&mut self.debug_abbrev, tag);
self.debug_abbrev.push(children);
}
pub fn emit_dwarf_attr(&mut self, attr: u64, form: u64) {
write_uleb128(&mut self.debug_abbrev, attr);
write_uleb128(&mut self.debug_abbrev, form);
}
pub fn end_abbrev(&mut self) {
write_uleb128(&mut self.debug_abbrev, 0);
write_uleb128(&mut self.debug_abbrev, 0);
}
pub fn emit_line_header(&mut self, unit_length: u32, version: u16) {
push_u32_le(&mut self.debug_line, unit_length);
push_u16_le(&mut self.debug_line, version);
}
pub fn emit_cie(&mut self, length: u32, version: u8, augmentation: &str) {
push_u32_le(&mut self.debug_frame, length);
push_u32_le(&mut self.debug_frame, 0xFFFF_FFFF); self.debug_frame.push(version);
self.debug_frame.extend_from_slice(augmentation.as_bytes());
self.debug_frame.push(0); }
pub fn emit_fde(&mut self, length: u32, cie_pointer: u32, initial_loc: u64, range: u64) {
push_u32_le(&mut self.debug_frame, length);
push_u32_le(&mut self.debug_frame, cie_pointer);
push_u64_le(&mut self.debug_frame, initial_loc);
push_u64_le(&mut self.debug_frame, range);
}
pub fn emit_arange(&mut self, start: u64, length: u64) {
push_u64_le(&mut self.debug_aranges, start);
push_u64_le(&mut self.debug_aranges, length);
}
pub fn end_aranges(&mut self) {
push_u64_le(&mut self.debug_aranges, 0);
push_u64_le(&mut self.debug_aranges, 0);
}
fn write_uleb128(buf: &mut Vec<u8>, mut value: u64) {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
buf.push(byte);
if value == 0 {
break;
}
}
}
pub fn to_sections(&self) -> Vec<(String, Vec<u8>)> {
let mut sections = Vec::new();
if self.emit_info && !self.debug_info.is_empty() {
sections.push((".debug_info".to_string(), self.debug_info.clone()));
}
if self.emit_abbrev && !self.debug_abbrev.is_empty() {
sections.push((".debug_abbrev".to_string(), self.debug_abbrev.clone()));
}
if self.emit_line && !self.debug_line.is_empty() {
sections.push((".debug_line".to_string(), self.debug_line.clone()));
}
if self.emit_frame && !self.debug_frame.is_empty() {
sections.push((".debug_frame".to_string(), self.debug_frame.clone()));
}
if self.emit_aranges && !self.debug_aranges.is_empty() {
sections.push((".debug_aranges".to_string(), self.debug_aranges.clone()));
}
if self.emit_str && !self.debug_str.is_empty() {
sections.push((".debug_str".to_string(), self.debug_str.clone()));
}
if self.emit_loc && !self.debug_loc.is_empty() {
sections.push((".debug_loc".to_string(), self.debug_loc.clone()));
}
if self.emit_ranges && !self.debug_ranges.is_empty() {
sections.push((".debug_ranges".to_string(), self.debug_ranges.clone()));
}
sections
}
}
impl X86MCStreamerTrait for X86MCDwarfStreamer {
fn switch_section(&mut self, _name: &str) {}
fn emit_instruction(&mut self, _bytes: &[u8], _mnemonic: &str) {}
fn emit_data(&mut self, _bytes: &[u8]) {}
fn emit_alignment(&mut self, _alignment: u32, _fill_byte: u8, _max_bytes: u32) {}
fn emit_fill(&mut self, _value: &[u8], _size: u64) {}
fn emit_org(&mut self, _address: u64, _fill_byte: u8) {}
fn emit_label(&mut self, _name: &str) {}
fn emit_symbol(&mut self, _name: &str, _binding: X86SymbolBinding, _sym_type: X86SymbolType) {}
fn emit_comment(&mut self, _text: &str) {}
fn emit_blank_line(&mut self) {}
fn emit_file(&mut self, _filename: &str) {}
fn emit_loc(&mut self, _file: u32, _line: u32, _column: u32) {}
fn emit_cfi_advance_loc(&mut self, _delta: u64) {}
fn emit_cfi_offset(&mut self, _reg: u32, _offset: i32) {}
fn emit_cfi_def_cfa(&mut self, _reg: u32, _offset: i32) {}
fn emit_cfi_def_cfa_register(&mut self, _reg: u32) {}
fn emit_cfi_def_cfa_offset(&mut self, _offset: i32) {}
fn emit_cfi_remember_state(&mut self) {}
fn emit_cfi_restore_state(&mut self) {}
fn emit_cfi_restore(&mut self, _reg: u32) {}
fn emit_cfi_undefined(&mut self, _reg: u32) {}
fn emit_cfi_register(&mut self, _reg1: u32, _reg2: u32) {}
fn emit_cfi_window_save(&mut self) {}
fn emit_fixup(&mut self, _fixup: X86Fixup) {}
fn emit_fragment(&mut self, _fragment: X86MCFragment) {}
fn finish(&mut self) {}
}
impl Default for X86MCDwarfStreamer {
fn default() -> Self {
Self::new()
}
}
fn write_uleb128(buf: &mut Vec<u8>, mut value: u64) {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
buf.push(byte);
if value == 0 {
break;
}
}
}
#[derive(Debug, Clone)]
pub enum X86MCStreamerAction {
SwitchSection(String),
EmitInstruction(Vec<u8>, String),
EmitData(Vec<u8>),
EmitAlignment(u32, u8, u32),
EmitFill(Vec<u8>, u64),
EmitOrg(u64, u8),
EmitLabel(String),
EmitSymbol(String, X86SymbolBinding, X86SymbolType),
EmitComment(String),
EmitBlankLine,
EmitFile(String),
EmitLoc(u32, u32, u32),
EmitCfiAdvanceLoc(u64),
EmitCfiOffset(u32, i32),
EmitCfiDefCfa(u32, i32),
EmitCfiDefCfaRegister(u32),
EmitCfiDefCfaOffset(i32),
EmitCfiRememberState,
EmitCfiRestoreState,
EmitCfiRestore(u32),
EmitCfiUndefined(u32),
EmitCfiRegister(u32, u32),
EmitCfiWindowSave,
EmitFixup(X86Fixup),
EmitFragment(X86MCFragment),
}
pub struct X86MCRecordStreamer {
pub actions: Vec<X86MCStreamerAction>,
inner: Option<Box<dyn X86MCStreamerTrait>>,
paused: bool,
}
impl X86MCRecordStreamer {
pub fn new() -> Self {
Self {
actions: Vec::new(),
inner: None,
paused: false,
}
}
pub fn with_inner(inner: Box<dyn X86MCStreamerTrait>) -> Self {
Self {
actions: Vec::new(),
inner: Some(inner),
paused: false,
}
}
pub fn pause(&mut self) {
self.paused = true;
}
pub fn resume(&mut self) {
self.paused = false;
}
pub fn clear(&mut self) {
self.actions.clear();
}
pub fn len(&self) -> usize {
self.actions.len()
}
pub fn is_empty(&self) -> bool {
self.actions.is_empty()
}
pub fn replay(&mut self) -> Result<(), String> {
if let Some(ref mut inner) = self.inner {
for action in &self.actions {
match action {
X86MCStreamerAction::SwitchSection(name) => inner.switch_section(name),
X86MCStreamerAction::EmitInstruction(bytes, mnemonic) => {
inner.emit_instruction(bytes, mnemonic)
}
X86MCStreamerAction::EmitData(bytes) => inner.emit_data(bytes),
X86MCStreamerAction::EmitAlignment(align, fill, max) => {
inner.emit_alignment(*align, *fill, *max)
}
X86MCStreamerAction::EmitFill(value, size) => inner.emit_fill(value, *size),
X86MCStreamerAction::EmitOrg(addr, fill) => inner.emit_org(*addr, *fill),
X86MCStreamerAction::EmitLabel(name) => inner.emit_label(name),
X86MCStreamerAction::EmitSymbol(name, binding, typ) => {
inner.emit_symbol(name, *binding, *typ)
}
X86MCStreamerAction::EmitComment(text) => inner.emit_comment(text),
X86MCStreamerAction::EmitBlankLine => inner.emit_blank_line(),
X86MCStreamerAction::EmitFile(name) => inner.emit_file(name),
X86MCStreamerAction::EmitLoc(file, line, col) => {
inner.emit_loc(*file, *line, *col)
}
X86MCStreamerAction::EmitCfiAdvanceLoc(delta) => {
inner.emit_cfi_advance_loc(*delta)
}
X86MCStreamerAction::EmitCfiOffset(reg, offset) => {
inner.emit_cfi_offset(*reg, *offset)
}
X86MCStreamerAction::EmitCfiDefCfa(reg, offset) => {
inner.emit_cfi_def_cfa(*reg, *offset)
}
X86MCStreamerAction::EmitCfiDefCfaRegister(reg) => {
inner.emit_cfi_def_cfa_register(*reg)
}
X86MCStreamerAction::EmitCfiDefCfaOffset(offset) => {
inner.emit_cfi_def_cfa_offset(*offset)
}
X86MCStreamerAction::EmitCfiRememberState => inner.emit_cfi_remember_state(),
X86MCStreamerAction::EmitCfiRestoreState => inner.emit_cfi_restore_state(),
X86MCStreamerAction::EmitCfiRestore(reg) => inner.emit_cfi_restore(*reg),
X86MCStreamerAction::EmitCfiUndefined(reg) => inner.emit_cfi_undefined(*reg),
X86MCStreamerAction::EmitCfiRegister(r1, r2) => {
inner.emit_cfi_register(*r1, *r2)
}
X86MCStreamerAction::EmitCfiWindowSave => inner.emit_cfi_window_save(),
X86MCStreamerAction::EmitFixup(fixup) => inner.emit_fixup(fixup.clone()),
X86MCStreamerAction::EmitFragment(frag) => {
inner.emit_fragment(frag.clone())
}
}
}
}
Ok(())
}
}
impl X86MCStreamerTrait for X86MCRecordStreamer {
fn switch_section(&mut self, name: &str) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::SwitchSection(name.to_string()));
}
if let Some(ref mut inner) = self.inner {
inner.switch_section(name);
}
}
fn emit_instruction(&mut self, bytes: &[u8], mnemonic: &str) {
if !self.paused {
self.actions.push(X86MCStreamerAction::EmitInstruction(
bytes.to_vec(),
mnemonic.to_string(),
));
}
if let Some(ref mut inner) = self.inner {
inner.emit_instruction(bytes, mnemonic);
}
}
fn emit_data(&mut self, bytes: &[u8]) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitData(bytes.to_vec()));
}
if let Some(ref mut inner) = self.inner {
inner.emit_data(bytes);
}
}
fn emit_alignment(&mut self, alignment: u32, fill_byte: u8, max_bytes: u32) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitAlignment(alignment, fill_byte, max_bytes));
}
if let Some(ref mut inner) = self.inner {
inner.emit_alignment(alignment, fill_byte, max_bytes);
}
}
fn emit_fill(&mut self, value: &[u8], size: u64) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitFill(value.to_vec(), size));
}
if let Some(ref mut inner) = self.inner {
inner.emit_fill(value, size);
}
}
fn emit_org(&mut self, address: u64, fill_byte: u8) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitOrg(address, fill_byte));
}
if let Some(ref mut inner) = self.inner {
inner.emit_org(address, fill_byte);
}
}
fn emit_label(&mut self, name: &str) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitLabel(name.to_string()));
}
if let Some(ref mut inner) = self.inner {
inner.emit_label(name);
}
}
fn emit_symbol(&mut self, name: &str, binding: X86SymbolBinding, sym_type: X86SymbolType) {
if !self.paused {
self.actions.push(X86MCStreamerAction::EmitSymbol(
name.to_string(),
binding,
sym_type,
));
}
if let Some(ref mut inner) = self.inner {
inner.emit_symbol(name, binding, sym_type);
}
}
fn emit_comment(&mut self, text: &str) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitComment(text.to_string()));
}
if let Some(ref mut inner) = self.inner {
inner.emit_comment(text);
}
}
fn emit_blank_line(&mut self) {
if !self.paused {
self.actions.push(X86MCStreamerAction::EmitBlankLine);
}
if let Some(ref mut inner) = self.inner {
inner.emit_blank_line();
}
}
fn emit_file(&mut self, filename: &str) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitFile(filename.to_string()));
}
if let Some(ref mut inner) = self.inner {
inner.emit_file(filename);
}
}
fn emit_loc(&mut self, file: u32, line: u32, column: u32) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitLoc(file, line, column));
}
if let Some(ref mut inner) = self.inner {
inner.emit_loc(file, line, column);
}
}
fn emit_cfi_advance_loc(&mut self, delta: u64) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitCfiAdvanceLoc(delta));
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_advance_loc(delta);
}
}
fn emit_cfi_offset(&mut self, reg: u32, offset: i32) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitCfiOffset(reg, offset));
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_offset(reg, offset);
}
}
fn emit_cfi_def_cfa(&mut self, reg: u32, offset: i32) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitCfiDefCfa(reg, offset));
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_def_cfa(reg, offset);
}
}
fn emit_cfi_def_cfa_register(&mut self, reg: u32) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitCfiDefCfaRegister(reg));
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_def_cfa_register(reg);
}
}
fn emit_cfi_def_cfa_offset(&mut self, offset: i32) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitCfiDefCfaOffset(offset));
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_def_cfa_offset(offset);
}
}
fn emit_cfi_remember_state(&mut self) {
if !self.paused {
self.actions.push(X86MCStreamerAction::EmitCfiRememberState);
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_remember_state();
}
}
fn emit_cfi_restore_state(&mut self) {
if !self.paused {
self.actions.push(X86MCStreamerAction::EmitCfiRestoreState);
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_restore_state();
}
}
fn emit_cfi_restore(&mut self, reg: u32) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitCfiRestore(reg));
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_restore(reg);
}
}
fn emit_cfi_undefined(&mut self, reg: u32) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitCfiUndefined(reg));
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_undefined(reg);
}
}
fn emit_cfi_register(&mut self, reg1: u32, reg2: u32) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitCfiRegister(reg1, reg2));
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_register(reg1, reg2);
}
}
fn emit_cfi_window_save(&mut self) {
if !self.paused {
self.actions.push(X86MCStreamerAction::EmitCfiWindowSave);
}
if let Some(ref mut inner) = self.inner {
inner.emit_cfi_window_save();
}
}
fn emit_fixup(&mut self, fixup: X86Fixup) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitFixup(fixup.clone()));
}
if let Some(ref mut inner) = self.inner {
inner.emit_fixup(fixup);
}
}
fn emit_fragment(&mut self, fragment: X86MCFragment) {
if !self.paused {
self.actions
.push(X86MCStreamerAction::EmitFragment(fragment.clone()));
}
if let Some(ref mut inner) = self.inner {
inner.emit_fragment(fragment);
}
}
fn finish(&mut self) {
if let Some(ref mut inner) = self.inner {
inner.finish();
}
}
}
impl Default for X86MCRecordStreamer {
fn default() -> Self {
Self::new()
}
}
pub struct X86MCAssembler {
pub sections: Vec<X86MCSection>,
pub symbols: Vec<X86MCSymbol>,
pub labels: HashMap<String, X86MCLabel>,
pub pending_fixups: Vec<(String, X86Fixup)>,
pub current_section: String,
pub current_offset: usize,
pub target_triple: String,
pub format: X86ObjectFormat,
pub pic: bool,
pub relaxation: bool,
pub features: HashSet<String>,
temp_counter: u64,
next_symbol_index: u32,
}
impl X86MCAssembler {
pub fn new(triple: &str) -> Self {
let format = X86ObjectFormat::from_triple(triple);
let mut s = Self {
sections: Vec::new(),
symbols: Vec::new(),
labels: HashMap::new(),
pending_fixups: Vec::new(),
current_section: format.text_section().to_string(),
current_offset: 0,
target_triple: triple.to_string(),
format,
pic: false,
relaxation: true,
features: HashSet::new(),
temp_counter: 0,
next_symbol_index: 0,
};
s.get_or_create_section(format.text_section(), X86SectionType::Regular, X86SectionFlags::text());
s.get_or_create_section(format.data_section(), X86SectionType::Regular, X86SectionFlags::data());
s.get_or_create_section(format.rodata_section(), X86SectionType::ReadOnly, X86SectionFlags::rodata());
s.get_or_create_section(format.bss_section(), X86SectionType::Bss, X86SectionFlags::bss());
s
}
pub fn new_temp_symbol(&mut self) -> String {
self.temp_counter += 1;
format!(".Ltmp{}", self.temp_counter)
}
pub fn new_temp_label(&mut self) -> String {
self.temp_counter += 1;
format!(".L{}", self.temp_counter)
}
pub fn get_or_create_section(
&mut self,
name: &str,
sec_type: X86SectionType,
flags: X86SectionFlags,
) -> usize {
if let Some(idx) = self.sections.iter().position(|s| s.name == name) {
return idx;
}
self.sections
.push(X86MCSection::new(name, sec_type, flags));
self.sections.len() - 1
}
pub fn find_section(&self, name: &str) -> Option<usize> {
self.sections.iter().position(|s| s.name == name)
}
pub fn switch_section(&mut self, name: &str) {
self.current_section = name.to_string();
self.current_offset = self.section_size(name);
}
pub fn push_section(&mut self) {
}
pub fn pop_section(&mut self) {
}
pub fn section_size(&self, name: &str) -> usize {
if let Some(idx) = self.find_section(name) {
self.sections[idx].fragment_size()
} else {
0
}
}
pub fn emit_instruction(&mut self, bytes: &[u8], mnemonic: &str) {
let sec_idx = self.get_or_create_section(
&self.current_section,
X86SectionType::Regular,
X86SectionFlags::text(),
);
let frag = MCInstFragment::new(bytes.to_vec()).with_mnemonic(mnemonic);
self.sections[sec_idx].add_fragment(X86MCFragment::Instruction(frag));
self.current_offset += bytes.len();
}
pub fn emit_data(&mut self, bytes: &[u8]) {
let sec_idx = self.get_or_create_section(
&self.current_section,
X86SectionType::Regular,
X86SectionFlags::data(),
);
let frag = MCDataFragment::new(bytes.to_vec());
self.sections[sec_idx].add_fragment(X86MCFragment::Data(frag));
self.current_offset += bytes.len();
}
pub fn emit_alignment(&mut self, alignment: u32, fill_byte: u8, max_bytes: u32) {
let sec_idx = self.get_or_create_section(
&self.current_section,
X86SectionType::Regular,
X86SectionFlags::text(),
);
let mut align_frag = MCAlignFragment::new(alignment, fill_byte, max_bytes);
align_frag.compute_fill(self.current_offset);
let padding = align_frag.fill_data.len();
self.sections[sec_idx].add_fragment(X86MCFragment::Align(align_frag));
self.current_offset += padding;
}
pub fn emit_fill(&mut self, value: &[u8], size: u64) {
let sec_idx = self.get_or_create_section(
&self.current_section,
X86SectionType::Regular,
X86SectionFlags::data(),
);
let frag = MCFillFragment::new(value.to_vec(), size);
let total = frag.total_size() as usize;
self.sections[sec_idx].add_fragment(X86MCFragment::Fill(frag));
self.current_offset += total;
}
pub fn emit_org(&mut self, address: u64, fill_byte: u8) {
let sec_idx = self.get_or_create_section(
&self.current_section,
X86SectionType::Regular,
X86SectionFlags::text(),
);
let frag = MCOrgFragment::new(address, fill_byte);
self.sections[sec_idx].add_fragment(X86MCFragment::Org(frag));
self.current_offset = address as usize;
}
pub fn define_symbol(&mut self, name: &str, binding: X86SymbolBinding, sym_type: X86SymbolType) {
let sec_idx = self
.find_section(&self.current_section)
.unwrap_or(0) as u32;
let sym = X86MCSymbol::new_defined(name, sec_idx, self.current_offset as u64)
.with_binding(binding)
.with_type(sym_type);
self.symbols.push(sym);
}
pub fn define_absolute_symbol(&mut self, name: &str, value: u64) {
let sym = X86MCSymbol::new_absolute(name, value);
self.symbols.push(sym);
}
pub fn define_temp_symbol(&mut self, name: &str) {
let sec_idx = self
.find_section(&self.current_section)
.unwrap_or(0) as u32;
let mut sym = X86MCSymbol::new_defined(name, sec_idx, self.current_offset as u64);
sym.temporary = true;
self.symbols.push(sym);
}
pub fn define_label(&mut self, name: &str) {
let sec_idx = self.find_section(&self.current_section).unwrap_or(0);
let frag_idx = self.sections[sec_idx].fragments.len();
let offset = self.current_offset;
let mut label = X86MCLabel::new(name);
label.define(sec_idx, frag_idx, offset);
self.labels.insert(name.to_string(), label);
}
pub fn resolve_label(&self, name: &str) -> Option<u64> {
self.labels.get(name).and_then(|l| l.resolved_address)
}
pub fn record_fixup(&mut self, fixup: X86Fixup) {
self.pending_fixups
.push((self.current_section.clone(), fixup));
}
pub fn evaluate_expression(&self, expr: &str) -> Option<X86ExprValue> {
let expr = expr.trim();
if let Ok(v) = expr.parse::<i64>() {
return Some(X86ExprValue::Constant(v));
}
if expr.starts_with("0x") || expr.starts_with("0X") {
if let Ok(v) = i64::from_str_radix(&expr[2..], 16) {
return Some(X86ExprValue::Constant(v));
}
}
if let Some(pos) = expr.find(" - ") {
let a = expr[..pos].trim();
let b = expr[pos + 3..].trim();
if !a.is_empty() && !b.is_empty() {
return Some(X86ExprValue::SymbolDiff(a.to_string(), b.to_string()));
}
}
if let Some(pos) = expr.find(" + ") {
let sym = expr[..pos].trim();
let off: i64 = expr[pos + 3..].trim().parse().ok()?;
return Some(X86ExprValue::SymbolOffset(sym.to_string(), off));
}
if !expr.is_empty() {
return Some(X86ExprValue::Symbol(expr.to_string()));
}
None
}
pub fn compute_section_size(&self, sec_idx: usize) -> usize {
if sec_idx >= self.sections.len() {
return 0;
}
self.sections[sec_idx].fragment_size()
}
pub fn flatten_section(&self, sec_idx: usize) -> Vec<u8> {
if sec_idx >= self.sections.len() {
return Vec::new();
}
let mut out = Vec::new();
for frag in &self.sections[sec_idx].fragments {
out.extend_from_slice(&frag.get_bytes());
}
out
}
pub fn flatten_all(&self) -> (Vec<u8>, Vec<(String, usize, usize)>) {
let mut out = Vec::new();
let mut boundaries = Vec::new();
for sec in &self.sections {
let start = out.len();
for frag in &sec.fragments {
out.extend_from_slice(&frag.get_bytes());
}
let end = out.len();
boundaries.push((sec.name.clone(), start, end - start));
}
(out, boundaries)
}
pub fn layout(&mut self) {
for sec in &mut self.sections {
let mut offset = 0u64;
for frag in &mut sec.fragments {
match frag {
X86MCFragment::Align(ref mut a) => {
a.compute_fill(offset as usize);
offset += a.fill_data.len() as u64;
}
X86MCFragment::Org(ref o) => {
if o.address > offset {
offset = o.address;
}
}
_ => {
offset += frag.size() as u64;
}
}
}
sec.size = offset;
}
}
pub fn relax(&mut self, max_iterations: u32) -> bool {
let mut changed = false;
for i in 0..max_iterations {
let iter_changed = self.relax_once();
changed |= iter_changed;
if !iter_changed {
break;
}
self.layout();
if i >= 10 {
break;
}
}
changed
}
fn relax_once(&mut self) -> bool {
let mut changed = false;
let mut addresses: Vec<(usize, usize, u64)> = Vec::new(); for (si, sec) in self.sections.iter().enumerate() {
let mut offset = 0u64;
for (fi, frag) in sec.fragments.iter().enumerate() {
if frag.is_relaxable() {
addresses.push((si, fi, offset));
}
offset += frag.size() as u64;
}
}
for (si, fi, addr) in &addresses {
let frag = &self.sections[*si].fragments[*fi];
if let X86MCFragment::Relaxable(ref r) = frag {
if let Some(ref target) = r.target_label {
if let Some(target_addr) = self.resolve_label(target) {
let distance = target_addr as i64 - *addr as i64;
let fits_short = r.is_within_short_range(distance);
if fits_short && r.current_size > r.min_size {
} else if !fits_short && r.current_size < r.max_size {
changed = true;
}
}
}
}
}
changed
}
pub fn apply_fixups(&mut self) {
self.pending_fixups.clear();
}
pub fn reset(&mut self) {
self.sections.clear();
self.symbols.clear();
self.labels.clear();
self.pending_fixups.clear();
self.current_offset = 0;
self.temp_counter = 0;
let format = self.format;
self.current_section = format.text_section().to_string();
self.get_or_create_section(format.text_section(), X86SectionType::Regular, X86SectionFlags::text());
self.get_or_create_section(format.data_section(), X86SectionType::Regular, X86SectionFlags::data());
self.get_or_create_section(format.rodata_section(), X86SectionType::ReadOnly, X86SectionFlags::rodata());
self.get_or_create_section(format.bss_section(), X86SectionType::Bss, X86SectionFlags::bss());
}
pub fn set_pic(&mut self, pic: bool) {
self.pic = pic;
}
pub fn set_relaxation(&mut self, enable: bool) {
self.relaxation = enable;
}
pub fn add_feature(&mut self, feature: &str) {
self.features.insert(feature.to_string());
}
pub fn has_feature(&self, feature: &str) -> bool {
self.features.contains(feature)
}
}
impl Default for X86MCAssembler {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu")
}
}
pub struct X86ELFObjectWriter {
pub data: Vec<u8>,
pub is_64bit: bool,
pub machine: u16,
}
impl X86ELFObjectWriter {
pub fn new(is_64bit: bool) -> Self {
Self {
data: Vec::new(),
is_64bit,
machine: if is_64bit { EM_X86_64 } else { EM_386 },
}
}
pub fn write_elf_header(
&mut self,
entry: u64,
phoff: u64,
shoff: u64,
flags: u32,
ehsize: u16,
phentsize: u16,
phnum: u16,
shentsize: u16,
shnum: u16,
shstrndx: u16,
) {
self.data.extend_from_slice(&ELF_MAGIC);
self.data.push(ELF_CLASS_64);
self.data.push(ELF_DATA_LSB);
self.data.push(ELF_VERSION_CURRENT);
self.data.push(ELF_OSABI_SYSV);
self.data.extend_from_slice(&[0u8; 8]);
push_u16_le(&mut self.data, ELF_ET_REL);
push_u16_le(&mut self.data, self.machine);
push_u32_le(&mut self.data, ELF_VERSION_CURRENT as u32);
push_u64_le(&mut self.data, entry);
push_u64_le(&mut self.data, phoff);
push_u64_le(&mut self.data, shoff);
push_u32_le(&mut self.data, flags);
push_u16_le(&mut self.data, ehsize);
push_u16_le(&mut self.data, phentsize);
push_u16_le(&mut self.data, phnum);
push_u16_le(&mut self.data, shentsize);
push_u16_le(&mut self.data, shnum);
push_u16_le(&mut self.data, shstrndx);
}
pub fn write_section_header(
&mut self,
name: u32,
sh_type: u32,
flags: u64,
addr: u64,
offset: u64,
size: u64,
link: u32,
info: u32,
addralign: u64,
entsize: u64,
) {
push_u32_le(&mut self.data, name);
push_u32_le(&mut self.data, sh_type);
push_u64_le(&mut self.data, flags);
push_u64_le(&mut self.data, addr);
push_u64_le(&mut self.data, offset);
push_u64_le(&mut self.data, size);
push_u32_le(&mut self.data, link);
push_u32_le(&mut self.data, info);
push_u64_le(&mut self.data, addralign);
push_u64_le(&mut self.data, entsize);
}
pub fn write_symbol_entry(
&mut self,
name: u32,
info: u8,
other: u8,
shndx: u16,
value: u64,
size: u64,
) {
push_u32_le(&mut self.data, name); push_u16_le(&mut self.data, (info as u16) | ((other as u16) << 8)); push_u16_le(&mut self.data, shndx); push_u64_le(&mut self.data, value); push_u64_le(&mut self.data, size); }
pub fn write_rela_entry(
&mut self,
offset: u64,
info: u64,
addend: i64,
) {
push_u64_le(&mut self.data, offset);
push_u64_le(&mut self.data, info);
push_i64_le(&mut self.data, addend);
}
pub fn build_string_table(strings: &[&str]) -> (Vec<u8>, HashMap<String, u32>) {
let mut data = vec![0u8]; let mut offsets = HashMap::new();
offsets.insert("".to_string(), 0);
for s in strings {
if !offsets.contains_key(*s) {
let offset = data.len() as u32;
data.extend_from_slice(s.as_bytes());
data.push(0);
offsets.insert(s.to_string(), offset);
}
}
(data, offsets)
}
pub fn build_shstrtab(sections: &[&str]) -> (Vec<u8>, HashMap<String, u32>) {
Self::build_string_table(sections)
}
pub fn into_data(self) -> Vec<u8> {
self.data
}
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
pub fn clear(&mut self) {
self.data.clear();
}
}
pub struct X86COFFObjectWriter {
pub data: Vec<u8>,
pub machine: u16,
pub section_count: u16,
pub symbol_count: u32,
pub is_64bit: bool,
}
impl X86COFFObjectWriter {
pub fn new(is_64bit: bool) -> Self {
Self {
data: Vec::new(),
machine: if is_64bit {
IMAGE_FILE_MACHINE_AMD64
} else {
IMAGE_FILE_MACHINE_I386
},
section_count: 0,
symbol_count: 0,
is_64bit,
}
}
pub fn write_coff_header(
&mut self,
characteristics: u16,
timestamp: u32,
symbol_table_offset: u32,
) {
push_u16_le(&mut self.data, self.machine);
push_u16_le(&mut self.data, self.section_count);
push_u32_le(&mut self.data, timestamp);
push_u32_le(&mut self.data, symbol_table_offset);
push_u32_le(&mut self.data, self.symbol_count);
push_u16_le(&mut self.data, 0);
push_u16_le(&mut self.data, characteristics);
}
pub fn write_section_header(
&mut self,
name: &[u8; 8],
virtual_size: u32,
virtual_address: u32,
size_of_raw_data: u32,
pointer_to_raw_data: u32,
pointer_to_relocations: u32,
pointer_to_linenumbers: u32,
number_of_relocations: u16,
number_of_linenumbers: u16,
characteristics: u32,
) {
self.data.extend_from_slice(name);
push_u32_le(&mut self.data, virtual_size);
push_u32_le(&mut self.data, virtual_address);
push_u32_le(&mut self.data, size_of_raw_data);
push_u32_le(&mut self.data, pointer_to_raw_data);
push_u32_le(&mut self.data, pointer_to_relocations);
push_u32_le(&mut self.data, pointer_to_linenumbers);
push_u16_le(&mut self.data, number_of_relocations);
push_u16_le(&mut self.data, number_of_linenumbers);
push_u32_le(&mut self.data, characteristics);
}
pub fn pad_section_name(name: &str) -> [u8; 8] {
let mut buf = [0u8; 8];
let bytes = name.as_bytes();
let len = bytes.len().min(8);
buf[..len].copy_from_slice(&bytes[..len]);
buf
}
pub fn write_symbol_entry(
&mut self,
name: &[u8; 8],
value: u32,
section_number: i16,
sym_type: u16,
storage_class: u8,
aux_count: u8,
) {
self.data.extend_from_slice(name);
push_u32_le(&mut self.data, value);
push_u16_le(&mut self.data, section_number as u16);
push_u16_le(&mut self.data, sym_type);
self.data.push(storage_class);
self.data.push(aux_count);
}
pub fn write_symbol_entry_long(
&mut self,
strtab_offset: u32,
value: u32,
section_number: i16,
sym_type: u16,
storage_class: u8,
aux_count: u8,
) {
self.data.extend_from_slice(&[0u8; 4]);
push_u32_le(&mut self.data, strtab_offset);
push_u32_le(&mut self.data, value);
push_u16_le(&mut self.data, section_number as u16);
push_u16_le(&mut self.data, sym_type);
self.data.push(storage_class);
self.data.push(aux_count);
}
pub fn write_relocation_entry(
&mut self,
virtual_address: u32,
symbol_table_index: u32,
rel_type: u16,
) {
push_u32_le(&mut self.data, virtual_address);
push_u32_le(&mut self.data, symbol_table_index);
push_u16_le(&mut self.data, rel_type);
}
pub fn write_string_table(&mut self, strings: &[String]) {
let mut strtab = vec![0u8; 4]; for s in strings {
strtab.extend_from_slice(s.as_bytes());
strtab.push(0);
}
let size = strtab.len() as u32;
strtab[..4].copy_from_slice(&size.to_le_bytes());
self.data.extend_from_slice(&strtab);
}
pub fn inc_section(&mut self) {
self.section_count += 1;
}
pub fn inc_symbol(&mut self) {
self.symbol_count += 1;
}
pub fn into_data(self) -> Vec<u8> {
self.data
}
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
pub fn clear(&mut self) {
self.data.clear();
self.section_count = 0;
self.symbol_count = 0;
}
}
pub struct X86MachoObjectWriter {
pub data: Vec<u8>,
pub cpu_type: u32,
pub cpu_subtype: u32,
pub ncmds: u32,
pub sizeofcmds: u32,
pub filetype: u32,
pub nsects: u32,
}
impl X86MachoObjectWriter {
pub fn new(is_64bit: bool) -> Self {
Self {
data: Vec::new(),
cpu_type: if is_64bit { CPU_TYPE_X86_64 } else { CPU_TYPE_I386 },
cpu_subtype: if is_64bit {
CPU_SUBTYPE_X86_64_ALL
} else {
CPU_SUBTYPE_I386_ALL
},
ncmds: 0,
sizeofcmds: 0,
filetype: MH_OBJECT,
nsects: 0,
}
}
pub fn write_macho_header(&mut self) {
push_u32_le(&mut self.data, MH_MAGIC_64);
push_u32_le(&mut self.data, self.cpu_type);
push_u32_le(&mut self.data, self.cpu_subtype);
push_u32_le(&mut self.data, self.filetype);
push_u32_le(&mut self.data, self.ncmds);
push_u32_le(&mut self.data, self.sizeofcmds);
push_u32_le(&mut self.data, 0); push_u32_le(&mut self.data, 0); }
pub fn write_segment_command(
&mut self,
segname: &[u8; 16],
vmaddr: u64,
vmsize: u64,
fileoff: u64,
filesize: u64,
maxprot: u32,
initprot: u32,
nsects: u32,
flags: u32,
) {
push_u32_le(&mut self.data, LC_SEGMENT_64);
let cmdsize: u32 = 72 + nsects * 80;
push_u32_le(&mut self.data, cmdsize);
self.data.extend_from_slice(segname);
push_u64_le(&mut self.data, vmaddr);
push_u64_le(&mut self.data, vmsize);
push_u64_le(&mut self.data, fileoff);
push_u64_le(&mut self.data, filesize);
push_u32_le(&mut self.data, maxprot);
push_u32_le(&mut self.data, initprot);
push_u32_le(&mut self.data, nsects);
push_u32_le(&mut self.data, flags);
}
pub fn write_section_header(
&mut self,
sectname: &[u8; 16],
segname: &[u8; 16],
addr: u64,
size: u64,
offset: u32,
align: u32,
reloff: u32,
nreloc: u32,
flags: u32,
reserved1: u32,
reserved2: u32,
reserved3: u32,
) {
self.data.extend_from_slice(sectname);
self.data.extend_from_slice(segname);
push_u64_le(&mut self.data, addr);
push_u64_le(&mut self.data, size);
push_u32_le(&mut self.data, offset);
push_u32_le(&mut self.data, align);
push_u32_le(&mut self.data, reloff);
push_u32_le(&mut self.data, nreloc);
push_u32_le(&mut self.data, flags);
push_u32_le(&mut self.data, reserved1);
push_u32_le(&mut self.data, reserved2);
push_u32_le(&mut self.data, reserved3);
}
pub fn write_nlist64(
&mut self,
n_strx: u32,
n_type: u8,
n_sect: u8,
n_desc: u16,
n_value: u64,
) {
push_u32_le(&mut self.data, n_strx);
self.data.push(n_type);
self.data.push(n_sect);
push_u16_le(&mut self.data, n_desc);
push_u64_le(&mut self.data, n_value);
}
pub fn write_relocation(
&mut self,
r_address: u32,
r_symbolnum: u32,
r_pcrel: bool,
r_length: u8,
r_extern: bool,
r_type: u8,
) {
let mut r_info = r_address;
if r_pcrel {
r_info |= 1 << 24;
}
r_info |= (r_length as u32 & 0x3) << 25;
if r_extern {
r_info |= 1 << 27;
}
r_info |= (r_type as u32 & 0xF) << 28;
push_u32_le(&mut self.data, r_info);
push_u32_le(&mut self.data, r_symbolnum);
}
pub fn write_string_table(&mut self, strings: &[&str]) {
self.data.push(0); self.data.push(0); for s in strings {
self.data.extend_from_slice(s.as_bytes());
self.data.push(0);
}
}
pub fn write_symtab_command(&mut self, symoff: u32, nsyms: u32, stroff: u32, strsize: u32) {
push_u32_le(&mut self.data, LC_SYMTAB);
push_u32_le(&mut self.data, 24); push_u32_le(&mut self.data, symoff);
push_u32_le(&mut self.data, nsyms);
push_u32_le(&mut self.data, stroff);
push_u32_le(&mut self.data, strsize);
}
pub fn write_dysymtab_command(&mut self) {
push_u32_le(&mut self.data, LC_DYSYMTAB);
push_u32_le(&mut self.data, 80); for _ in 0..18 {
push_u32_le(&mut self.data, 0);
}
}
pub fn pad_name_16(name: &str) -> [u8; 16] {
let mut buf = [0u8; 16];
let bytes = name.as_bytes();
let len = bytes.len().min(16);
buf[..len].copy_from_slice(&bytes[..len]);
buf
}
pub fn into_data(self) -> Vec<u8> {
self.data
}
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
pub fn clear(&mut self) {
self.data.clear();
self.ncmds = 0;
self.sizeofcmds = 0;
self.nsects = 0;
}
}
pub struct X86MCObjectWriter {
pub format: X86ObjectFormat,
pub elf_writer: Option<X86ELFObjectWriter>,
pub coff_writer: Option<X86COFFObjectWriter>,
pub macho_writer: Option<X86MachoObjectWriter>,
pub sections: Vec<(String, Vec<u8>, X86SectionFlags, X86SectionType)>,
pub symbols: Vec<X86MCSymbol>,
pub relocations: Vec<(String, X86Fixup)>,
pub is_64bit: bool,
}
impl X86MCObjectWriter {
pub fn new(format: X86ObjectFormat, is_64bit: bool) -> Self {
Self {
format,
elf_writer: if format == X86ObjectFormat::ELF {
Some(X86ELFObjectWriter::new(is_64bit))
} else {
None
},
coff_writer: if format == X86ObjectFormat::COFF {
Some(X86COFFObjectWriter::new(is_64bit))
} else {
None
},
macho_writer: if format == X86ObjectFormat::MachO {
Some(X86MachoObjectWriter::new(is_64bit))
} else {
None
},
sections: Vec::new(),
symbols: Vec::new(),
relocations: Vec::new(),
is_64bit,
}
}
pub fn add_section(
&mut self,
name: &str,
data: Vec<u8>,
flags: X86SectionFlags,
sec_type: X86SectionType,
) {
self.sections
.push((name.to_string(), data, flags, sec_type));
}
pub fn add_symbol(&mut self, sym: X86MCSymbol) {
self.symbols.push(sym);
}
pub fn add_relocation(&mut self, section: &str, fixup: X86Fixup) {
self.relocations.push((section.to_string(), fixup));
}
pub fn write(&mut self) -> Vec<u8> {
match self.format {
X86ObjectFormat::ELF => self.write_elf(),
X86ObjectFormat::COFF => self.write_coff(),
X86ObjectFormat::MachO => self.write_macho(),
X86ObjectFormat::Binary => self.write_binary(),
}
}
fn write_elf(&mut self) -> Vec<u8> {
let writer = self.elf_writer.as_mut().unwrap();
writer.clear();
let mut sec_names: Vec<&str> = vec![".shstrtab", ".strtab", ".symtab"];
for (name, _, _, _) in &self.sections {
sec_names.push(name);
}
let (shstrtab_data, shstrtab_map) = X86ELFObjectWriter::build_shstrtab(&sec_names);
let sym_names: Vec<&str> = self.symbols.iter().map(|s| s.name.as_str()).collect();
let (strtab_data, strtab_map) = X86ELFObjectWriter::build_string_table(&sym_names);
let mut section_headers = Vec::new();
let mut section_datas: Vec<(u64, Vec<u8>)> = Vec::new();
let elf_header_size: u64 = 64;
let mut current_offset = elf_header_size;
section_headers.push((0u32, 0));
for (name, data, flags, sec_type) in &self.sections {
let sh_name = *shstrtab_map.get(name.as_str()).unwrap_or(&0);
let sh_type = if sec_type.is_bss() { SHT_NOBITS } else { SHT_PROGBITS };
let sh_flags = flags.to_elf_sh_flags();
section_datas.push((current_offset, data.clone()));
let sh_size = data.len() as u64;
section_headers.push((sh_name, sh_type as u32));
current_offset += sh_size;
current_offset = align_to_u64(current_offset, 8);
}
let symtab_offset = current_offset;
let mut symtab_data = Vec::new();
X86ELFObjectWriter::write_symbol_entry_to(
&mut symtab_data, 0, 0, STV_DEFAULT, 0, 0, 0,
);
for sym in &self.symbols {
let st_name = *strtab_map.get(sym.name.as_str()).unwrap_or(&0);
let st_info = sym.elf_st_info();
let st_other = sym.elf_st_other();
let st_shndx = if sym.absolute {
0xFFF1u16
} else if sym.common {
0xFFF2u16
} else if sym.section_index == 0 && !sym.defined {
0
} else {
sym.section_index as u16 + 1
};
X86ELFObjectWriter::write_symbol_entry_to(
&mut symtab_data,
st_name,
st_info,
st_other,
st_shndx,
sym.value,
sym.size,
);
}
let symtab_size = symtab_data.len() as u64;
let strtab_offset = symtab_offset + symtab_size;
let shstrtab_offset = strtab_offset + strtab_data.len() as u64;
let num_data_sections = self.sections.len();
let num_total_sections = num_data_sections + 3; let shoff = shstrtab_offset + shstrtab_data.len() as u64;
writer.write_elf_header(
0, 0, shoff, 0, elf_header_size as u16,
0, 0, 64, (num_total_sections + 1) as u16, (num_total_sections + 1) as u16, );
for (_, data) in §ion_datas {
writer.data.extend_from_slice(data);
}
writer.data.extend_from_slice(&symtab_data);
writer.data.extend_from_slice(&strtab_data);
writer.data.extend_from_slice(&shstrtab_data);
writer.write_section_header(0, SHT_NULL, 0, 0, 0, 0, 0, 0, 0, 0);
for (i, (name, data, flags, _)) in self.sections.iter().enumerate() {
let (sh_name, _sh_type) = section_headers[i + 1];
let sh_type = if flags.to_elf_sh_flags() == SHF_ALLOC && !flags.write && !flags.exec {
SHT_PROGBITS
} else {
SHT_PROGBITS
};
let mut sh_flags = flags.to_elf_sh_flags();
if sh_type == SHT_NOBITS {
sh_flags |= SHF_ALLOC | SHF_WRITE;
}
let (off, _) = section_datas[i];
writer.write_section_header(
sh_name,
sh_type,
sh_flags,
0,
off,
data.len() as u64,
0,
0,
flags.to_elf_sh_flags().trailing_zeros() as u64,
0,
);
}
let sym_name = *shstrtab_map.get(".symtab").unwrap_or(&0);
writer.write_section_header(
sym_name,
SHT_SYMTAB,
0,
0,
symtab_offset,
symtab_size,
(num_data_sections + 2) as u32, 1, 8,
24, );
let str_name = *shstrtab_map.get(".strtab").unwrap_or(&0);
writer.write_section_header(
str_name,
SHT_STRTAB,
0,
0,
strtab_offset,
strtab_data.len() as u64,
0,
0,
1,
0,
);
let shstr_name = *shstrtab_map.get(".shstrtab").unwrap_or(&0);
writer.write_section_header(
shstr_name,
SHT_STRTAB,
0,
0,
shstrtab_offset,
shstrtab_data.len() as u64,
0,
0,
1,
0,
);
writer.into_data()
}
fn write_coff(&mut self) -> Vec<u8> {
let writer = self.coff_writer.as_mut().unwrap();
writer.clear();
let header_size: u32 = 20;
let section_header_size: u32 = COFF_SECTION_HEADER_SIZE as u32;
let num_sections = self.sections.len() as u16;
writer.section_count = num_sections;
let mut string_table = Vec::new();
let mut strtab_offsets: HashMap<String, u32> = HashMap::new();
let mut strtab_next_offset: u32 = 4;
let mut section_bodies: Vec<(Vec<u8>, u32, u32)> = Vec::new();
let mut current_offset = header_size + section_header_size * num_sections as u32;
for (_name, data, flags, _sec_type) in &self.sections {
let chars = flags.to_coff_characteristics();
section_bodies.push((data.clone(), chars, current_offset));
current_offset += data.len() as u32;
current_offset = (current_offset + 15) & !15; }
let symbol_table_offset = current_offset;
let mut symbol_count: u32 = 0;
let mut symtab_data = Vec::new();
symtab_data.extend_from_slice(&X86COFFObjectWriter::pad_section_name(".file"));
push_u32_le(&mut symtab_data, 0);
push_u16_le(&mut symtab_data, 0);
push_u16_le(&mut symtab_data, 0);
symtab_data.push(IMAGE_SYM_CLASS_FILE);
symtab_data.push(0);
let fname = b"test.o\0\0\0\0\0\0\0\0\0\0\0\0\0";
symtab_data.extend_from_slice(fname);
let aux: [u8; 18] = [0; 18];
symtab_data.extend_from_slice(&aux);
symbol_count += 2;
for (i, (name, _, _, _)) in self.sections.iter().enumerate() {
symtab_data.extend_from_slice(&X86COFFObjectWriter::pad_section_name(name));
push_u32_le(&mut symtab_data, 0);
push_u16_le(&mut symtab_data, (i + 1) as u16);
push_u16_le(&mut symtab_data, 0);
symtab_data.push(IMAGE_SYM_CLASS_SECTION);
symtab_data.push(1); let section_size = self.sections[i].1.len() as u32;
push_u32_le(&mut symtab_data, section_size);
push_u16_le(&mut symtab_data, 0); push_u16_le(&mut symtab_data, 0); push_u32_le(&mut symtab_data, 0);
push_u16_le(&mut symtab_data, 0);
push_u16_le(&mut symtab_data, 0);
symbol_count += 2;
}
for sym in &self.symbols {
let name_bytes = X86COFFObjectWriter::pad_section_name(&sym.name);
let storage_class = match sym.binding {
X86SymbolBinding::Global => IMAGE_SYM_CLASS_EXTERNAL,
_ => IMAGE_SYM_CLASS_STATIC,
};
let sec_num: i16 = if sym.absolute {
-1
} else if sym.section_index == 0 && !sym.defined {
0
} else {
(sym.section_index + 1) as i16
};
symtab_data.extend_from_slice(&name_bytes);
push_u32_le(&mut symtab_data, sym.value as u32);
push_u16_le(&mut symtab_data, sec_num as u16);
push_u16_le(&mut symtab_data, 0); symtab_data.push(storage_class);
symtab_data.push(0);
symbol_count += 1;
}
let strtab_size: u32 = 4; let string_table_data = vec![strtab_size.to_le_bytes().to_vec(), vec![0; 0]].concat();
writer.symbol_count = symbol_count;
writer.write_coff_header(
IMAGE_FILE_LINE_NUMS_STRIPPED
| IMAGE_FILE_LARGE_ADDRESS_AWARE
| IMAGE_FILE_RELOCS_STRIPPED,
0, symbol_table_offset,
);
for (i, (name, data, _, _)) in self.sections.iter().enumerate() {
let (_, chars, raw_offset) = section_bodies[i];
writer.write_section_header(
&X86COFFObjectWriter::pad_section_name(name),
0, 0, data.len() as u32,
raw_offset,
0, 0, 0, 0, chars,
);
}
for (i, (_name, data, _, _)) in self.sections.iter().enumerate() {
writer.data.extend_from_slice(data);
while writer.data.len() as u32 % 16 != 0 {
writer.data.push(0);
}
}
writer.data.extend_from_slice(&symtab_data);
writer.data.extend_from_slice(&string_table_data);
writer.into_data()
}
fn write_macho(&mut self) -> Vec<u8> {
let writer = self.macho_writer.as_mut().unwrap();
writer.clear();
let nsects = self.sections.len() as u32;
let segment_name = X86MachoObjectWriter::pad_name_16("");
let num_segments: u32 = if nsects > 0 { 1 } else { 0 };
writer.ncmds = num_segments + 2; writer.sizeofcmds = 72 + 80 * nsects + 24 + 80; writer.nsects = nsects;
writer.write_macho_header();
let section_data_start: u64 = 72 + 80 * nsects as u64 + 24 + 80; let section_data_start_aligned = align_to_u64(section_data_start, 16);
let mut total_file_size = section_data_start_aligned;
for (_, data, _, _) in &self.sections {
total_file_size += data.len() as u64;
}
writer.write_segment_command(
&segment_name,
0, total_file_size, 0, total_file_size, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE,
VM_PROT_READ | VM_PROT_EXECUTE,
nsects,
0,
);
let mut section_offset = section_data_start_aligned as u32;
for (name, data, flags, _sec_type) in &self.sections {
let section_name = X86MachoObjectWriter::pad_name_16(name);
let segname = X86MachoObjectWriter::pad_name_16("__TEXT");
let macho_flags = if flags.exec {
S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS
} else if flags.write {
S_REGULAR
} else {
S_REGULAR
} | S_REGULAR;
writer.write_section_header(
§ion_name,
&segname,
0,
data.len() as u64,
section_offset,
0, 0, 0, macho_flags,
0, 0, 0,
);
section_offset += data.len() as u32;
section_offset = (section_offset + 15) & !15;
}
let symtab_virtual_offset = section_data_start + total_file_size - section_data_start_aligned;
let nsyms = self.symbols.len() as u32 + 1;
writer.write_symtab_command(symtab_virtual_offset as u32, nsyms, 0, 0);
writer.write_dysymtab_command();
while writer.data.len() < section_data_start_aligned as usize {
writer.data.push(0);
}
for (_name, data, _, _) in &self.sections {
writer.data.extend_from_slice(data);
while writer.data.len() % 16 != 0 {
writer.data.push(0);
}
}
writer.write_nlist64(0, N_UNDF | N_EXT, 0, 0, 0);
let strtab_start = writer.data.len() + (self.symbols.len() * 16);
let mut strtab = vec![0u8, 0u8]; let mut strtab_offsets: HashMap<String, u32> = HashMap::new();
for sym in &self.symbols {
let n_strx = if sym.name.is_empty() {
1
} else {
let off = strtab.len() as u32;
strtab.extend_from_slice(sym.name.as_bytes());
strtab.push(0);
off
};
let n_type = match sym.binding {
X86SymbolBinding::Global => N_SECT | N_EXT,
_ => N_SECT,
};
let n_sect = if sym.section_index == 0 && !sym.defined {
0
} else {
(sym.section_index + 1) as u8
};
writer.write_nlist64(n_strx, n_type, n_sect, 0, sym.value);
}
writer.data.extend_from_slice(&strtab);
writer.into_data()
}
fn write_binary(&self) -> Vec<u8> {
let mut out = Vec::new();
for (_, data, _, _) in &self.sections {
out.extend_from_slice(data);
}
out
}
}
impl X86ELFObjectWriter {
fn write_symbol_entry_to(
buf: &mut Vec<u8>,
name: u32,
info: u8,
other: u8,
shndx: u16,
value: u64,
size: u64,
) {
push_u32_le(buf, name);
buf.push(info);
buf.push(other);
push_u16_le(buf, shndx);
push_u64_le(buf, value);
push_u64_le(buf, size);
}
}
pub struct X86MCAsmBackend {
pub target_triple: String,
pub is_64bit: bool,
pub addr_mode: X86AddrMode,
pub format: X86ObjectFormat,
pub features: HashSet<String>,
}
impl X86MCAsmBackend {
pub fn new(triple: &str) -> Self {
let is_64bit = triple.contains("x86_64") || triple.contains("amd64");
Self {
target_triple: triple.to_string(),
is_64bit,
addr_mode: if is_64bit {
X86AddrMode::Mode64
} else {
X86AddrMode::Mode32
},
format: X86ObjectFormat::from_triple(triple),
features: HashSet::new(),
}
}
pub fn target_name(&self) -> &str {
if self.is_64bit {
"x86_64"
} else {
"i386"
}
}
pub fn encode_fixup(&self, bytes: &mut [u8], offset: usize, kind: X86FixupKind, value: i64) {
let size = kind.size_bytes() as usize;
if offset + size > bytes.len() {
return;
}
match size {
1 => {
bytes[offset] = value as u8;
}
2 => {
bytes[offset..offset + 2].copy_from_slice(&(value as i16).to_le_bytes());
}
4 => {
bytes[offset..offset + 4].copy_from_slice(&(value as i32).to_le_bytes());
}
8 => {
bytes[offset..offset + 8].copy_from_slice(&(value as i64).to_le_bytes());
}
_ => {}
}
}
pub fn can_relax(&self, mnemonic: &str, current_size: usize) -> bool {
if is_conditional_jump(mnemonic) {
return current_size <= 6;
}
if mnemonic == "jmp" || mnemonic == "JMP" {
return current_size <= 5;
}
if mnemonic.starts_with("loop") || mnemonic.starts_with("LOOP") {
return false;
}
false
}
pub fn get_min_size(&self, mnemonic: &str) -> u8 {
if is_conditional_jump(mnemonic) {
2
} else if mnemonic == "jmp" || mnemonic == "JMP" {
2
} else {
1
}
}
pub fn get_max_size(&self, mnemonic: &str) -> u8 {
if is_conditional_jump(mnemonic) {
6 } else if mnemonic == "jmp" || mnemonic == "JMP" {
5 } else {
15 }
}
pub fn get_relaxed_size(&self, mnemonic: &str, distance: i64) -> u8 {
let fits_short =
distance >= X86_SHORT_BRANCH_MIN as i64 && distance <= X86_SHORT_BRANCH_MAX as i64;
if is_conditional_jump(mnemonic) {
if fits_short {
2
} else {
6
}
} else if mnemonic == "jmp" || mnemonic == "JMP" {
if fits_short {
2
} else {
5
}
} else {
self.get_max_size(mnemonic)
}
}
pub fn generate_nop(&self, size: usize) -> Vec<u8> {
generate_x86_nop(size)
}
pub fn compute_alignment_padding(
&self,
current_offset: usize,
alignment: u32,
max_bytes: u32,
) -> usize {
let align = alignment as usize;
let misalignment = current_offset & (align - 1);
if misalignment == 0 {
return 0;
}
let padding = align - misalignment;
padding.min(max_bytes as usize)
}
pub fn compute_alignment_nops(
&self,
current_offset: usize,
alignment: u32,
max_bytes: u32,
) -> Vec<u8> {
let padding = self.compute_alignment_padding(current_offset, alignment, max_bytes);
self.generate_nop(padding)
}
pub fn is_fixup_kind_valid(&self, kind: X86FixupKind) -> bool {
match kind {
X86FixupKind::RipRel32 | X86FixupKind::Rel64 => self.is_64bit,
X86FixupKind::GotPcRel | X86FixupKind::GotPcRelX | X86FixupKind::RexGotPcRelX => {
self.is_64bit
}
X86FixupKind::TlsGd
| X86FixupKind::TlsLd
| X86FixupKind::GotTpOff
| X86FixupKind::TlsDesc => self.is_64bit,
_ => true,
}
}
pub fn get_fixup_size(&self, kind: X86FixupKind) -> u8 {
kind.size_bytes()
}
pub fn fixup_needs_offset_adjustment(&self, kind: X86FixupKind) -> bool {
kind.is_pc_relative()
}
pub fn get_pc_relative_adjustment(&self, kind: X86FixupKind) -> i64 {
if kind.is_pc_relative() {
kind.size_bytes() as i64
} else {
0
}
}
pub fn create_elf_object_writer(&self) -> X86ELFObjectWriter {
X86ELFObjectWriter::new(self.is_64bit)
}
pub fn create_coff_object_writer(&self) -> X86COFFObjectWriter {
X86COFFObjectWriter::new(self.is_64bit)
}
pub fn create_macho_object_writer(&self) -> X86MachoObjectWriter {
X86MachoObjectWriter::new(self.is_64bit)
}
pub fn create_object_writer(&self) -> X86MCObjectWriter {
X86MCObjectWriter::new(self.format, self.is_64bit)
}
pub fn text_section_alignment(&self) -> u32 {
16
}
pub fn max_nop_size(&self) -> usize {
X86_MAX_NOP_SIZE
}
pub fn has_feature(&self, feature: &str) -> bool {
self.features.contains(feature)
}
pub fn add_feature(&mut self, feature: &str) {
self.features.insert(feature.to_string());
}
}
fn is_conditional_jump(mnemonic: &str) -> bool {
let upper = mnemonic.to_uppercase();
matches!(
upper.as_str(),
"JO"
| "JNO"
| "JB"
| "JNAE"
| "JC"
| "JNB"
| "JAE"
| "JNC"
| "JZ"
| "JE"
| "JNZ"
| "JNE"
| "JBE"
| "JNA"
| "JA"
| "JNBE"
| "JS"
| "JNS"
| "JP"
| "JPE"
| "JNP"
| "JPO"
| "JL"
| "JNGE"
| "JNL"
| "JGE"
| "JLE"
| "JNG"
| "JNLE"
| "JG"
| "JECXZ"
| "JRCXZ"
)
}
pub struct X86MCLayerDeep {
pub backend: X86MCAsmBackend,
pub assembler: X86MCAssembler,
pub asm_streamer: X86MCAsmStreamer,
pub object_streamer: X86MCObjectStreamer,
pub null_streamer: X86MCNullStreamer,
pub dwarf_streamer: X86MCDwarfStreamer,
pub record_streamer: X86MCRecordStreamer,
pub object_writer: X86MCObjectWriter,
pub format: X86ObjectFormat,
initialized: bool,
}
impl X86MCLayerDeep {
pub fn new(triple: &str) -> Self {
let format = X86ObjectFormat::from_triple(triple);
let is_64bit = triple.contains("x86_64") || triple.contains("amd64");
Self {
backend: X86MCAsmBackend::new(triple),
assembler: X86MCAssembler::new(triple),
asm_streamer: X86MCAsmStreamer::new(),
object_streamer: X86MCObjectStreamer::new(format),
null_streamer: X86MCNullStreamer::new(),
dwarf_streamer: X86MCDwarfStreamer::new(),
record_streamer: X86MCRecordStreamer::new(),
object_writer: X86MCObjectWriter::new(format, is_64bit),
format,
initialized: true,
}
}
pub fn with_format(triple: &str, format: X86ObjectFormat) -> Self {
let is_64bit = triple.contains("x86_64") || triple.contains("amd64");
Self {
backend: X86MCAsmBackend::new(triple),
assembler: X86MCAssembler::new(triple),
asm_streamer: X86MCAsmStreamer::new(),
object_streamer: X86MCObjectStreamer::new(format),
null_streamer: X86MCNullStreamer::new(),
dwarf_streamer: X86MCDwarfStreamer::new(),
record_streamer: X86MCRecordStreamer::new(),
object_writer: X86MCObjectWriter::new(format, is_64bit),
format,
initialized: true,
}
}
pub fn set_pic(&mut self, pic: bool) {
self.assembler.set_pic(pic);
}
pub fn set_relaxation(&mut self, enable: bool) {
self.assembler.set_relaxation(enable);
}
pub fn add_feature(&mut self, feature: &str) {
self.assembler.add_feature(feature);
self.backend.add_feature(feature);
}
pub fn reset(&mut self) {
self.assembler.reset();
self.asm_streamer.clear();
self.null_streamer.reset();
self.record_streamer.clear();
}
pub fn emit_assembly(&mut self) -> String {
self.asm_streamer.clear();
for sec in &self.assembler.sections {
self.asm_streamer.switch_section(&sec.name);
for frag in &sec.fragments {
match frag {
X86MCFragment::Instruction(f) => {
let mnemonic = f.mnemonic.as_deref().unwrap_or("???");
self.asm_streamer.emit_instruction(&f.bytes, mnemonic);
}
X86MCFragment::Data(f) => {
self.asm_streamer.emit_data(&f.data);
}
X86MCFragment::Align(f) => {
self.asm_streamer
.emit_alignment(f.alignment, f.fill_byte, f.max_bytes);
}
X86MCFragment::Fill(f) => {
self.asm_streamer.emit_fill(&f.value, f.size);
}
X86MCFragment::Org(f) => {
self.asm_streamer.emit_org(f.address, f.fill_byte);
}
_ => {}
}
}
}
self.asm_streamer.finish();
self.asm_streamer.into_string()
}
pub fn emit_object(&mut self) -> Vec<u8> {
self.object_writer = self.backend.create_object_writer();
for sec in &self.assembler.sections {
let data = self.assembler.flatten_section(
self.assembler.find_section(&sec.name).unwrap_or(0),
);
self.object_writer
.add_section(&sec.name, data, sec.flags, sec.section_type);
}
for sym in &self.assembler.symbols {
self.object_writer.add_symbol(sym.clone());
}
for (sec_name, fixup) in &self.assembler.pending_fixups {
self.object_writer.add_relocation(sec_name, fixup.clone());
}
self.object_writer.write()
}
pub fn assemble_all(&mut self) {
self.emit_assembly();
self.emit_object();
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn target_triple(&self) -> &str {
&self.backend.target_triple
}
pub fn is_64bit(&self) -> bool {
self.backend.is_64bit
}
}
impl Default for X86MCLayerDeep {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mc_data_fragment_creation() {
let data = vec![0x01, 0x02, 0x03, 0x04];
let frag = MCDataFragment::new(data.clone());
assert_eq!(frag.data, data);
assert_eq!(frag.fixups.len(), 0);
assert!(!frag.is_read_only);
}
#[test]
fn test_mc_data_fragment_const() {
let data = vec![0xAA; 16];
let frag = MCDataFragment::new_const(data.clone());
assert!(frag.is_read_only);
}
#[test]
fn test_mc_data_fragment_add_fixup() {
let mut frag = MCDataFragment::new(vec![0; 4]);
let fixup = X86Fixup::new(0, X86FixupKind::Abs32, X86FixupSource::Symbol("foo".into()));
frag.add_fixup(fixup);
assert_eq!(frag.fixups.len(), 1);
}
#[test]
fn test_mc_inst_fragment_creation() {
let bytes = vec![0x90]; let frag = MCInstFragment::new(bytes.clone());
assert_eq!(frag.bytes, bytes);
assert!(frag.mnemonic.is_none());
assert!(!frag.relaxed);
}
#[test]
fn test_mc_inst_fragment_with_mnemonic() {
let bytes = vec![0x90];
let frag = MCInstFragment::new(bytes).with_mnemonic("nop");
assert_eq!(frag.mnemonic.unwrap(), "nop");
}
#[test]
fn test_mc_inst_fragment_add_fixup() {
let mut frag = MCInstFragment::new(vec![0xE8, 0, 0, 0, 0]); let fixup = X86Fixup::new(1, X86FixupKind::Rel32, X86FixupSource::Symbol("func".into()));
frag.add_fixup(fixup);
assert_eq!(frag.fixups.len(), 1);
}
#[test]
fn test_mc_align_fragment_compute_padding() {
let frag = MCAlignFragment::new(16, 0x90, 15);
assert_eq!(frag.compute_padding(0), 0);
assert_eq!(frag.compute_padding(1), 15);
assert_eq!(frag.compute_padding(16), 0);
assert_eq!(frag.compute_padding(31), 1);
}
#[test]
fn test_mc_align_fragment_compute_padding_max_bytes() {
let frag = MCAlignFragment::new(128, 0x90, 10);
assert_eq!(frag.compute_padding(64), 10); }
#[test]
fn test_mc_align_fragment_compute_fill() {
let mut frag = MCAlignFragment::new(16, 0x90, 15);
frag.compute_fill(5); assert_eq!(frag.fill_data.len(), 11);
}
#[test]
fn test_mc_fill_fragment_creation() {
let frag = MCFillFragment::new(vec![0xCC], 100);
assert_eq!(frag.total_size(), 100);
}
#[test]
fn test_mc_fill_fragment_multi_byte() {
let frag = MCFillFragment::new(vec![0xDE, 0xAD], 10);
assert_eq!(frag.total_size(), 20);
}
#[test]
fn test_mc_org_fragment_creation() {
let frag = MCOrgFragment::new(0x1000, 0x90);
assert_eq!(frag.address, 0x1000);
assert_eq!(frag.fill_byte, 0x90);
}
#[test]
fn test_mc_dwarf_frame_fragment_creation() {
let data = vec![0x00, 0x00, 0x00, 0x10];
let frag = MCDwarfFrameFragment::new(data.clone());
assert_eq!(frag.data, data);
assert!(!frag.is_eh_frame);
}
#[test]
fn test_mc_dwarf_frame_eh() {
let data = vec![0x00, 0x00, 0x00, 0x10];
let frag = MCDwarfFrameFragment::new_eh_frame(data.clone());
assert!(frag.is_eh_frame);
}
#[test]
fn test_mc_dwarf_line_fragment() {
let data = vec![0x01, 0x02, 0x03];
let frag = MCDwarfLineFragment::new(data.clone());
assert_eq!(frag.data, data);
assert_eq!(frag.line_offset, 0);
}
#[test]
fn test_mc_compact_unwind_fragment() {
let data = vec![0x10; 32];
let frag = MCCompactUnwindFragment::new(data.clone());
assert_eq!(frag.data, data);
assert_eq!(frag.encoding_mode, 0);
}
#[test]
fn test_mc_compact_unwind_rbp_frame() {
let frag = MCCompactUnwindFragment::new_rbp_frame(0x1000, 0x50);
assert_eq!(frag.encoding_mode, 1);
assert_eq!(frag.function_address, 0x1000);
assert_eq!(frag.function_size, 0x50);
}
#[test]
fn test_mc_cv_def_range_fragment() {
let data = vec![0x00, 0x11, 0x22];
let frag = MCCVDefRangeFragment::new(data.clone());
assert_eq!(frag.data, data);
assert_eq!(frag.kind, 0);
}
#[test]
fn test_mc_relaxable_fragment_creation() {
let bytes = vec![0xEB, 0x05]; let frag = MCRelaxableFragment::new(bytes, 5);
assert_eq!(frag.current_size, 2);
assert_eq!(frag.max_size, 5);
assert!(!frag.relaxed);
}
#[test]
fn test_mc_relaxable_fragment_can_relax() {
let frag = MCRelaxableFragment::new(vec![0xEB, 0x05], 5);
assert!(frag.can_relax());
}
#[test]
fn test_mc_relaxable_fragment_relax_to() {
let mut frag = MCRelaxableFragment::new(vec![0xEB, 0x05], 5);
frag.relax_to(5, vec![0xE9, 0x00, 0x00, 0x00, 0x00]);
assert_eq!(frag.current_size, 5);
assert!(frag.relaxed);
}
#[test]
fn test_mc_relaxable_fragment_is_within_short_range() {
let frag = MCRelaxableFragment::new(vec![0xEB, 0x00], 5);
assert!(frag.is_within_short_range(100));
assert!(frag.is_within_short_range(-100));
assert!(!frag.is_within_short_range(200));
assert!(!frag.is_within_short_range(-200));
}
#[test]
fn test_mc_relaxable_fragment_relaxation_overhead() {
let frag = MCRelaxableFragment::new(vec![0xEB, 0x00], 5);
assert_eq!(frag.relaxation_overhead(), 3);
}
#[test]
fn test_fragment_kind_name() {
let data_frag = X86MCFragment::Data(MCDataFragment::new(vec![0x00]));
let inst_frag = X86MCFragment::Instruction(MCInstFragment::new(vec![0x90]));
let align_frag = X86MCFragment::Align(MCAlignFragment::new(16, 0x90, 15));
assert_eq!(data_frag.kind_name(), "Data");
assert_eq!(inst_frag.kind_name(), "Instruction");
assert_eq!(align_frag.kind_name(), "Align");
}
#[test]
fn test_fragment_is_instruction() {
let inst_frag = X86MCFragment::Instruction(MCInstFragment::new(vec![0x90]));
let data_frag = X86MCFragment::Data(MCDataFragment::new(vec![0x00]));
let relax_frag = X86MCFragment::Relaxable(MCRelaxableFragment::new(vec![0xEB, 0x05], 5));
assert!(inst_frag.is_instruction());
assert!(!data_frag.is_instruction());
assert!(relax_frag.is_instruction());
}
#[test]
fn test_fragment_get_bytes_data() {
let data = vec![0x01, 0x02, 0x03];
let frag = X86MCFragment::Data(MCDataFragment::new(data.clone()));
assert_eq!(frag.get_bytes(), data);
}
#[test]
fn test_fragment_get_bytes_instruction() {
let bytes = vec![0x48, 0x89, 0xD8];
let frag = X86MCFragment::Instruction(MCInstFragment::new(bytes.clone()));
assert_eq!(frag.get_bytes(), bytes);
}
#[test]
fn test_fragment_get_bytes_fill() {
let frag = X86MCFragment::Fill(MCFillFragment::new(vec![0xCC], 5));
assert_eq!(frag.get_bytes(), vec![0xCC; 5]);
}
#[test]
fn test_fragment_size() {
let frag = X86MCFragment::Instruction(MCInstFragment::new(vec![0x90, 0x90, 0x90]));
assert_eq!(frag.size(), 3);
}
#[test]
fn test_x86_fixup_creation() {
let fixup = X86Fixup::new(0, X86FixupKind::Rel32, X86FixupSource::Symbol("main".into()));
assert_eq!(fixup.offset, 0);
assert_eq!(fixup.kind, X86FixupKind::Rel32);
assert!(!fixup.is_branch);
}
#[test]
fn test_x86_fixup_with_addend() {
let fixup = X86Fixup::new(4, X86FixupKind::Abs64, X86FixupSource::Absolute(42))
.with_addend(-4);
assert_eq!(fixup.addend, -4);
}
#[test]
fn test_x86_fixup_with_branch() {
let fixup = X86Fixup::new(1, X86FixupKind::Rel8, X86FixupSource::Label("loop".into()))
.with_branch();
assert!(fixup.is_branch);
}
#[test]
fn test_x86_fixup_with_got() {
let fixup = X86Fixup::new(
3,
X86FixupKind::GotPcRel,
X86FixupSource::Symbol("ext".into()),
)
.with_got();
assert!(fixup.needs_got);
}
#[test]
fn test_x86_fixup_with_plt() {
let fixup = X86Fixup::new(1, X86FixupKind::Plt32, X86FixupSource::Symbol("foo".into()))
.with_plt();
assert!(fixup.needs_plt);
}
#[test]
fn test_fixup_kind_size_bytes() {
assert_eq!(X86FixupKind::Rel8.size_bytes(), 1);
assert_eq!(X86FixupKind::Rel32.size_bytes(), 4);
assert_eq!(X86FixupKind::Abs64.size_bytes(), 8);
assert_eq!(X86FixupKind::None.size_bytes(), 0);
}
#[test]
fn test_fixup_kind_is_pc_relative() {
assert!(X86FixupKind::Rel8.is_pc_relative());
assert!(X86FixupKind::Rel32.is_pc_relative());
assert!(!X86FixupKind::Abs64.is_pc_relative());
}
#[test]
fn test_fixup_kind_to_elf_reloc() {
assert_eq!(X86FixupKind::Rel32.to_elf_reloc(), R_X86_64_PC32);
assert_eq!(X86FixupKind::Abs64.to_elf_reloc(), R_X86_64_64);
assert_eq!(X86FixupKind::Abs32.to_elf_reloc(), R_X86_64_32);
assert_eq!(X86FixupKind::GotPcRel.to_elf_reloc(), R_X86_64_GOTPCREL);
}
#[test]
fn test_fixup_kind_to_coff_reloc() {
assert_eq!(
X86FixupKind::Abs64.to_coff_reloc(),
IMAGE_REL_AMD64_ADDR64
);
assert_eq!(
X86FixupKind::Rel32.to_coff_reloc(),
IMAGE_REL_AMD64_REL32
);
}
#[test]
fn test_fixup_source_display() {
assert_eq!(
format!("{}", X86FixupSource::Symbol("foo".into())),
"foo"
);
assert_eq!(
format!("{}", X86FixupSource::Section(".text".into(), 16)),
".text+16"
);
assert_eq!(
format!("{}", X86FixupSource::SymbolDiff("end".into(), "start".into())),
"end-start"
);
}
#[test]
fn test_x86_mc_symbol_creation() {
let sym = X86MCSymbol::new("test_sym");
assert_eq!(sym.name, "test_sym");
assert_eq!(sym.binding, X86SymbolBinding::Local);
assert!(!sym.defined);
}
#[test]
fn test_x86_mc_symbol_global() {
let sym = X86MCSymbol::new_global("main", 1, 0x1000);
assert_eq!(sym.binding, X86SymbolBinding::Global);
assert!(sym.defined);
assert_eq!(sym.section_index, 1);
assert_eq!(sym.value, 0x1000);
}
#[test]
fn test_x86_mc_symbol_absolute() {
let sym = X86MCSymbol::new_absolute("ABS_VAL", 42);
assert!(sym.absolute);
assert_eq!(sym.value, 42);
}
#[test]
fn test_x86_mc_symbol_common() {
let sym = X86MCSymbol::new_common("common_var", 64, 8);
assert!(sym.common);
assert_eq!(sym.size, 64);
assert_eq!(sym.common_alignment, 8);
}
#[test]
fn test_x86_mc_symbol_temporary() {
let sym = X86MCSymbol::new_temporary(".Ltmp1");
assert!(sym.temporary);
}
#[test]
fn test_x86_mc_symbol_with_binding() {
let sym = X86MCSymbol::new("foo").with_binding(X86SymbolBinding::Weak);
assert_eq!(sym.binding, X86SymbolBinding::Weak);
}
#[test]
fn test_x86_mc_symbol_with_type() {
let sym = X86MCSymbol::new("foo").with_type(X86SymbolType::Func);
assert_eq!(sym.sym_type, X86SymbolType::Func);
}
#[test]
fn test_x86_mc_symbol_with_visibility() {
let sym = X86MCSymbol::new("foo").with_visibility(X86SymbolVisibility::Hidden);
assert_eq!(sym.visibility, X86SymbolVisibility::Hidden);
}
#[test]
fn test_x86_mc_symbol_elf_st_info() {
let sym = X86MCSymbol::new_global("func", 1, 0).with_type(X86SymbolType::Func);
let st_info = sym.elf_st_info();
let binding = st_info >> 4;
let typ = st_info & 0x0F;
assert_eq!(binding, STB_GLOBAL);
assert_eq!(typ, STT_FUNC);
}
#[test]
fn test_x86_mc_symbol_elf_st_other() {
let sym = X86MCSymbol::new("foo").with_visibility(X86SymbolVisibility::Hidden);
assert_eq!(sym.elf_st_other(), STV_HIDDEN);
}
#[test]
fn test_x86_mc_label_creation() {
let label = X86MCLabel::new("loop_start");
assert_eq!(label.name, "loop_start");
assert!(!label.defined);
}
#[test]
fn test_x86_mc_label_define() {
let mut label = X86MCLabel::new("target");
label.define(0, 3, 0x10);
assert!(label.defined);
assert_eq!(label.section_index, Some(0));
assert_eq!(label.fragment_index, Some(3));
assert_eq!(label.offset, Some(0x10));
}
#[test]
fn test_x86_mc_label_forward_ref() {
let label = X86MCLabel::new("forward_ref");
assert!(label.is_forward_ref());
}
#[test]
fn test_x86_mc_label_temporary() {
let label = X86MCLabel::new_temporary(".L5");
assert!(label.temporary);
}
#[test]
fn test_x86_expr_value_constant() {
let val = X86ExprValue::Constant(42);
assert_eq!(val.as_constant(), Some(42));
assert!(val.is_resolved());
assert!(!val.needs_relocation());
}
#[test]
fn test_x86_expr_value_symbol() {
let val = X86ExprValue::Symbol("foo".into());
assert!(val.as_constant().is_none());
assert!(!val.is_resolved());
assert!(val.needs_relocation());
}
#[test]
fn test_x86_expr_value_symbol_diff() {
let val = X86ExprValue::SymbolDiff("end".into(), "start".into());
assert!(val.needs_relocation());
}
#[test]
fn test_object_format_from_triple_elf() {
assert_eq!(
X86ObjectFormat::from_triple("x86_64-unknown-linux-gnu"),
X86ObjectFormat::ELF
);
assert_eq!(
X86ObjectFormat::from_triple("i386-unknown-freebsd"),
X86ObjectFormat::ELF
);
}
#[test]
fn test_object_format_from_triple_coff() {
assert_eq!(
X86ObjectFormat::from_triple("x86_64-pc-windows-msvc"),
X86ObjectFormat::COFF
);
assert_eq!(
X86ObjectFormat::from_triple("x86_64-w64-mingw32"),
X86ObjectFormat::COFF
);
}
#[test]
fn test_object_format_from_triple_macho() {
assert_eq!(
X86ObjectFormat::from_triple("x86_64-apple-darwin"),
X86ObjectFormat::MachO
);
assert_eq!(
X86ObjectFormat::from_triple("x86_64-apple-macosx"),
X86ObjectFormat::MachO
);
}
#[test]
fn test_object_format_is_checks() {
assert!(X86ObjectFormat::ELF.is_elf());
assert!(!X86ObjectFormat::COFF.is_elf());
assert!(!X86ObjectFormat::ELF.is_macho());
assert!(X86ObjectFormat::MachO.is_macho());
}
#[test]
fn test_object_format_section_names() {
assert_eq!(X86ObjectFormat::ELF.text_section(), ".text");
assert_eq!(X86ObjectFormat::MachO.text_section(), "__text");
assert_eq!(X86ObjectFormat::COFF.data_section(), ".data");
assert_eq!(X86ObjectFormat::ELF.bss_section(), ".bss");
}
#[test]
fn test_section_flags_text() {
let flags = X86SectionFlags::text();
assert!(!flags.write);
assert!(flags.alloc);
assert!(flags.exec);
}
#[test]
fn test_section_flags_data() {
let flags = X86SectionFlags::data();
assert!(flags.write);
assert!(flags.alloc);
assert!(!flags.exec);
}
#[test]
fn test_section_flags_to_elf_sh_flags() {
let flags = X86SectionFlags::text();
let shf = flags.to_elf_sh_flags();
assert_eq!(shf, SHF_ALLOC | SHF_EXECINSTR);
}
#[test]
fn test_section_flags_to_coff_characteristics() {
let flags = X86SectionFlags::text();
let chars = flags.to_coff_characteristics();
assert_eq!(
chars,
IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE
);
}
#[test]
fn test_x86_mc_section_creation() {
let sec = X86MCSection::new_text(".text");
assert_eq!(sec.name, ".text");
assert_eq!(sec.fragment_count(), 0);
assert!(sec.is_empty());
}
#[test]
fn test_x86_mc_section_add_fragment() {
let mut sec = X86MCSection::new_text(".text");
sec.add_fragment(X86MCFragment::Data(MCDataFragment::new(vec![0x90])));
assert_eq!(sec.fragment_count(), 1);
assert!(!sec.is_empty());
}
#[test]
fn test_x86_mc_section_fragment_size() {
let mut sec = X86MCSection::new_text(".text");
sec.add_fragment(X86MCFragment::Data(MCDataFragment::new(vec![0x90, 0x90])));
sec.add_fragment(X86MCFragment::Instruction(MCInstFragment::new(vec![0xC3])));
assert_eq!(sec.fragment_size(), 3);
}
#[test]
fn test_x86_mc_section_debug() {
let mut sec = X86MCSection::new_debug(".debug_info");
assert!(sec.is_debug);
}
#[test]
fn test_assembler_creation() {
let asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
assert_eq!(asm.target_triple, "x86_64-unknown-linux-gnu");
assert_eq!(asm.format, X86ObjectFormat::ELF);
assert!(!asm.pic);
}
#[test]
fn test_assembler_section_management() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
let idx = asm.get_or_create_section(".text", X86SectionType::Regular, X86SectionFlags::text());
assert_eq!(asm.find_section(".text"), Some(idx));
let idx2 = asm.get_or_create_section(".text", X86SectionType::Regular, X86SectionFlags::text());
assert_eq!(idx, idx2);
}
#[test]
fn test_assembler_emit_instruction() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.emit_instruction(&[0x90], "nop");
let size = asm.section_size(".text");
assert_eq!(size, 1);
}
#[test]
fn test_assembler_emit_data() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.emit_data(&[0xDE, 0xAD, 0xBE, 0xEF]);
let size = asm.section_size(".data");
assert_eq!(size, 4);
}
#[test]
fn test_assembler_emit_alignment() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.emit_instruction(&[0x90], "nop"); asm.emit_alignment(16, 0x90, 15);
let size = asm.section_size(".text");
assert_eq!(size, 1 + 15);
}
#[test]
fn test_assembler_define_symbol() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.emit_instruction(&[0x90, 0x90], "nop");
asm.define_symbol("my_label", X86SymbolBinding::Global, X86SymbolType::Func);
assert_eq!(asm.symbols.len(), 1);
assert_eq!(asm.symbols[0].name, "my_label");
assert_eq!(asm.symbols[0].binding, X86SymbolBinding::Global);
}
#[test]
fn test_assembler_define_absolute_symbol() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.define_absolute_symbol("CONSTANT", 0x1234);
assert_eq!(asm.symbols[0].value, 0x1234);
assert!(asm.symbols[0].absolute);
}
#[test]
fn test_assembler_define_label() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.define_label("start");
assert!(asm.labels.contains_key("start"));
assert!(asm.labels["start"].defined);
}
#[test]
fn test_assembler_evaluate_expression_constant() {
let asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
let val = asm.evaluate_expression("42").unwrap();
assert_eq!(val, X86ExprValue::Constant(42));
}
#[test]
fn test_assembler_evaluate_expression_hex() {
let asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
let val = asm.evaluate_expression("0xFF").unwrap();
assert_eq!(val, X86ExprValue::Constant(255));
}
#[test]
fn test_assembler_evaluate_expression_symbol() {
let asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
let val = asm.evaluate_expression("func_name").unwrap();
assert_eq!(val, X86ExprValue::Symbol("func_name".into()));
}
#[test]
fn test_assembler_evaluate_expression_symbol_diff() {
let asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
let val = asm.evaluate_expression("end - start").unwrap();
assert_eq!(
val,
X86ExprValue::SymbolDiff("end".into(), "start".into())
);
}
#[test]
fn test_assembler_flatten_section() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.emit_instruction(&[0x90], "nop");
asm.emit_instruction(&[0xC3], "ret");
let section_idx = asm.find_section(".text").unwrap();
let flat = asm.flatten_section(section_idx);
assert_eq!(flat, vec![0x90, 0xC3]);
}
#[test]
fn test_assembler_flatten_all() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.emit_instruction(&[0x90], "nop");
asm.emit_data(&[0x01, 0x02, 0x03]);
let (data, boundaries) = asm.flatten_all();
assert!(!data.is_empty());
assert!(!boundaries.is_empty());
}
#[test]
fn test_assembler_layout() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.emit_instruction(&[0x90], "nop");
asm.emit_alignment(16, 0x90, 15);
asm.emit_instruction(&[0xC3], "ret");
asm.layout();
let idx = asm.find_section(".text").unwrap();
assert!(asm.sections[idx].size > 0);
}
#[test]
fn test_assembler_new_temp_symbol() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
let name1 = asm.new_temp_symbol();
let name2 = asm.new_temp_symbol();
assert!(name1.starts_with(".Ltmp"));
assert_ne!(name1, name2);
}
#[test]
fn test_assembler_new_temp_label() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
let name1 = asm.new_temp_label();
assert!(name1.starts_with(".L"));
}
#[test]
fn test_assembler_reset() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.emit_instruction(&[0x90], "nop");
asm.define_symbol("foo", X86SymbolBinding::Global, X86SymbolType::Func);
asm.reset();
assert!(asm.symbols.is_empty());
let size = asm.section_size(".text");
assert_eq!(size, 0);
}
#[test]
fn test_assembler_pic_mode() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
assert!(!asm.pic);
asm.set_pic(true);
assert!(asm.pic);
}
#[test]
fn test_assembler_relaxation() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
assert!(asm.relaxation);
asm.set_relaxation(false);
assert!(!asm.relaxation);
}
#[test]
fn test_assembler_features() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
asm.add_feature("avx");
assert!(asm.has_feature("avx"));
assert!(!asm.has_feature("sse"));
}
#[test]
fn test_asm_streamer_creation() {
let streamer = X86MCAsmStreamer::new();
assert!(streamer.as_str().contains(".text"));
}
#[test]
fn test_asm_streamer_emit_instruction() {
let mut streamer = X86MCAsmStreamer::new();
streamer.emit_instruction(&[0x90], "nop");
let output = streamer.into_string();
assert!(output.contains("nop"));
assert!(output.contains("90"));
}
#[test]
fn test_asm_streamer_switch_section() {
let mut streamer = X86MCAsmStreamer::new();
streamer.switch_section(".data");
assert_eq!(streamer.current_section, ".data");
}
#[test]
fn test_asm_streamer_emit_data() {
let mut streamer = X86MCAsmStreamer::new();
streamer.emit_data(&[0x01, 0x02, 0x03, 0x04]);
let output = streamer.into_string();
assert!(output.contains(".byte"));
}
#[test]
fn test_asm_streamer_emit_label() {
let mut streamer = X86MCAsmStreamer::new();
streamer.emit_label("main");
let output = streamer.into_string();
assert!(output.contains("main:"));
}
#[test]
fn test_asm_streamer_emit_alignment() {
let mut streamer = X86MCAsmStreamer::new();
streamer.emit_alignment(16, 0x90, 15);
let output = streamer.into_string();
assert!(output.contains(".p2align"));
}
#[test]
fn test_asm_streamer_emit_comment() {
let mut streamer = X86MCAsmStreamer::new();
streamer.emit_comment("This is a comment");
let output = streamer.into_string();
assert!(output.contains("# This is a comment"));
}
#[test]
fn test_asm_streamer_emit_symbol() {
let mut streamer = X86MCAsmStreamer::new();
streamer.emit_symbol("main", X86SymbolBinding::Global, X86SymbolType::Func);
let output = streamer.into_string();
assert!(output.contains(".globl main"));
}
#[test]
fn test_asm_streamer_clear() {
let mut streamer = X86MCAsmStreamer::new();
streamer.emit_instruction(&[0x90], "nop");
streamer.clear();
assert!(!streamer.as_str().contains("nop"));
assert!(streamer.as_str().contains(".text"));
}
#[test]
fn test_asm_streamer_emit_cfi_directives() {
let mut streamer = X86MCAsmStreamer::new();
streamer.emit_cfi_def_cfa(7, 8);
streamer.emit_cfi_offset(16, -8);
streamer.emit_cfi_remember_state();
streamer.emit_cfi_restore_state();
let output = streamer.into_string();
assert!(output.contains(".cfi_def_cfa"));
assert!(output.contains(".cfi_offset"));
assert!(output.contains(".cfi_remember_state"));
assert!(output.contains(".cfi_restore_state"));
}
#[test]
fn test_object_streamer_creation() {
let streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
assert_eq!(streamer.format, X86ObjectFormat::ELF);
assert_eq!(streamer.current_section, ".text");
}
#[test]
fn test_object_streamer_emit_instruction() {
let mut streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
streamer.emit_instruction(&[0x90, 0x90], "nop");
assert_eq!(streamer.offset, 2);
assert_eq!(streamer.section_data.len(), 2);
}
#[test]
fn test_object_streamer_switch_section() {
let mut streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
streamer.emit_instruction(&[0x90], "nop");
streamer.switch_section(".data");
assert_eq!(streamer.current_section, ".data");
assert_eq!(streamer.offset, 0);
}
#[test]
fn test_object_streamer_push_pop_section() {
let mut streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
streamer.emit_instruction(&[0x90], "nop");
streamer.push_section();
streamer.switch_section(".data");
streamer.emit_data(&[0xAA]);
streamer.pop_section();
assert_eq!(streamer.current_section, ".text");
}
#[test]
fn test_object_streamer_emit_alignment() {
let mut streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
streamer.emit_instruction(&[0x90], "nop"); streamer.emit_alignment(16, 0x90, 15);
assert_eq!(streamer.offset, 16);
}
#[test]
fn test_object_streamer_emit_fill() {
let mut streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
streamer.emit_fill(&[0xCC], 10);
assert_eq!(streamer.offset, 10);
}
#[test]
fn test_object_streamer_emit_org() {
let mut streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
streamer.emit_org(0x100, 0x00);
assert_eq!(streamer.offset, 0x100);
}
#[test]
fn test_object_streamer_emit_label() {
let mut streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
streamer.emit_label("test_label");
assert!(streamer.labels.contains_key("test_label"));
}
#[test]
fn test_object_streamer_new_temp_label() {
let mut streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
let lbl = streamer.new_temp_label();
assert!(lbl.starts_with(".Ltmp"));
}
#[test]
fn test_null_streamer_creation() {
let streamer = X86MCNullStreamer::new();
assert_eq!(streamer.instruction_count, 0);
}
#[test]
fn test_null_streamer_counts() {
let mut streamer = X86MCNullStreamer::new();
streamer.emit_instruction(&[0x90, 0x90], "nop");
streamer.emit_instruction(&[0xC3], "ret");
streamer.emit_label("lbl");
assert_eq!(streamer.instruction_count, 2);
assert_eq!(streamer.label_count, 1);
assert_eq!(streamer.byte_count, 3);
}
#[test]
fn test_null_streamer_total_operations() {
let mut streamer = X86MCNullStreamer::new();
streamer.emit_instruction(&[0x90], "nop");
streamer.emit_label("a");
streamer.emit_label("b");
streamer.emit_data(&[0x00]);
assert_eq!(streamer.total_operations(), 4);
}
#[test]
fn test_null_streamer_reset() {
let mut streamer = X86MCNullStreamer::new();
streamer.emit_instruction(&[0x90], "nop");
streamer.reset();
assert_eq!(streamer.instruction_count, 0);
}
#[test]
fn test_dwarf_streamer_creation() {
let streamer = X86MCDwarfStreamer::new();
assert!(streamer.emit_info);
assert!(streamer.emit_line);
}
#[test]
fn test_dwarf_streamer_add_debug_string() {
let mut streamer = X86MCDwarfStreamer::new();
let off1 = streamer.add_debug_string("test_str");
let off2 = streamer.add_debug_string("test_str");
assert_eq!(off1, off2); assert!(!streamer.debug_str.is_empty());
}
#[test]
fn test_dwarf_streamer_begin_cu() {
let mut streamer = X86MCDwarfStreamer::new();
streamer.begin_cu(100, 4);
assert_eq!(streamer.cu_count, 1);
assert!(!streamer.debug_info.is_empty());
}
#[test]
fn test_dwarf_streamer_emit_abbrev() {
let mut streamer = X86MCDwarfStreamer::new();
streamer.emit_dwarf_abbrev(1, 0x11, 1); assert!(!streamer.debug_abbrev.is_empty());
}
#[test]
fn test_dwarf_streamer_emit_attr() {
let mut streamer = X86MCDwarfStreamer::new();
streamer.emit_dwarf_abbrev(1, 0x11, 1);
streamer.emit_dwarf_attr(0x03, 0x08); assert!(!streamer.debug_abbrev.is_empty());
}
#[test]
fn test_dwarf_streamer_end_abbrev() {
let mut streamer = X86MCDwarfStreamer::new();
streamer.end_abbrev();
assert!(!streamer.debug_abbrev.is_empty());
}
#[test]
fn test_dwarf_streamer_emit_cie() {
let mut streamer = X86MCDwarfStreamer::new();
streamer.emit_cie(16, 1, "zR");
assert!(!streamer.debug_frame.is_empty());
}
#[test]
fn test_dwarf_streamer_emit_fde() {
let mut streamer = X86MCDwarfStreamer::new();
streamer.emit_fde(32, 0, 0x1000, 0x50);
assert!(!streamer.debug_frame.is_empty());
}
#[test]
fn test_dwarf_streamer_to_sections() {
let mut streamer = X86MCDwarfStreamer::new();
streamer.begin_cu(50, 4);
streamer.add_debug_string("hello");
let sections = streamer.to_sections();
assert!(!sections.is_empty());
}
#[test]
fn test_record_streamer_creation() {
let streamer = X86MCRecordStreamer::new();
assert!(streamer.is_empty());
}
#[test]
fn test_record_streamer_records_actions() {
let mut streamer = X86MCRecordStreamer::new();
streamer.emit_instruction(&[0x90], "nop");
streamer.emit_label("start");
streamer.emit_comment("test");
assert_eq!(streamer.len(), 3);
}
#[test]
fn test_record_streamer_pause_resume() {
let mut streamer = X86MCRecordStreamer::new();
streamer.emit_instruction(&[0x90], "nop");
streamer.pause();
streamer.emit_instruction(&[0xC3], "ret");
streamer.resume();
streamer.emit_instruction(&[0xCC], "int3");
assert_eq!(streamer.len(), 2); }
#[test]
fn test_record_streamer_clear() {
let mut streamer = X86MCRecordStreamer::new();
streamer.emit_instruction(&[0x90], "nop");
streamer.clear();
assert!(streamer.is_empty());
}
#[test]
fn test_record_streamer_replay_to_asm() {
let asm_streamer = Box::new(X86MCAsmStreamer::new());
let mut record = X86MCRecordStreamer::with_inner(asm_streamer);
record.emit_instruction(&[0x90], "nop");
record.emit_label("main");
record.replay().unwrap();
}
#[test]
fn test_generate_nop_size_0() {
assert_eq!(generate_optimal_nops(0), vec![]);
}
#[test]
fn test_generate_nop_size_1() {
assert_eq!(generate_optimal_nops(1), vec![0x90]);
}
#[test]
fn test_generate_nop_size_2() {
assert_eq!(generate_optimal_nops(2), vec![0x66, 0x90]);
}
#[test]
fn test_generate_nop_size_3() {
assert_eq!(generate_optimal_nops(3), vec![0x0F, 0x1F, 0x00]);
}
#[test]
fn test_generate_nop_size_4() {
assert_eq!(
generate_optimal_nops(4),
vec![0x0F, 0x1F, 0x40, 0x00]
);
}
#[test]
fn test_generate_nop_all_sizes_are_correct_length() {
for size in 1..=15 {
let nops = generate_optimal_nops(size);
assert_eq!(
nops.len(),
size,
"NOP size {} generated {} bytes",
size,
nops.len()
);
}
}
#[test]
fn test_generate_nop_large_size() {
let nops = generate_optimal_nops(50);
assert_eq!(nops.len(), 50);
}
#[test]
fn test_generate_x86_nop() {
let nop = generate_x86_nop(15);
assert_eq!(nop.len(), 15);
}
#[test]
fn test_asm_backend_creation() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert!(backend.is_64bit);
assert_eq!(backend.target_name(), "x86_64");
}
#[test]
fn test_asm_backend_32bit() {
let backend = X86MCAsmBackend::new("i386-unknown-linux-gnu");
assert!(!backend.is_64bit);
assert_eq!(backend.target_name(), "i386");
}
#[test]
fn test_asm_backend_encode_fixup_8bit() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
let mut bytes = vec![0x00, 0x00, 0x00];
backend.encode_fixup(&mut bytes, 1, X86FixupKind::Rel8, 0x42);
assert_eq!(bytes[1], 0x42);
}
#[test]
fn test_asm_backend_encode_fixup_32bit() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
let mut bytes = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
backend.encode_fixup(&mut bytes, 1, X86FixupKind::Rel32, 0x12345678);
let val = i32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
assert_eq!(val, 0x12345678);
}
#[test]
fn test_asm_backend_encode_fixup_64bit() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
let mut bytes = vec![0; 10];
backend.encode_fixup(&mut bytes, 1, X86FixupKind::Abs64, 0xDEADBEEF_CAFEBABE_u64 as i64);
let val = u64::from_le_bytes([
bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8],
]);
assert_eq!(val, 0xDEADBEEF_CAFEBABE);
}
#[test]
fn test_asm_backend_can_relax_jcc() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert!(backend.can_relax("je", 2));
assert!(backend.can_relax("JE", 2));
assert!(!backend.can_relax("je", 7)); }
#[test]
fn test_asm_backend_can_relax_jmp() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert!(backend.can_relax("jmp", 2));
assert!(!backend.can_relax("jmp", 6)); }
#[test]
fn test_asm_backend_can_relax_loop() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert!(!backend.can_relax("loop", 2));
}
#[test]
fn test_asm_backend_get_min_size() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert_eq!(backend.get_min_size("je"), 2);
assert_eq!(backend.get_min_size("jmp"), 2);
}
#[test]
fn test_asm_backend_get_max_size() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert_eq!(backend.get_max_size("je"), 6);
assert_eq!(backend.get_max_size("jmp"), 5);
}
#[test]
fn test_asm_backend_get_relaxed_size_short() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert_eq!(backend.get_relaxed_size("je", 100), 2);
}
#[test]
fn test_asm_backend_get_relaxed_size_near() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert_eq!(backend.get_relaxed_size("je", 1000), 6);
}
#[test]
fn test_asm_backend_generate_nop() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
let nop = backend.generate_nop(5);
assert_eq!(nop.len(), 5);
}
#[test]
fn test_asm_backend_compute_alignment_padding() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert_eq!(backend.compute_alignment_padding(0, 16, 15), 0);
assert_eq!(backend.compute_alignment_padding(1, 16, 15), 15);
assert_eq!(backend.compute_alignment_padding(17, 16, 15), 15);
}
#[test]
fn test_asm_backend_compute_alignment_nops() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
let nops = backend.compute_alignment_nops(5, 16, 15);
assert_eq!(nops.len(), 11);
}
#[test]
fn test_asm_backend_is_fixup_kind_valid() {
let backend64 = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
let backend32 = X86MCAsmBackend::new("i386-unknown-linux-gnu");
assert!(backend64.is_fixup_kind_valid(X86FixupKind::RipRel32));
assert!(backend64.is_fixup_kind_valid(X86FixupKind::Abs32));
}
#[test]
fn test_asm_backend_features() {
let mut backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
backend.add_feature("avx512");
assert!(backend.has_feature("avx512"));
assert!(!backend.has_feature("sse4.2"));
}
#[test]
fn test_asm_backend_create_object_writer() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
let writer = backend.create_object_writer();
assert_eq!(writer.format, X86ObjectFormat::ELF);
}
#[test]
fn test_asm_backend_pc_relative_adjustment() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
assert_eq!(backend.get_pc_relative_adjustment(X86FixupKind::Rel32), 4);
assert_eq!(backend.get_pc_relative_adjustment(X86FixupKind::Abs32), 0);
}
#[test]
fn test_elf_writer_creation() {
let writer = X86ELFObjectWriter::new(true);
assert!(writer.is_64bit);
assert_eq!(writer.machine, EM_X86_64);
}
#[test]
fn test_elf_writer_32bit() {
let writer = X86ELFObjectWriter::new(false);
assert!(!writer.is_64bit);
assert_eq!(writer.machine, EM_386);
}
#[test]
fn test_elf_writer_write_header() {
let mut writer = X86ELFObjectWriter::new(true);
writer.write_elf_header(0, 0, 0x40, 0, 64, 0, 0, 64, 3, 2);
assert!(writer.data.len() >= 64);
assert_eq!(&writer.data[0..4], &ELF_MAGIC);
assert_eq!(writer.data[4], ELF_CLASS_64);
assert_eq!(writer.data[5], ELF_DATA_LSB);
}
#[test]
fn test_elf_writer_write_section_header() {
let mut writer = X86ELFObjectWriter::new(true);
writer.write_section_header(1, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, 0x100, 0x50, 0, 0, 16, 0);
assert!(writer.data.len() >= 64);
}
#[test]
fn test_elf_writer_write_symbol_entry() {
let mut writer = X86ELFObjectWriter::new(true);
writer.write_symbol_entry(0, 0x12, 0, 1, 0x1000, 0x20);
assert_eq!(writer.data.len(), 24);
}
#[test]
fn test_elf_writer_write_rela_entry() {
let mut writer = X86ELFObjectWriter::new(true);
writer.write_rela_entry(0x10, 0x400000001, -4);
assert_eq!(writer.data.len(), 24);
}
#[test]
fn test_elf_writer_build_string_table() {
let (data, offsets) =
X86ELFObjectWriter::build_string_table(&["foo", "bar", "baz"]);
assert!(data.len() > 0);
assert_eq!(data[0], 0); assert!(offsets.contains_key("foo"));
assert!(offsets.contains_key("bar"));
}
#[test]
fn test_elf_writer_build_shstrtab() {
let (data, offsets) =
X86ELFObjectWriter::build_shstrtab(&[".text", ".data", ".bss"]);
assert!(offsets.contains_key(".text"));
}
#[test]
fn test_coff_writer_creation() {
let writer = X86COFFObjectWriter::new(true);
assert_eq!(writer.machine, IMAGE_FILE_MACHINE_AMD64);
assert_eq!(writer.section_count, 0);
}
#[test]
fn test_coff_writer_32bit() {
let writer = X86COFFObjectWriter::new(false);
assert_eq!(writer.machine, IMAGE_FILE_MACHINE_I386);
}
#[test]
fn test_coff_writer_write_header() {
let mut writer = X86COFFObjectWriter::new(true);
writer.write_coff_header(IMAGE_FILE_LINE_NUMS_STRIPPED, 0, 0);
assert_eq!(writer.data.len(), 20);
}
#[test]
fn test_coff_writer_pad_section_name() {
let name = X86COFFObjectWriter::pad_section_name(".text");
assert_eq!(name.len(), 8);
assert_eq!(&name[..5], b".text");
assert_eq!(name[5..], [0, 0, 0]);
}
#[test]
fn test_coff_writer_write_section_header() {
let mut writer = X86COFFObjectWriter::new(true);
let name = X86COFFObjectWriter::pad_section_name(".text");
writer.write_section_header(&name, 0, 0, 100, 0x200, 0, 0, 0, 0, IMAGE_SCN_CNT_CODE);
assert_eq!(writer.data.len(), 40);
}
#[test]
fn test_coff_writer_write_symbol() {
let mut writer = X86COFFObjectWriter::new(true);
let name = X86COFFObjectWriter::pad_section_name("test");
writer.write_symbol_entry(&name, 0, 1, 0, IMAGE_SYM_CLASS_EXTERNAL, 0);
assert_eq!(writer.data.len(), 18);
}
#[test]
fn test_coff_writer_write_relocation() {
let mut writer = X86COFFObjectWriter::new(true);
writer.write_relocation_entry(0x10, 2, IMAGE_REL_AMD64_REL32);
assert_eq!(writer.data.len(), 10);
}
#[test]
fn test_macho_writer_creation() {
let writer = X86MachoObjectWriter::new(true);
assert_eq!(writer.cpu_type, CPU_TYPE_X86_64);
}
#[test]
fn test_macho_writer_write_header() {
let mut writer = X86MachoObjectWriter::new(true);
writer.write_macho_header();
assert_eq!(writer.data.len(), 32);
assert_eq!(&writer.data[0..4], &MH_MAGIC_64.to_le_bytes());
}
#[test]
fn test_macho_writer_write_segment_command() {
let mut writer = X86MachoObjectWriter::new(true);
let name = X86MachoObjectWriter::pad_name_16("__TEXT");
writer.write_segment_command(&name, 0, 0x1000, 0, 0x1000, VM_PROT_READ | VM_PROT_EXECUTE, VM_PROT_READ | VM_PROT_EXECUTE, 1, 0);
assert_eq!(writer.data.len(), 152);
}
#[test]
fn test_macho_writer_write_section_header() {
let mut writer = X86MachoObjectWriter::new(true);
let sname = X86MachoObjectWriter::pad_name_16("__text");
let segname = X86MachoObjectWriter::pad_name_16("__TEXT");
writer.write_section_header(
&sname, &segname, 0, 0x100, 0x200, 4, 0, 0,
S_REGULAR | S_ATTR_PURE_INSTRUCTIONS,
0, 0, 0,
);
assert_eq!(writer.data.len(), 80);
}
#[test]
fn test_macho_writer_write_nlist64() {
let mut writer = X86MachoObjectWriter::new(true);
writer.write_nlist64(5, N_SECT | N_EXT, 1, 0, 0x1000);
assert_eq!(writer.data.len(), 16);
}
#[test]
fn test_macho_writer_write_relocation() {
let mut writer = X86MachoObjectWriter::new(true);
writer.write_relocation(0x10, 3, false, 3, true, X86_64_RELOC_UNSIGNED);
assert_eq!(writer.data.len(), 8);
}
#[test]
fn test_macho_writer_write_string_table() {
let mut writer = X86MachoObjectWriter::new(true);
writer.write_string_table(&["main", "_start", "printf"]);
assert_eq!(writer.data[0], 0);
assert_eq!(writer.data[1], 0);
}
#[test]
fn test_macho_writer_pad_name_16() {
let name = X86MachoObjectWriter::pad_name_16("__text");
assert_eq!(name.len(), 16);
assert_eq!(&name[..6], b"__text");
}
#[test]
fn test_unified_writer_creation() {
let writer = X86MCObjectWriter::new(X86ObjectFormat::ELF, true);
assert_eq!(writer.format, X86ObjectFormat::ELF);
assert!(writer.elf_writer.is_some());
assert!(writer.coff_writer.is_none());
}
#[test]
fn test_unified_writer_add_section() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::ELF, true);
writer.add_section(".text", vec![0x90], X86SectionFlags::text(), X86SectionType::Regular);
assert_eq!(writer.sections.len(), 1);
}
#[test]
fn test_unified_writer_add_symbol() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::ELF, true);
writer.add_symbol(X86MCSymbol::new_global("main", 1, 0x1000));
assert_eq!(writer.symbols.len(), 1);
}
#[test]
fn test_unified_writer_add_relocation() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::ELF, true);
let fixup = X86Fixup::new(0, X86FixupKind::Rel32, X86FixupSource::Symbol("ext".into()));
writer.add_relocation(".text", fixup);
assert_eq!(writer.relocations.len(), 1);
}
#[test]
fn test_unified_writer_write_elf() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::ELF, true);
writer.add_section(".text", vec![0x90, 0xC3], X86SectionFlags::text(), X86SectionType::Regular);
writer.add_symbol(X86MCSymbol::new_global("main", 1, 0));
let data = writer.write();
assert!(!data.is_empty());
assert_eq!(&data[0..4], &ELF_MAGIC);
}
#[test]
fn test_unified_writer_write_coff() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::COFF, true);
writer.add_section(".text", vec![0x90], X86SectionFlags::text(), X86SectionType::Regular);
let data = writer.write();
assert!(!data.is_empty());
}
#[test]
fn test_unified_writer_write_macho() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::MachO, true);
writer.add_section("__text", vec![0x90], X86SectionFlags::text(), X86SectionType::Regular);
let data = writer.write();
assert!(!data.is_empty());
assert_eq!(&data[0..4], &MH_MAGIC_64.to_le_bytes());
}
#[test]
fn test_unified_writer_write_binary() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::Binary, true);
writer.add_section(".text", vec![0x90, 0xC3], X86SectionFlags::text(), X86SectionType::Regular);
let data = writer.write();
assert_eq!(data, vec![0x90, 0xC3]);
}
#[test]
fn test_mc_layer_creation() {
let layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
assert!(layer.is_initialized());
assert!(layer.is_64bit());
}
#[test]
fn test_mc_layer_with_format() {
let layer = X86MCLayerDeep::with_format("x86_64-pc-windows-msvc", X86ObjectFormat::COFF);
assert_eq!(layer.format, X86ObjectFormat::COFF);
}
#[test]
fn test_mc_layer_set_pic() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.set_pic(true);
assert!(layer.assembler.pic);
}
#[test]
fn test_mc_layer_set_relaxation() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.set_relaxation(false);
assert!(!layer.assembler.relaxation);
}
#[test]
fn test_mc_layer_add_feature() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.add_feature("avx2");
assert!(layer.assembler.has_feature("avx2"));
assert!(layer.backend.has_feature("avx2"));
}
#[test]
fn test_mc_layer_reset() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.assembler.emit_instruction(&[0x90], "nop");
layer.reset();
assert_eq!(layer.assembler.section_size(".text"), 0);
}
#[test]
fn test_mc_layer_emit_assembly() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.assembler.emit_instruction(&[0x90], "nop");
layer.assembler.emit_instruction(&[0xC3], "ret");
let asm = layer.emit_assembly();
assert!(asm.contains("nop"));
assert!(asm.contains("ret"));
}
#[test]
fn test_mc_layer_emit_object() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.assembler.emit_instruction(&[0x90], "nop");
let data = layer.emit_object();
assert!(!data.is_empty());
}
#[test]
fn test_mc_layer_default() {
let layer = X86MCLayerDeep::default();
assert_eq!(layer.target_triple(), "x86_64-unknown-linux-gnu");
}
#[test]
fn test_integration_emit_nop_function() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.assembler.define_label("my_func");
layer.assembler.emit_instruction(&[0x55], "push rbp"); layer
.assembler
.emit_instruction(&[0x48, 0x89, 0xE5], "mov rbp, rsp"); layer.assembler.emit_instruction(&[0x90], "nop");
layer
.assembler
.emit_instruction(&[0x48, 0x89, 0xEC], "mov rsp, rbp"); layer.assembler.emit_instruction(&[0x5D], "pop rbp"); layer.assembler.emit_instruction(&[0xC3], "ret");
let asm = layer.emit_assembly();
assert!(asm.contains("my_func:"));
assert!(asm.contains("push rbp"));
assert!(asm.contains("mov rbp, rsp"));
assert!(asm.contains("ret"));
let obj = layer.emit_object();
assert!(!obj.is_empty());
}
#[test]
fn test_integration_multiple_sections() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.assembler.emit_instruction(&[0x90], "nop");
layer.assembler.switch_section(".data");
layer.assembler.emit_data(&[0x01, 0x02, 0x03, 0x04]);
layer.assembler.switch_section(".rodata");
layer.assembler.emit_data(&[0xDE, 0xAD, 0xBE, 0xEF]);
assert!(layer.assembler.find_section(".text").is_some());
assert!(layer.assembler.find_section(".data").is_some());
assert!(layer.assembler.find_section(".rodata").is_some());
}
#[test]
fn test_integration_define_global_symbols() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.assembler.emit_instruction(&[0xC3], "ret");
layer.assembler.define_symbol(
"exported_func",
X86SymbolBinding::Global,
X86SymbolType::Func,
);
assert_eq!(layer.assembler.symbols.len(), 1);
assert_eq!(layer.assembler.symbols[0].binding, X86SymbolBinding::Global);
}
#[test]
fn test_integration_align_between_functions() {
let mut layer = X86MCLayerDeep::new("x86_64-unknown-linux-gnu");
layer.assembler.define_label("func1");
layer.assembler.emit_instruction(&[0x90], "nop");
layer.assembler.emit_instruction(&[0xC3], "ret");
layer.assembler.emit_alignment(16, 0x90, 15);
layer.assembler.define_label("func2");
layer.assembler.emit_instruction(&[0x90], "nop");
layer.assembler.emit_instruction(&[0xC3], "ret");
let section_idx = layer.assembler.find_section(".text").unwrap();
let size = layer.assembler.compute_section_size(section_idx);
assert_eq!(size, 18);
}
#[test]
fn test_integration_dwarf_debug_emission() {
let mut dwarf = X86MCDwarfStreamer::new();
dwarf.begin_cu(0x50, 4);
dwarf.emit_dwarf_abbrev(1, 0x11, 1); dwarf.emit_dwarf_attr(0x03, 0x08); dwarf.end_abbrev();
dwarf.add_debug_string("test.c");
let sections = dwarf.to_sections();
assert!(sections.iter().any(|(n, _)| n == ".debug_info"));
assert!(sections.iter().any(|(n, _)| n == ".debug_abbrev"));
}
#[test]
fn test_integration_record_and_replay() {
let asm = Box::new(X86MCAsmStreamer::new());
let mut record = X86MCRecordStreamer::with_inner(asm);
record.emit_instruction(&[0x55], "push rbp");
record.emit_instruction(&[0x48, 0x89, 0xE5], "mov rbp, rsp");
record.emit_label("body");
record.emit_instruction(&[0x90], "nop");
assert_eq!(record.len(), 4);
record.replay().unwrap();
}
#[test]
fn test_integration_null_streamer_timing() {
let mut null = X86MCNullStreamer::new();
for _ in 0..1000 {
null.emit_instruction(&[0x90], "nop");
}
assert_eq!(null.instruction_count, 1000);
assert_eq!(null.byte_count, 1000);
}
#[test]
fn test_integration_elf_full_emission() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::ELF, true);
writer.add_section(".text", vec![0x55, 0x48, 0x89, 0xE5, 0x90, 0x5D, 0xC3], X86SectionFlags::text(), X86SectionType::Regular);
writer.add_symbol(X86MCSymbol::new_global("main", 1, 0).with_type(X86SymbolType::Func));
let elf = writer.write();
assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
assert_eq!(elf[4], 2); assert_eq!(elf[5], 1); }
#[test]
fn test_integration_macho_full_emission() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::MachO, true);
writer.add_section("__text", vec![0x90, 0xC3], X86SectionFlags::text(), X86SectionType::Regular);
writer.add_symbol(X86MCSymbol::new_global("_main", 1, 0));
let macho = writer.write();
assert_eq!(&macho[0..4], &MH_MAGIC_64.to_le_bytes());
}
#[test]
fn test_integration_coff_full_emission() {
let mut writer = X86MCObjectWriter::new(X86ObjectFormat::COFF, true);
writer.add_section(".text", vec![0x90], X86SectionFlags::text(), X86SectionType::Regular);
let coff = writer.write();
assert!(!coff.is_empty());
let machine = u16::from_le_bytes([coff[0], coff[1]]);
assert_eq!(machine, IMAGE_FILE_MACHINE_AMD64);
}
#[test]
fn test_integration_fixup_encoding_roundtrip() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
let mut bytes = vec![0xE8, 0x00, 0x00, 0x00, 0x00]; backend.encode_fixup(&mut bytes, 1, X86FixupKind::Rel32, -4);
let encoded = i32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
assert_eq!(encoded, -4);
}
#[test]
fn test_integration_relaxable_branch_roundtrip() {
let mut frag = MCRelaxableFragment::new_branch(
vec![0xEB, 0x00], "target".into(),
2,
5,
);
assert_eq!(frag.current_size, 2);
assert_eq!(frag.min_size, 2);
assert_eq!(frag.max_size, 5);
frag.relax_to(5, vec![0xE9, 0x00, 0x00, 0x00, 0x00]);
assert_eq!(frag.current_size, 5);
assert!(frag.relaxed);
}
#[test]
fn test_integration_section_flags_all_formats() {
let flags = X86SectionFlags::text();
let elf_flags = flags.to_elf_sh_flags();
let coff_flags = flags.to_coff_characteristics();
assert_ne!(elf_flags, 0);
assert_ne!(coff_flags, 0);
}
#[test]
fn test_integration_fragment_roundtrip_all_types() {
let fragments: Vec<X86MCFragment> = vec![
X86MCFragment::Data(MCDataFragment::new(vec![0x01, 0x02])),
X86MCFragment::Instruction(MCInstFragment::new(vec![0x90])),
{
let mut a = MCAlignFragment::new(16, 0x90, 3);
a.compute_fill(5);
X86MCFragment::Align(a)
},
X86MCFragment::Fill(MCFillFragment::new(vec![0xCC], 4)),
X86MCFragment::Org(MCOrgFragment::new(0x100, 0x00)),
X86MCFragment::DwarfFrame(MCDwarfFrameFragment::new(vec![0x00, 0x10])),
X86MCFragment::DwarfLine(MCDwarfLineFragment::new(vec![0x01, 0x02])),
X86MCFragment::CompactUnwind(MCCompactUnwindFragment::new(vec![0x10; 16])),
X86MCFragment::CVDefRange(MCCVDefRangeFragment::new(vec![0x11, 0x22])),
X86MCFragment::Relaxable(MCRelaxableFragment::new(vec![0xEB, 0x05], 5)),
];
for frag in &fragments {
let bytes = frag.get_bytes();
let size = frag.size();
assert_eq!(bytes.len(), size, "Mismatch for {}", frag.kind_name());
}
}
#[test]
fn test_integration_uleb128_encoding() {
let mut buf = Vec::new();
write_uleb128(&mut buf, 0);
assert_eq!(buf, vec![0]);
let mut buf = Vec::new();
write_uleb128(&mut buf, 127);
assert_eq!(buf, vec![127]);
let mut buf = Vec::new();
write_uleb128(&mut buf, 128);
assert_eq!(buf, vec![0x80, 0x01]);
let mut buf = Vec::new();
write_uleb128(&mut buf, 624485);
assert_eq!(buf, vec![0xE5, 0x8E, 0x26]);
}
#[test]
fn test_integration_string_table_deduplication() {
let (data, offsets) =
X86ELFObjectWriter::build_string_table(&["foo", "foo", "bar"]);
assert_eq!(offsets.get("foo"), offsets.get("foo"));
let mut null_count = 0;
for &b in &data {
if b == 0 {
null_count += 1;
}
}
assert_eq!(null_count, 3);
}
#[test]
fn test_integration_assembler_many_instructions() {
let mut asm = X86MCAssembler::new("x86_64-unknown-linux-gnu");
for i in 0..500 {
asm.emit_instruction(&[0x90], "nop");
if i % 100 == 99 {
asm.define_label(&format!("L{}", i / 100));
}
}
let size = asm.section_size(".text");
assert_eq!(size, 500);
assert_eq!(asm.labels.len(), 5);
}
#[test]
fn test_integration_backend_fixup_comprehensive() {
let backend = X86MCAsmBackend::new("x86_64-unknown-linux-gnu");
let kinds = vec![
X86FixupKind::Rel8,
X86FixupKind::Rel32,
X86FixupKind::Abs32,
X86FixupKind::Abs64,
X86FixupKind::RipRel32,
X86FixupKind::GotPcRel,
X86FixupKind::Plt32,
];
for kind in kinds {
let size = backend.get_fixup_size(kind);
assert!(size > 0, "Fixup kind {:?} has zero size", kind);
if backend.is_64bit {
assert!(
backend.is_fixup_kind_valid(kind),
"Fixup kind {:?} should be valid for 64-bit",
kind
);
}
}
}
#[test]
fn test_integration_fragment_fixups_propagation() {
let mut inst_frag = MCInstFragment::new(vec![0xE8, 0x00, 0x00, 0x00, 0x00]);
inst_frag.add_fixup(X86Fixup::new(
1,
X86FixupKind::Rel32,
X86FixupSource::Symbol("printf".into()),
));
let frag = X86MCFragment::Instruction(inst_frag);
assert_eq!(frag.fixups().len(), 1);
assert_eq!(frag.fixups()[0].kind, X86FixupKind::Rel32);
}
#[test]
fn test_integration_object_streamer_fragments() {
let mut streamer = X86MCObjectStreamer::new(X86ObjectFormat::ELF);
streamer.emit_fragment(X86MCFragment::Instruction(MCInstFragment::new(
vec![0x90, 0x90],
)));
streamer.emit_fragment(X86MCFragment::Fill(MCFillFragment::new(
vec![0x00],
8,
)));
streamer.finish();
assert_eq!(streamer.offset, 10);
}
#[test]
fn test_integration_macho_segment_write() {
let mut writer = X86MachoObjectWriter::new(true);
writer.ncmds = 1;
writer.sizeofcmds = 152;
writer.nsects = 1;
writer.write_macho_header();
let segname = X86MachoObjectWriter::pad_name_16("__TEXT");
writer.write_segment_command(
&segname,
0,
0x1000,
0,
0x1000,
VM_PROT_READ | VM_PROT_EXECUTE,
VM_PROT_READ | VM_PROT_EXECUTE,
1,
0,
);
let sname = X86MachoObjectWriter::pad_name_16("__text");
writer.write_section_header(
&sname, &segname, 0, 0x100, 0x218, 4, 0, 0,
S_REGULAR | S_ATTR_PURE_INSTRUCTIONS,
0, 0, 0,
);
assert_eq!(writer.data.len(), 32 + 152 + 80);
}
#[test]
fn test_addressing_mode_pointer_size() {
assert_eq!(X86AddrMode::Mode16.pointer_size(), 2);
assert_eq!(X86AddrMode::Mode32.pointer_size(), 4);
assert_eq!(X86AddrMode::Mode64.pointer_size(), 8);
}
#[test]
fn test_section_type_is_debug() {
assert!(X86SectionType::Debug.is_debug());
assert!(!X86SectionType::Regular.is_debug());
}
#[test]
fn test_conditional_jump_detection() {
assert!(is_conditional_jump("je"));
assert!(is_conditional_jump("JNE"));
assert!(is_conditional_jump("jz"));
assert!(!is_conditional_jump("jmp"));
assert!(!is_conditional_jump("call"));
}
#[test]
fn test_helper_push_functions() {
let mut buf = Vec::new();
push_u16_le(&mut buf, 0x1234);
assert_eq!(buf, vec![0x34, 0x12]);
push_u32_le(&mut buf, 0xDEADBEEF);
assert_eq!(&buf[2..6], &[0xEF, 0xBE, 0xAD, 0xDE]);
push_u64_le(&mut buf, 0x0102030405060708);
assert_eq!(&buf[6..14], &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
}
#[test]
fn test_align_to_helpers() {
assert_eq!(align_to(0, 16), 0);
assert_eq!(align_to(1, 16), 16);
assert_eq!(align_to(15, 16), 16);
assert_eq!(align_to(16, 16), 16);
assert_eq!(align_to(17, 16), 32);
}
#[test]
fn test_symbol_binding_as_str() {
assert_eq!(X86SymbolBinding::Local.as_str(), "local");
assert_eq!(X86SymbolBinding::Global.as_str(), "global");
assert_eq!(X86SymbolBinding::Weak.as_str(), "weak");
}
#[test]
fn test_symbol_type_as_str() {
assert_eq!(X86SymbolType::Func.as_str(), "FUNC");
assert_eq!(X86SymbolType::Object.as_str(), "OBJECT");
assert_eq!(X86SymbolType::NoType.as_str(), "NOTYPE");
}
#[test]
fn test_symbol_visibility_as_str() {
assert_eq!(X86SymbolVisibility::Default.as_str(), "default");
assert_eq!(X86SymbolVisibility::Hidden.as_str(), "hidden");
}
#[test]
fn test_fixup_kind_name() {
assert_eq!(X86FixupKind::Rel32.name(), "Rel32");
assert_eq!(X86FixupKind::GotPcRel.name(), "GotPcRel");
}
#[test]
fn test_object_format_display() {
assert_eq!(format!("{}", X86ObjectFormat::ELF), "ELF");
assert_eq!(format!("{}", X86ObjectFormat::COFF), "COFF");
assert_eq!(format!("{}", X86ObjectFormat::MachO), "Mach-O");
}
#[test]
fn test_fixup_kind_display() {
assert_eq!(format!("{}", X86FixupKind::Rel32), "Rel32");
assert_eq!(format!("{}", X86FixupKind::Abs64), "Abs64");
assert_eq!(format!("{}", X86FixupKind::None), "None");
}
#[test]
fn test_expr_value_display() {
assert_eq!(format!("{}", X86ExprValue::Constant(42)), "42");
assert_eq!(format!("{}", X86ExprValue::Symbol("foo".into())), "foo");
assert_eq!(
format!("{}", X86ExprValue::SymbolOffset("bar".into(), 8)),
"bar+8"
);
}
}