use crate::elf::elf_relocations::{
R_X86_64_16, R_X86_64_32, R_X86_64_32S, R_X86_64_64, R_X86_64_8, R_X86_64_COPY,
R_X86_64_GLOB_DAT, R_X86_64_GOT32, R_X86_64_GOTOFF64, R_X86_64_GOTPC32,
R_X86_64_GOTPC32_TLSDESC, R_X86_64_GOTPCREL, R_X86_64_JUMP_SLOT, R_X86_64_NONE, R_X86_64_PC16,
R_X86_64_PC32, R_X86_64_PC64, R_X86_64_PC8, R_X86_64_PLT32, R_X86_64_RELATIVE, R_X86_64_SIZE32,
R_X86_64_SIZE64, R_X86_64_TLSDESC, R_X86_64_TLSDESC_CALL,
};
use crate::elf::elf_types::{
elf_r_info, elf_r_sym, elf_r_type, st_bind, st_info, st_type, st_visibility, Elf64Ehdr,
Elf64Rel, Elf64Rela, Elf64Shdr, Elf64Sym, ElfClass, ElfEndian, ElfMachine, ElfOsAbi, EI_CLASS,
EI_DATA, EI_OSABI, EI_VERSION, ELF_MAGIC, EM_X86_64, ET_REL, EV_CURRENT, SECTION_NAME_BSS,
SECTION_NAME_DATA, SECTION_NAME_RODATA, SECTION_NAME_SHSTRTAB, SECTION_NAME_STRTAB,
SECTION_NAME_SYMTAB, SECTION_NAME_TEXT, SHF_ALLOC, SHF_EXECINSTR, SHF_MERGE, SHF_STRINGS,
SHF_WRITE, SHT_NOBITS, SHT_NOTE, SHT_NULL, SHT_PROGBITS, SHT_REL, SHT_RELA, SHT_STRTAB,
SHT_SYMTAB, STB_GLOBAL, STB_LOCAL, STB_WEAK, STT_FILE, STT_FUNC, STT_NOTYPE, STT_OBJECT,
STT_SECTION, STV_DEFAULT, STV_HIDDEN, STV_PROTECTED,
};
use crate::mc_assembler::{
apply_relocation, elf_relocation_kind as asm_elf_reloc_kind, write_relocation_value,
x86_64_relocation_kind, FixupKind as AsmFixupKind, MCAssembler, MCFixup as AsmMCFixup,
MCFragment as AsmMCFragment, MCSection as AsmMCSection, RelocationKind, RelocationResult,
SectionType as AsmSectionType,
};
use crate::mc_inst::{
AsmDirective, AsmStatement, AsmToken, FeatureBits, MCAlignFragment, MCAsmParser, MCBinaryExpr,
MCBoundaryAlignFragment, MCCompactEncodedInstFragment, MCConstantExpr, MCContext,
MCDataFragment, MCDwarfCallFrameFragment, MCDwarfLineAddrFragment, MCExpr, MCFillFragment,
MCFixup, MCFixupKind, MCFragment, MCInst, MCInstBuilder, MCInstFlags, MCInstrDesc, MCInstrInfo,
MCLEBFragment, MCLabel, MCObjectWriter as MCObjectWriterTrait, MCOperand, MCOperandInfo,
MCOperandType, MCOrgFragment, MCRegisterClass, MCRegisterDesc, MCRegisterInfo,
MCRelaxableFragment, MCRelocationEntry, MCSection, MCSectionCOFF, MCSectionELF, MCSectionMachO,
MCSectionType, MCSectionWasm, MCSubtargetInfo, MCSymbol, MCSymbolRefExpr,
MCSymbolRefVariantKind, MCTargetExpr, MCUnaryExpr, MCValue, MatchResult, NullObjectWriter,
ParsedOperand,
};
use crate::mc_streamer::{
emit_assembly_header, emit_function_epilogue, emit_function_prologue, encode_x86_instruction,
x86_64_relocations as streamer_relocations, x86_mnemonic, x86_opcodes, x86_reg_name,
AssemblerFlag, LineEntry, MCAsmStreamer, MCContext as MCStreamerContext,
MCFixup as StreamerMCFixup, MCFragment as StreamerMCFragment, MCNullStreamer, MCObjectStreamer,
MCStreamer, MCSymbol as StreamerMCSymbol, SectionFlags, SymbolBinding, SymbolType,
SymbolVisibility,
};
use crate::mc_target::{
MCAsmBackend, MCCodeEmitter, MCInstPrinter, MCInstrAnalysis, MCObjectWriter,
MCRegisterInfo as MCRegisterInfoTrait, MCSubtargetInfo as MCSubtargetInfoTrait, MCTarget,
MCTargetRegistry, ObjectFormat, X86MCAsmBackend as BaseX86MCAsmBackend,
X86MCCodeEmitter as BaseX86MCCodeEmitter, X86MCObjectWriter as BaseX86MCObjectWriter,
X86MCTarget,
};
use crate::object_writer::{
coff_const, macho_const, write_leb128_u32, ObjectSection, ObjectSymbol, ObjectWriter,
RelocationEntry, RelocationType as OwRelocType, SectionFlags as OwSectionFlags,
SymbolBinding as OwSymbolBinding, SymbolType as OwSymbolType,
};
use crate::x86::x86_asm_printer::{AsmSyntax, X86AsmPrinter};
use crate::x86::x86_full_mc_decoder::X86FullMCDecoder;
use crate::x86::x86_full_mc_encoder::X86FullMCEncoder;
use crate::x86::x86_instr_info::{
OperandType, X86InstrDesc, X86InstrInfo, X86MemOperand, X86Opcode, X86Operand, X86SchedInfo,
};
use crate::x86::x86_mc_decoder::X86MCDecoder;
use crate::x86::x86_mc_encoder::{mod_field, prefixes as mc_prefixes, X86MCEncoder, X86Mode};
use crate::x86::x86_register_info::{
RegClass, X86Reg, X86RegisterInfo, AH, AL, AX, BH, BL, BPL, BX, CH, CL, CS, CX, DH, DIL, DL,
DS, DX, EAX, EBP, EBX, ECX, EDI, EDX, ES, ESI, ESP, FS, GS, K0, K1, K2, K3, K4, K5, K6, K7,
R10, R10B, R10D, R10W, R11, R11B, R11D, R11W, R12, R12B, R12D, R12W, R13, R13B, R13D, R13W,
R14, R14B, R14D, R14W, R15, R15B, R15D, R15W, R8, R8B, R8D, R8W, R9, R9B, R9D, R9W, RAX, RBP,
RBX, RCX, RDI, RDX, RIP, RSI, RSP, SIL, SPL, SS, TOTAL_REG_COUNT, XMM0, XMM1, XMM10, XMM11,
XMM12, XMM13, XMM14, XMM15, XMM16, XMM17, XMM18, XMM19, XMM2, XMM20, XMM21, XMM22, XMM23,
XMM24, XMM25, XMM26, XMM27, XMM28, XMM29, XMM3, XMM30, XMM31, XMM4, XMM5, XMM6, XMM7, XMM8,
XMM9, YMM0, YMM1, YMM10, YMM11, YMM12, YMM13, YMM14, YMM15, YMM16, YMM17, YMM18, YMM19, YMM2,
YMM20, YMM21, YMM22, YMM23, YMM24, YMM25, YMM26, YMM27, YMM28, YMM29, YMM3, YMM30, YMM31, YMM4,
YMM5, YMM6, YMM7, YMM8, YMM9, ZMM0, ZMM1, ZMM10, ZMM11, ZMM12, ZMM13, ZMM14, ZMM15, ZMM16,
ZMM17, ZMM18, ZMM19, ZMM2, ZMM20, ZMM21, ZMM22, ZMM23, ZMM24, ZMM25, ZMM26, ZMM27, ZMM28,
ZMM29, ZMM3, ZMM30, ZMM31, ZMM4, ZMM5, ZMM6, ZMM7, ZMM8, ZMM9,
};
use crate::x86::x86_subtarget::X86Subtarget;
use crate::codegen::{MachineInstr, MachineOperand};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::io::{self, Write};
fn make_mc_inst(opcode: u32, operands: Vec<MCOperand>) -> MCInst {
MCInst {
opcode,
operands,
loc: None,
flags: MCInstFlags::default(),
size: 0,
}
}
fn mc_inst_to_machine_instr(inst: &MCInst) -> MachineInstr {
let mut operands = Vec::new();
for op in &inst.operands {
match op {
MCOperand::Reg(r) => operands.push(MachineOperand::PhysReg(*r)),
MCOperand::Imm(v) => operands.push(MachineOperand::Imm(*v)),
_ => operands.push(MachineOperand::Imm(0)),
}
}
MachineInstr {
opcode: inst.opcode,
operands,
def: None,
}
}
#[derive(Debug)]
pub struct X86MCContext {
pub target_triple: String,
pub cpu: String,
pub symbols: HashMap<String, MCSymbol>,
pub sections: Vec<MCSection>,
pub current_section: Option<usize>,
pub fragments: Vec<MCFragment>,
pub next_temp_id: u64,
pub section_offsets: HashMap<String, u64>,
pub relocations: Vec<MCRelocationEntry>,
pub labels: HashMap<String, MCLabel>,
pub is_pic: bool,
pub code_model: X86CodeModel,
pub relocation_model: X86RelocationModel,
pub flags: Vec<AssemblerFlag>,
pub cfi_instructions: Vec<X86CFIInstruction>,
pub debug_lines: Vec<X86DebugLineEntry>,
pub debug_files: Vec<X86DebugFileEntry>,
pub debug_frame_entries: Vec<X86DebugFrameEntry>,
pub gen_dwarf: bool,
pub use_cfi: bool,
pub no_exec_stack: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CodeModel {
Small,
Medium,
Large,
Kernel,
Tiny,
}
impl Default for X86CodeModel {
fn default() -> Self {
X86CodeModel::Small
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RelocationModel {
Static,
PIC,
DynamicNoPic,
ROPI,
RWPI,
ROPIRWPI,
}
impl Default for X86RelocationModel {
fn default() -> Self {
X86RelocationModel::Static
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum X86CFIInstruction {
StartProc,
EndProc,
DefCfa { register: u16, offset: i64 },
DefCfaRegister(u16),
DefCfaOffset(i64),
Offset { register: u16, offset: i64 },
Restore(u16),
Undefined(u16),
Register { reg1: u16, reg2: u16 },
RememberState,
RestoreState,
WindowSave,
Escape(Vec<u8>),
AdvanceLoc(u64),
Lsda { encoding: u8, personality: String },
Personality { encoding: u8, function: String },
SignalFrame,
}
impl fmt::Display for X86CFIInstruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::StartProc => write!(f, ".cfi_startproc"),
Self::EndProc => write!(f, ".cfi_endproc"),
Self::DefCfa { register, offset } => {
write!(f, ".cfi_def_cfa {}, {}", reg_name(*register), offset)
}
Self::DefCfaRegister(r) => write!(f, ".cfi_def_cfa_register {}", reg_name(*r)),
Self::DefCfaOffset(o) => write!(f, ".cfi_def_cfa_offset {}", o),
Self::Offset { register, offset } => {
write!(f, ".cfi_offset {}, {}", reg_name(*register), offset)
}
Self::Restore(r) => write!(f, ".cfi_restore {}", reg_name(*r)),
Self::Undefined(r) => write!(f, ".cfi_undefined {}", reg_name(*r)),
Self::Register { reg1, reg2 } => {
write!(f, ".cfi_register {}, {}", reg_name(*reg1), reg_name(*reg2))
}
Self::RememberState => write!(f, ".cfi_remember_state"),
Self::RestoreState => write!(f, ".cfi_restore_state"),
Self::WindowSave => write!(f, ".cfi_window_save"),
Self::Escape(bytes) => write!(f, ".cfi_escape {:x?}", bytes),
Self::AdvanceLoc(delta) => write!(f, ".cfi_advance_loc {}", delta),
Self::Lsda {
encoding,
personality,
} => {
write!(f, ".cfi_lsda {}, {}", encoding, personality)
}
Self::Personality { encoding, function } => {
write!(f, ".cfi_personality {}, {}", encoding, function)
}
Self::SignalFrame => write!(f, ".cfi_signal_frame"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct X86DebugLineEntry {
pub address: u64,
pub file_id: u32,
pub line: u32,
pub column: u32,
pub is_stmt: bool,
pub is_epilogue_begin: bool,
pub is_prologue_end: bool,
pub discriminator: u32,
}
impl Default for X86DebugLineEntry {
fn default() -> Self {
Self {
address: 0,
file_id: 1,
line: 0,
column: 0,
is_stmt: false,
is_epilogue_begin: false,
is_prologue_end: false,
discriminator: 0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct X86DebugFileEntry {
pub file_id: u32,
pub directory_id: u32,
pub name: String,
pub directory: String,
pub md5_checksum: Option<[u8; 16]>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct X86DebugFrameEntry {
pub is_cie: bool,
pub length: u32,
pub cie_id: u32,
pub version: u8,
pub augmentation: String,
pub code_alignment_factor: u32,
pub data_alignment_factor: i32,
pub return_address_register: u8,
pub initial_instructions: Vec<u8>,
pub fde_instructions: Vec<u8>,
pub pc_begin: u64,
pub pc_range: u64,
pub lsda_pointer: Option<u64>,
}
fn reg_name(reg: u16) -> String {
let info = X86RegisterInfo;
match reg {
r if r == RAX => "rax".into(),
r if r == RBX => "rbx".into(),
r if r == RCX => "rcx".into(),
r if r == RDX => "rdx".into(),
r if r == RSI => "rsi".into(),
r if r == RDI => "rdi".into(),
r if r == RBP => "rbp".into(),
r if r == RSP => "rsp".into(),
r if r == R8 => "r8".into(),
r if r == R9 => "r9".into(),
r if r == R10 => "r10".into(),
r if r == R11 => "r11".into(),
r if r == R12 => "r12".into(),
r if r == R13 => "r13".into(),
r if r == R14 => "r14".into(),
r if r == R15 => "r15".into(),
r if r == RIP => "rip".into(),
_ => {
format!("%r{}", reg)
}
}
}
impl X86MCContext {
pub fn new(target_triple: impl Into<String>, cpu: impl Into<String>) -> Self {
Self {
target_triple: target_triple.into(),
cpu: cpu.into(),
symbols: HashMap::new(),
sections: Vec::new(),
current_section: None,
fragments: Vec::new(),
next_temp_id: 0,
section_offsets: HashMap::new(),
relocations: Vec::new(),
labels: HashMap::new(),
is_pic: false,
code_model: X86CodeModel::default(),
relocation_model: X86RelocationModel::default(),
flags: Vec::new(),
cfi_instructions: Vec::new(),
debug_lines: Vec::new(),
debug_files: Vec::new(),
debug_frame_entries: Vec::new(),
gen_dwarf: false,
use_cfi: false,
no_exec_stack: false,
}
}
pub fn set_code_model(&mut self, model: X86CodeModel) {
self.code_model = model;
}
pub fn set_relocation_model(&mut self, model: X86RelocationModel) {
self.relocation_model = model;
}
pub fn set_pic(&mut self, pic: bool) {
self.is_pic = pic;
}
pub fn set_gen_dwarf(&mut self, gen: bool) {
self.gen_dwarf = gen;
}
pub fn set_use_cfi(&mut self, use_cfi: bool) {
self.use_cfi = use_cfi;
}
pub fn add_flag(&mut self, flag: AssemblerFlag) {
self.flags.push(flag);
}
pub fn get_or_create_symbol(&mut self, name: impl Into<String>) -> &MCSymbol {
let name = name.into();
if !self.symbols.contains_key(&name) {
let sym = MCSymbol::new(&name);
self.symbols.insert(name.clone(), sym);
}
self.symbols.get(&name).unwrap()
}
pub fn create_temp_symbol(&mut self) -> MCSymbol {
let name = format!(".Ltmp{}", self.next_temp_id);
self.next_temp_id += 1;
let sym = MCSymbol::new(&name);
self.symbols.insert(name, sym.clone());
sym
}
pub fn get_or_create_section(&mut self, section: MCSection) -> usize {
for (i, s) in self.sections.iter().enumerate() {
if s.name == section.name {
return i;
}
}
let idx = self.sections.len();
self.sections.push(section);
idx
}
pub fn switch_section(&mut self, section_idx: usize) {
self.current_section = Some(section_idx);
}
pub fn get_current_section(&self) -> Option<usize> {
self.current_section
}
pub fn emit_instruction(&mut self, inst: MCInst) {
self.fragments.push(MCFragment::Instruction(inst));
}
pub fn emit_data(&mut self, data: Vec<u8>) {
self.fragments.push(MCFragment::Data(data));
}
pub fn emit_alignment(&mut self, alignment: u32, value: u8, _max_bytes: u32) {
self.fragments.push(MCFragment::Align(alignment, value));
}
pub fn emit_fill(&mut self, size: u64, value: u8) {
self.fragments.push(MCFragment::Fill(size, value));
}
pub fn emit_org(&mut self, offset: u64) {
self.fragments.push(MCFragment::Org(offset, 0));
}
pub fn emit_label(&mut self, label: MCLabel) {
let name = label.name.clone();
self.labels.insert(name.clone(), label.clone());
self.fragments.push(MCFragment::Label(label));
}
pub fn emit_symbol_def(&mut self, name: impl Into<String>, value: u64) {
let name = name.into();
let sym = self.get_or_create_symbol(&name).clone();
let mut sym_mut = sym.clone();
sym_mut.value = value;
self.symbols.insert(name, sym_mut);
self.fragments.push(MCFragment::SymbolDef(sym.name.clone()));
}
pub fn emit_fill_expr(&mut self, size: u64, value: u64) {
self.fragments
.push(MCFragment::FillExpr(MCExpr::Constant(value as i64), size));
}
pub fn emit_relaxable(&mut self, inst: MCInst, small: Vec<u8>, large: Vec<u8>) {
self.fragments
.push(MCFragment::Relaxable(MCRelaxableFragment::new(
inst, small, large,
)));
}
pub fn get_current_offset(&self) -> u64 {
let mut offset = 0u64;
for frag in &self.fragments {
offset += frag.size().unwrap_or(0);
}
offset
}
pub fn flatten(&self) -> Vec<u8> {
let mut result = Vec::new();
for frag in &self.fragments {
match frag {
MCFragment::Instruction(inst) => {
let encoder = X86MCEncoder::new(
X86Mode::Mode64,
X86Subtarget::new("x86_64-unknown-linux-gnu", "generic", ""),
);
let mi = mc_inst_to_machine_instr(inst);
let encoded = encoder.encode_instruction(&mi);
result.extend_from_slice(&encoded);
}
MCFragment::Data(data) => {
result.extend_from_slice(data);
}
MCFragment::Align(alignment, value) => {
let align = *alignment as u64;
let pad = (align - (result.len() as u64 % align)) % align;
for _ in 0..pad {
result.push(*value);
}
}
MCFragment::Fill(size, value) => {
for _ in 0..*size {
result.push(*value);
}
}
MCFragment::Org(offset, fill) => {
while result.len() < *offset as usize {
result.push(*fill);
}
}
MCFragment::Label(_) => {
}
MCFragment::SymbolDef(sym) => {
let off = result.len() as u64;
}
MCFragment::FillExpr(_, size) => {
for _ in 0..*size {
result.push(0);
}
}
MCFragment::Relaxable(rf) => {
if rf.is_relaxed {
result.extend_from_slice(&rf.large_encoding);
} else {
result.extend_from_slice(&rf.small_encoding);
}
}
}
}
result
}
pub fn fragment_count(&self) -> usize {
self.fragments.len()
}
pub fn instruction_count(&self) -> usize {
self.fragments.iter().filter(|f| f.is_instruction()).count()
}
pub fn record_cfi(&mut self, cfi: X86CFIInstruction) {
self.cfi_instructions.push(cfi);
}
pub fn record_debug_line(&mut self, entry: X86DebugLineEntry) {
self.debug_lines.push(entry);
}
pub fn record_debug_file(&mut self, entry: X86DebugFileEntry) {
self.debug_files.push(entry);
}
pub fn record_debug_frame(&mut self, entry: X86DebugFrameEntry) {
self.debug_frame_entries.push(entry);
}
pub fn add_relocation(&mut self, reloc: MCRelocationEntry) {
self.relocations.push(reloc);
}
pub fn clear(&mut self) {
self.symbols.clear();
self.sections.clear();
self.current_section = None;
self.fragments.clear();
self.next_temp_id = 0;
self.section_offsets.clear();
self.relocations.clear();
self.labels.clear();
self.cfi_instructions.clear();
self.debug_lines.clear();
self.debug_files.clear();
self.debug_frame_entries.clear();
}
}
impl Default for X86MCContext {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu", "generic")
}
}
impl fmt::Display for X86MCContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "X86MCContext {{ triple: {} }}", self.target_triple)?;
writeln!(f, " sections: {}", self.sections.len())?;
writeln!(f, " symbols: {}", self.symbols.len())?;
writeln!(f, " fragments: {}", self.fragments.len())?;
writeln!(f, " relocations: {}", self.relocations.len())?;
writeln!(f, " cfi_instructions: {}", self.cfi_instructions.len())?;
writeln!(f, " debug_lines: {}", self.debug_lines.len())?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct X86MCRegisterInfo {
pub base: X86RegisterInfo,
pub dwarf_to_reg: HashMap<u16, X86Reg>,
pub reg_to_dwarf: HashMap<X86Reg, u16>,
pub llvm_to_reg: Vec<X86Reg>,
pub reg_classes: Vec<MCRegisterClass>,
pub sub_reg_indices: Vec<(X86Reg, X86Reg, u32)>,
pub callee_saved: Vec<X86Reg>,
pub caller_saved: Vec<X86Reg>,
pub allocatable_mask: Vec<u8>,
}
impl X86MCRegisterInfo {
pub fn new_x86_64() -> Self {
let base = X86RegisterInfo;
let mut dwarf_to_reg = HashMap::new();
let mut reg_to_dwarf = HashMap::new();
let dwarf_mapping: &[(X86Reg, u16)] = &[
(X86Reg(RAX), 0),
(X86Reg(RDX), 1),
(X86Reg(RCX), 2),
(X86Reg(RBX), 3),
(X86Reg(RSI), 4),
(X86Reg(RDI), 5),
(X86Reg(RBP), 6),
(X86Reg(RSP), 7),
(X86Reg(R8), 8),
(X86Reg(R9), 9),
(X86Reg(R10), 10),
(X86Reg(R11), 11),
(X86Reg(R12), 12),
(X86Reg(R13), 13),
(X86Reg(R14), 14),
(X86Reg(R15), 15),
(X86Reg(RIP), 16),
(X86Reg(XMM0), 17),
(X86Reg(XMM1), 18),
(X86Reg(XMM2), 19),
(X86Reg(XMM3), 20),
(X86Reg(XMM4), 21),
(X86Reg(XMM5), 22),
(X86Reg(XMM6), 23),
(X86Reg(XMM7), 24),
(X86Reg(XMM8), 25),
(X86Reg(XMM9), 26),
(X86Reg(XMM10), 27),
(X86Reg(XMM11), 28),
(X86Reg(XMM12), 29),
(X86Reg(XMM13), 30),
(X86Reg(XMM14), 31),
(X86Reg(XMM15), 32),
(X86Reg(XMM16), 33),
(X86Reg(XMM17), 34),
(X86Reg(XMM18), 35),
(X86Reg(XMM19), 36),
(X86Reg(XMM20), 37),
(X86Reg(XMM21), 38),
(X86Reg(XMM22), 39),
(X86Reg(XMM23), 40),
(X86Reg(XMM24), 41),
(X86Reg(XMM25), 42),
(X86Reg(XMM26), 43),
(X86Reg(XMM27), 44),
(X86Reg(XMM28), 45),
(X86Reg(XMM29), 46),
(X86Reg(XMM30), 47),
(X86Reg(XMM31), 48),
(X86Reg(K0), 118),
(X86Reg(K1), 119),
(X86Reg(K2), 120),
(X86Reg(K3), 121),
(X86Reg(K4), 122),
(X86Reg(K5), 123),
(X86Reg(K6), 124),
(X86Reg(K7), 125),
];
for &(reg, dwarf_num) in dwarf_mapping {
dwarf_to_reg.insert(dwarf_num, reg);
reg_to_dwarf.insert(reg, dwarf_num);
}
let reg_classes = vec![
MCRegisterClass {
name: "GR64".to_string(),
id: 0,
regs: vec![
RAX as u32, RCX as u32, RDX as u32, RBX as u32, RSP as u32, RBP as u32,
RSI as u32, RDI as u32, R8 as u32, R9 as u32, R10 as u32, R11 as u32,
R12 as u32, R13 as u32, R14 as u32, R15 as u32,
],
alignment: 8,
copy_cost: 1,
is_allocatable: true,
},
MCRegisterClass {
name: "GR32".to_string(),
id: 1,
regs: vec![
EAX as u32, ECX as u32, EDX as u32, EBX as u32, ESP as u32, EBP as u32,
ESI as u32, EDI as u32,
],
alignment: 4,
copy_cost: 1,
is_allocatable: true,
},
MCRegisterClass {
name: "GR16".to_string(),
id: 2,
regs: vec![
AX as u32, CX as u32, DX as u32, BX as u32, BPL as u32, SIL as u32, DIL as u32,
SPL as u32,
],
alignment: 2,
copy_cost: 1,
is_allocatable: true,
},
MCRegisterClass {
name: "GR8".to_string(),
id: 3,
regs: vec![
AL as u32, CL as u32, DL as u32, BL as u32, AH as u32, CH as u32, DH as u32,
BH as u32,
],
alignment: 1,
copy_cost: 1,
is_allocatable: true,
},
MCRegisterClass {
name: "VR128".to_string(),
id: 4,
regs: vec![
XMM0 as u32,
XMM1 as u32,
XMM2 as u32,
XMM3 as u32,
XMM4 as u32,
XMM5 as u32,
XMM6 as u32,
XMM7 as u32,
XMM8 as u32,
XMM9 as u32,
XMM10 as u32,
XMM11 as u32,
XMM12 as u32,
XMM13 as u32,
XMM14 as u32,
XMM15 as u32,
],
alignment: 16,
copy_cost: 2,
is_allocatable: true,
},
MCRegisterClass {
name: "VR256".to_string(),
id: 5,
regs: vec![
YMM0 as u32,
YMM1 as u32,
YMM2 as u32,
YMM3 as u32,
YMM4 as u32,
YMM5 as u32,
YMM6 as u32,
YMM7 as u32,
YMM8 as u32,
YMM9 as u32,
YMM10 as u32,
YMM11 as u32,
YMM12 as u32,
YMM13 as u32,
YMM14 as u32,
YMM15 as u32,
],
alignment: 32,
copy_cost: 2,
is_allocatable: true,
},
MCRegisterClass {
name: "VR512".to_string(),
id: 6,
regs: vec![
ZMM0 as u32,
ZMM1 as u32,
ZMM2 as u32,
ZMM3 as u32,
ZMM4 as u32,
ZMM5 as u32,
ZMM6 as u32,
ZMM7 as u32,
ZMM8 as u32,
ZMM9 as u32,
ZMM10 as u32,
ZMM11 as u32,
ZMM12 as u32,
ZMM13 as u32,
ZMM14 as u32,
ZMM15 as u32,
ZMM16 as u32,
ZMM17 as u32,
ZMM18 as u32,
ZMM19 as u32,
ZMM20 as u32,
ZMM21 as u32,
ZMM22 as u32,
ZMM23 as u32,
ZMM24 as u32,
ZMM25 as u32,
ZMM26 as u32,
ZMM27 as u32,
ZMM28 as u32,
ZMM29 as u32,
ZMM30 as u32,
ZMM31 as u32,
],
alignment: 64,
copy_cost: 3,
is_allocatable: true,
},
MCRegisterClass {
name: "VK".to_string(),
id: 7,
regs: vec![
K0 as u32, K1 as u32, K2 as u32, K3 as u32, K4 as u32, K5 as u32, K6 as u32,
K7 as u32,
],
alignment: 2,
copy_cost: 1,
is_allocatable: true,
},
MCRegisterClass {
name: "SEGMENT".to_string(),
id: 8,
regs: vec![
CS as u32, DS as u32, ES as u32, FS as u32, GS as u32, SS as u32,
],
alignment: 2,
copy_cost: 1,
is_allocatable: false,
},
];
let callee_saved: Vec<X86Reg> = vec![
X86Reg(RBX),
X86Reg(RBP),
X86Reg(R12),
X86Reg(R13),
X86Reg(R14),
X86Reg(R15),
];
let caller_saved: Vec<X86Reg> = vec![
X86Reg(RAX),
X86Reg(RCX),
X86Reg(RDX),
X86Reg(RSI),
X86Reg(RDI),
X86Reg(R8),
X86Reg(R9),
X86Reg(R10),
X86Reg(R11),
];
let mut allocatable_mask = vec![0u8; (TOTAL_REG_COUNT as usize + 7) / 8];
for ® in callee_saved.iter().chain(caller_saved.iter()) {
let idx = reg.0 as usize;
allocatable_mask[idx / 8] |= 1 << (idx % 8);
}
let sub_reg_indices = vec![
(X86Reg(RAX), X86Reg(EAX), 1u32),
(X86Reg(RAX), X86Reg(AX), 2u32),
(X86Reg(RAX), X86Reg(AL), 3u32),
(X86Reg(RAX), X86Reg(AH), 4u32),
(X86Reg(RBX), X86Reg(EBX), 1u32),
(X86Reg(RBX), X86Reg(BX), 2u32),
(X86Reg(RBX), X86Reg(BL), 3u32),
(X86Reg(RBX), X86Reg(BH), 4u32),
(X86Reg(RCX), X86Reg(ECX), 1u32),
(X86Reg(RCX), X86Reg(CX), 2u32),
(X86Reg(RCX), X86Reg(CL), 3u32),
(X86Reg(RCX), X86Reg(CH), 4u32),
(X86Reg(RDX), X86Reg(EDX), 1u32),
(X86Reg(RDX), X86Reg(DX), 2u32),
(X86Reg(RDX), X86Reg(DL), 3u32),
(X86Reg(RDX), X86Reg(DH), 4u32),
(X86Reg(RSP), X86Reg(ESP), 1u32),
(X86Reg(RSP), X86Reg(SPL), 2u32),
(X86Reg(RBP), X86Reg(EBP), 1u32),
(X86Reg(RBP), X86Reg(BPL), 2u32),
(X86Reg(RSI), X86Reg(ESI), 1u32),
(X86Reg(RSI), X86Reg(SIL), 2u32),
(X86Reg(RDI), X86Reg(EDI), 1u32),
(X86Reg(RDI), X86Reg(DIL), 2u32),
(X86Reg(R8), X86Reg(R8D), 1u32),
(X86Reg(R8), X86Reg(R8W), 2u32),
(X86Reg(R8), X86Reg(R8B), 3u32),
(X86Reg(R9), X86Reg(R9D), 1u32),
(X86Reg(R9), X86Reg(R9W), 2u32),
(X86Reg(R9), X86Reg(R9B), 3u32),
(X86Reg(R10), X86Reg(R10D), 1u32),
(X86Reg(R10), X86Reg(R10W), 2u32),
(X86Reg(R10), X86Reg(R10B), 3u32),
(X86Reg(R11), X86Reg(R11D), 1u32),
(X86Reg(R11), X86Reg(R11W), 2u32),
(X86Reg(R11), X86Reg(R11B), 3u32),
(X86Reg(R12), X86Reg(R12D), 1u32),
(X86Reg(R12), X86Reg(R12W), 2u32),
(X86Reg(R12), X86Reg(R12B), 3u32),
(X86Reg(R13), X86Reg(R13D), 1u32),
(X86Reg(R13), X86Reg(R13W), 2u32),
(X86Reg(R13), X86Reg(R13B), 3u32),
(X86Reg(R14), X86Reg(R14D), 1u32),
(X86Reg(R14), X86Reg(R14W), 2u32),
(X86Reg(R14), X86Reg(R14B), 3u32),
(X86Reg(R15), X86Reg(R15D), 1u32),
(X86Reg(R15), X86Reg(R15W), 2u32),
(X86Reg(R15), X86Reg(R15B), 3u32),
(X86Reg(YMM0), X86Reg(XMM0), 1u32),
(X86Reg(YMM1), X86Reg(XMM1), 1u32),
(X86Reg(YMM2), X86Reg(XMM2), 1u32),
(X86Reg(YMM3), X86Reg(XMM3), 1u32),
(X86Reg(YMM4), X86Reg(XMM4), 1u32),
(X86Reg(YMM5), X86Reg(XMM5), 1u32),
(X86Reg(YMM6), X86Reg(XMM6), 1u32),
(X86Reg(YMM7), X86Reg(XMM7), 1u32),
(X86Reg(ZMM0), X86Reg(YMM0), 1u32),
(X86Reg(ZMM1), X86Reg(YMM1), 1u32),
(X86Reg(ZMM2), X86Reg(YMM2), 1u32),
(X86Reg(ZMM3), X86Reg(YMM3), 1u32),
];
let llvm_to_reg: Vec<X86Reg> = (0..TOTAL_REG_COUNT).map(|i| X86Reg::new(i)).collect();
Self {
base,
dwarf_to_reg,
reg_to_dwarf,
llvm_to_reg,
reg_classes,
sub_reg_indices,
callee_saved,
caller_saved,
allocatable_mask,
}
}
pub fn get_dwarf_reg_num(&self, reg: X86Reg) -> Option<u16> {
self.reg_to_dwarf.get(®).copied()
}
pub fn get_reg_from_dwarf(&self, dwarf_num: u16) -> Option<X86Reg> {
self.dwarf_to_reg.get(&dwarf_num).copied()
}
pub fn get_llvm_reg_num(&self, reg: X86Reg) -> u16 {
reg.0
}
pub fn is_allocatable(&self, reg: X86Reg) -> bool {
let idx = reg.0 as usize;
if idx >= self.allocatable_mask.len() * 8 {
return false;
}
self.allocatable_mask[idx / 8] & (1 << (idx % 8)) != 0
}
pub fn is_callee_saved(&self, reg: X86Reg) -> bool {
self.callee_saved.iter().any(|r| r.0 == reg.0)
}
pub fn get_reg_class(&self, reg: X86Reg) -> Option<&MCRegisterClass> {
self.reg_classes.iter().find(|c| c.contains(reg.0 as u32))
}
pub fn get_super_reg(&self, sub: X86Reg) -> Option<X86Reg> {
self.sub_reg_indices
.iter()
.find(|(_, s, _)| s.0 == sub.0)
.map(|(super_reg, _, _)| *super_reg)
}
pub fn get_sub_regs(&self, super_reg: X86Reg) -> Vec<(X86Reg, u32)> {
self.sub_reg_indices
.iter()
.filter(|(sup, _, _)| sup.0 == super_reg.0)
.map(|(_, sub, idx)| (*sub, *idx))
.collect()
}
pub fn get_reg_name(&self, _reg: X86Reg) -> Option<&'static str> {
None
}
}
impl Default for X86MCRegisterInfo {
fn default() -> Self {
Self::new_x86_64()
}
}
#[derive(Debug, Clone)]
pub struct X86MCInstrInfo {
pub base: X86InstrInfo,
pub descs: HashMap<u16, MCInstrDesc>,
pub name_to_opcode: HashMap<String, u16>,
pub opcode_to_name: HashMap<u16, String>,
pub is_branch: HashSet<u16>,
pub is_call: HashSet<u16>,
pub is_return: HashSet<u16>,
pub is_barrier: HashSet<u16>,
pub is_terminator: HashSet<u16>,
pub is_indirect_branch: HashSet<u16>,
pub may_load: HashSet<u16>,
pub may_store: HashSet<u16>,
pub has_side_effects: HashSet<u16>,
pub is_uncond_branch: HashSet<u16>,
pub is_cond_branch: HashSet<u16>,
}
impl X86MCInstrInfo {
pub fn new() -> Self {
let base = X86InstrInfo::default();
let mut descs = HashMap::new();
let mut name_to_opcode = HashMap::new();
let mut opcode_to_name = HashMap::new();
let mut is_branch = HashSet::new();
let mut is_call = HashSet::new();
let mut is_return = HashSet::new();
let mut is_barrier = HashSet::new();
let mut is_terminator = HashSet::new();
let mut is_indirect_branch = HashSet::new();
let mut may_load = HashSet::new();
let mut may_store = HashSet::new();
let mut has_side_effects = HashSet::new();
let mut is_uncond_branch = HashSet::new();
let mut is_cond_branch = HashSet::new();
let instr_data: Vec<(u16, &str, u8, u8, u8, bool, bool, bool, bool, bool)> = vec![
(1, "MOV", 3, 1, 2, false, false, false, false, false),
(2, "ADD", 3, 1, 2, false, false, false, false, false),
(3, "SUB", 3, 1, 2, false, false, false, false, false),
(4, "MUL", 3, 1, 2, false, false, false, false, false),
(5, "DIV", 3, 1, 2, false, false, false, false, false),
(6, "AND", 3, 1, 2, false, false, false, false, false),
(7, "OR", 3, 1, 2, false, false, false, false, false),
(8, "XOR", 3, 1, 2, false, false, false, false, false),
(9, "SHL", 3, 1, 2, false, false, false, false, false),
(10, "SHR", 3, 1, 2, false, false, false, false, false),
(11, "PUSH", 1, 0, 1, false, false, false, false, false),
(12, "POP", 1, 1, 1, false, false, false, false, false),
(13, "CALL", 5, 0, 1, false, true, false, false, false),
(14, "RET", 1, 0, 0, false, false, true, true, false),
(15, "JMP", 5, 0, 1, true, false, false, false, false),
(16, "JE", 2, 0, 1, true, false, false, false, false),
(17, "JNE", 2, 0, 1, true, false, false, false, false),
(18, "CMP", 3, 1, 2, false, false, false, false, false),
(19, "LEA", 3, 1, 2, false, false, false, false, false),
(20, "INC", 2, 1, 1, false, false, false, false, false),
(21, "DEC", 2, 1, 1, false, false, false, false, false),
(22, "NOT", 2, 1, 1, false, false, false, false, false),
(23, "NEG", 2, 1, 1, false, false, false, false, false),
(24, "NOP", 1, 0, 0, false, false, false, false, false),
(25, "TEST", 3, 1, 2, false, false, false, false, false),
(26, "IMUL", 3, 1, 2, false, false, false, false, false),
(27, "IDIV", 3, 1, 2, false, false, false, false, false),
(28, "SAR", 3, 1, 2, false, false, false, false, false),
(29, "MOVSX", 4, 1, 2, false, false, false, false, false),
(30, "MOVZX", 4, 1, 2, false, false, false, false, false),
(31, "ADC", 3, 1, 2, false, false, false, false, false),
(32, "SBB", 3, 1, 2, false, false, false, false, false),
(33, "BSWAP", 2, 1, 1, false, false, false, false, false),
(34, "XCHG", 2, 1, 2, false, false, false, false, false),
(35, "SYSCALL", 2, 0, 0, false, true, false, true, false),
(36, "CPUID", 2, 0, 0, false, false, false, true, false),
];
for (opcode, mnemonic, size, num_defs, num_ops, branch, call, ret, barrier, store) in
instr_data
{
name_to_opcode.insert(mnemonic.to_string(), opcode);
opcode_to_name.insert(opcode, mnemonic.to_string());
let flags = MCInstFlags {
is_terminator: ret || branch || call,
is_branch: branch || call,
is_call: call,
is_return: ret,
is_compare: opcode == 18,
is_move_imm: opcode == 1,
may_load: !ret && !barrier,
may_store: store,
has_side_effects: barrier || call || opcode == 35,
is_barrier: barrier || ret,
is_indirect_branch: opcode == 15,
is_cond_branch: opcode == 16 || opcode == 17,
is_uncond_branch: opcode == 15,
is_trap: false,
is_speculatable: !barrier && !call,
};
let mut desc = MCInstrDesc::new(opcode as u32, mnemonic);
desc.num_operands = num_ops as u32;
desc.num_defs = num_defs as u32;
desc.size = size as u32;
desc.flags = flags;
if branch || call {
is_branch.insert(opcode);
}
if call {
is_call.insert(opcode);
}
if ret {
is_return.insert(opcode);
}
if barrier || ret {
is_barrier.insert(opcode);
}
if ret || branch || call {
is_terminator.insert(opcode);
}
if opcode == 15 || opcode == 13 {
is_indirect_branch.insert(opcode);
}
if !ret && !barrier {
may_load.insert(opcode);
}
if store {
may_store.insert(opcode);
}
if barrier || call || opcode == 35 {
has_side_effects.insert(opcode);
}
if opcode == 15 {
is_uncond_branch.insert(opcode);
}
if opcode == 16 || opcode == 17 {
is_cond_branch.insert(opcode);
}
descs.insert(opcode, desc);
}
Self {
base,
descs,
name_to_opcode,
opcode_to_name,
is_branch,
is_call,
is_return,
is_barrier,
is_terminator,
is_indirect_branch,
may_load,
may_store,
has_side_effects,
is_uncond_branch,
is_cond_branch,
}
}
pub fn get_desc(&self, opcode: u16) -> Option<&MCInstrDesc> {
self.descs.get(&opcode)
}
pub fn lookup_opcode(&self, name: &str) -> Option<u16> {
self.name_to_opcode.get(name).copied()
}
pub fn get_mnemonic(&self, opcode: u16) -> Option<&str> {
self.opcode_to_name.get(&opcode).map(|s| s.as_str())
}
pub fn num_instructions(&self) -> usize {
self.descs.len()
}
pub fn is_branch_opcode(&self, opcode: u16) -> bool {
self.is_branch.contains(&opcode)
}
pub fn is_call_opcode(&self, opcode: u16) -> bool {
self.is_call.contains(&opcode)
}
pub fn is_return_opcode(&self, opcode: u16) -> bool {
self.is_return.contains(&opcode)
}
pub fn is_barrier_opcode(&self, opcode: u16) -> bool {
self.is_barrier.contains(&opcode)
}
pub fn is_terminator_opcode(&self, opcode: u16) -> bool {
self.is_terminator.contains(&opcode)
}
pub fn is_uncond_branch_opcode(&self, opcode: u16) -> bool {
self.is_uncond_branch.contains(&opcode)
}
pub fn is_cond_branch_opcode(&self, opcode: u16) -> bool {
self.is_cond_branch.contains(&opcode)
}
}
impl Default for X86MCInstrInfo {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86MCSubtargetInfo {
pub target_triple: String,
pub cpu: String,
pub features: FeatureBits,
pub feature_names: Vec<String>,
pub has_fpu: bool,
pub has_vector: bool,
pub has_atomics: bool,
pub has_compressed: bool,
pub sched_model: String,
pub itineraries: Vec<InstrItinerary>,
pub proc_resources: Vec<ProcResource>,
pub write_latencies: Vec<WriteLatency>,
pub write_resources: Vec<WriteRes>,
pub read_advances: Vec<ReadAdvance>,
pub has_sse: bool,
pub has_sse2: bool,
pub has_sse3: bool,
pub has_ssse3: bool,
pub has_sse41: bool,
pub has_sse42: bool,
pub has_avx: bool,
pub has_avx2: bool,
pub has_avx512f: bool,
pub has_avx512bw: bool,
pub has_avx512dq: bool,
pub has_avx512vl: bool,
pub has_fma: bool,
pub has_bmi: bool,
pub has_bmi2: bool,
pub has_lzcnt: bool,
pub has_popcnt: bool,
pub has_movbe: bool,
pub has_rdrand: bool,
pub has_rdseed: bool,
pub has_aes: bool,
pub has_sha: bool,
pub has_mpx: bool,
pub has_sgx: bool,
pub has_cet: bool,
pub has_xsave: bool,
pub has_xsaveopt: bool,
pub has_xsaves: bool,
pub has_fsgsbase: bool,
pub has_rdpid: bool,
pub has_clflushopt: bool,
pub has_clwb: bool,
pub has_prefetchwt1: bool,
pub has_pku: bool,
pub has_waitpkg: bool,
pub has_shstk: bool,
pub has_gfni: bool,
pub has_vaes: bool,
pub has_vpclmulqdq: bool,
pub has_avxvnni: bool,
pub has_avxifma: bool,
pub has_amx_bf16: bool,
pub has_amx_tile: bool,
pub has_amx_int8: bool,
pub is_64bit: bool,
pub slow_3ops_lea: bool,
pub slow_divide: bool,
pub pad_short_functions: bool,
pub prefetchw: bool,
}
#[derive(Debug, Clone)]
pub struct InstrItinerary {
pub opcode: u16,
pub stage: u8,
pub latency: u32,
pub resource: u8,
}
#[derive(Debug, Clone)]
pub struct ProcResource {
pub name: String,
pub num_units: u32,
pub super_resource: Option<usize>,
pub buffer_size: i32,
}
#[derive(Debug, Clone)]
pub struct WriteLatency {
pub opcode: u16,
pub cycles: u32,
}
#[derive(Debug, Clone)]
pub struct WriteRes {
pub opcode: u16,
pub resource_id: u8,
pub cycles: u32,
}
#[derive(Debug, Clone)]
pub struct ReadAdvance {
pub opcode: u16,
pub operand_idx: u8,
pub advance_cycles: u32,
}
impl X86MCSubtargetInfo {
pub fn new(cpu: impl Into<String>, features: Vec<String>) -> Self {
let cpu = cpu.into();
let mut sti = Self {
target_triple: "x86_64-unknown-linux-gnu".into(),
cpu: cpu.clone(),
features: FeatureBits::new(256),
feature_names: features.clone(),
has_fpu: true,
has_vector: true,
has_atomics: true,
has_compressed: false,
sched_model: "skylake".into(),
itineraries: Vec::new(),
proc_resources: Vec::new(),
write_latencies: Vec::new(),
write_resources: Vec::new(),
read_advances: Vec::new(),
has_sse: true,
has_sse2: true,
has_sse3: false,
has_ssse3: false,
has_sse41: false,
has_sse42: false,
has_avx: false,
has_avx2: false,
has_avx512f: false,
has_avx512bw: false,
has_avx512dq: false,
has_avx512vl: false,
has_fma: false,
has_bmi: false,
has_bmi2: false,
has_lzcnt: false,
has_popcnt: false,
has_movbe: false,
has_rdrand: false,
has_rdseed: false,
has_aes: false,
has_sha: false,
has_mpx: false,
has_sgx: false,
has_cet: false,
has_xsave: false,
has_xsaveopt: false,
has_xsaves: false,
has_fsgsbase: false,
has_rdpid: false,
has_clflushopt: false,
has_clwb: false,
has_prefetchwt1: false,
has_pku: false,
has_waitpkg: false,
has_shstk: false,
has_gfni: false,
has_vaes: false,
has_vpclmulqdq: false,
has_avxvnni: false,
has_avxifma: false,
has_amx_bf16: false,
has_amx_tile: false,
has_amx_int8: false,
is_64bit: true,
slow_3ops_lea: false,
slow_divide: false,
pad_short_functions: false,
prefetchw: false,
};
for feat in features {
sti.apply_feature_flag(&feat);
}
sti.apply_cpu_defaults(&cpu);
sti
}
fn apply_cpu_defaults(&mut self, cpu: &str) {
match cpu {
"generic" | "x86-64" => {
self.has_sse2 = true;
self.is_64bit = true;
}
"nehalem" | "westmere" => {
self.has_sse2 = true;
self.has_sse3 = true;
self.has_ssse3 = true;
self.has_sse41 = true;
self.has_sse42 = true;
self.is_64bit = true;
}
"sandybridge" | "ivybridge" => {
self.has_sse2 = true;
self.has_sse3 = true;
self.has_ssse3 = true;
self.has_sse41 = true;
self.has_sse42 = true;
self.has_avx = true;
self.is_64bit = true;
self.sched_model = "sandybridge".into();
}
"haswell" | "broadwell" => {
self.has_sse2 = true;
self.has_sse3 = true;
self.has_ssse3 = true;
self.has_sse41 = true;
self.has_sse42 = true;
self.has_avx = true;
self.has_avx2 = true;
self.has_fma = true;
self.has_bmi = true;
self.has_bmi2 = true;
self.has_lzcnt = true;
self.is_64bit = true;
self.sched_model = "haswell".into();
}
"skylake" | "skylake-avx512" | "cascadelake" | "cooperlake" | "cannonlake"
| "icelake-client" | "icelake-server" | "tigerlake" | "rocketlake" | "alderlake"
| "raptorlake" | "meteorlake" | "arrowlake" | "lunarlake" => {
self.has_sse2 = true;
self.has_sse3 = true;
self.has_ssse3 = true;
self.has_sse41 = true;
self.has_sse42 = true;
self.has_avx = true;
self.has_avx2 = true;
self.has_fma = true;
self.has_bmi = true;
self.has_bmi2 = true;
self.has_lzcnt = true;
self.has_popcnt = true;
self.has_movbe = true;
self.has_xsave = true;
self.has_fsgsbase = true;
self.has_rdrand = true;
self.is_64bit = true;
self.sched_model = "skylake".into();
if cpu.contains("avx512")
|| cpu == "cascadelake"
|| cpu == "cooperlake"
|| cpu == "icelake-server"
{
self.has_avx512f = true;
self.has_avx512bw = true;
self.has_avx512dq = true;
self.has_avx512vl = true;
}
}
"znver1" | "znver2" | "znver3" | "znver4" | "znver5" => {
self.has_sse2 = true;
self.has_sse3 = true;
self.has_ssse3 = true;
self.has_sse41 = true;
self.has_sse42 = true;
self.has_avx = true;
self.has_avx2 = true;
self.has_fma = true;
self.has_bmi = true;
self.has_bmi2 = true;
self.has_lzcnt = true;
self.has_popcnt = true;
self.has_movbe = true;
self.has_xsave = true;
self.has_rdrand = true;
self.has_fsgsbase = true;
self.is_64bit = true;
self.sched_model = "znver3".into();
if cpu == "znver4" || cpu == "znver5" {
self.has_avx512f = true;
self.has_avx512bw = true;
self.has_avx512dq = true;
self.has_avx512vl = true;
}
}
"atom" | "silvermont" | "goldmont" | "goldmont-plus" | "tremont" | "gracemont" => {
self.has_sse2 = true;
self.has_sse3 = true;
self.has_ssse3 = true;
self.has_sse41 = true;
self.has_sse42 = true;
self.has_movbe = true;
self.is_64bit = true;
self.sched_model = "atom".into();
}
"pentium4" | "pentium-m" | "pentium" => {
self.has_sse2 = true;
self.is_64bit = false;
self.sched_model = "pentium4".into();
}
"i386" | "i486" | "i586" | "i686" => {
self.is_64bit = false;
self.sched_model = "i686".into();
}
_ => {
self.has_sse = true;
self.has_sse2 = true;
self.is_64bit = true;
}
}
}
pub fn apply_feature_flag(&mut self, flag: &str) {
if flag.is_empty() {
return;
}
let enable = flag.starts_with('+');
let flag_name = if enable || flag.starts_with('-') {
&flag[1..]
} else {
flag
};
match flag_name {
"sse" => self.has_sse = enable,
"sse2" => self.has_sse2 = enable,
"sse3" => self.has_sse3 = enable,
"ssse3" => self.has_ssse3 = enable,
"sse4.1" | "sse41" => self.has_sse41 = enable,
"sse4.2" | "sse42" => self.has_sse42 = enable,
"avx" => self.has_avx = enable,
"avx2" => self.has_avx2 = enable,
"avx512f" => self.has_avx512f = enable,
"avx512bw" => self.has_avx512bw = enable,
"avx512dq" => self.has_avx512dq = enable,
"avx512vl" => self.has_avx512vl = enable,
"fma" | "fma3" => self.has_fma = enable,
"bmi" => self.has_bmi = enable,
"bmi2" => self.has_bmi2 = enable,
"lzcnt" => self.has_lzcnt = enable,
"popcnt" => self.has_popcnt = enable,
"movbe" => self.has_movbe = enable,
"rdrand" => self.has_rdrand = enable,
"rdseed" => self.has_rdseed = enable,
"aes" => self.has_aes = enable,
"sha" => self.has_sha = enable,
"mpx" => self.has_mpx = enable,
"sgx" => self.has_sgx = enable,
"cet" | "shstk" => self.has_cet = enable,
"xsave" => self.has_xsave = enable,
"xsaveopt" => self.has_xsaveopt = enable,
"xsaves" => self.has_xsaves = enable,
"fsgsbase" => self.has_fsgsbase = enable,
"64bit" | "mode64" => self.is_64bit = enable,
"slow-3ops-lea" => self.slow_3ops_lea = enable,
"slow-divide" => self.slow_divide = enable,
"pad-short-functions" => self.pad_short_functions = enable,
"prefetchw" => self.prefetchw = enable,
"vaes" => self.has_vaes = enable,
"vpclmulqdq" => self.has_vpclmulqdq = enable,
"gfni" => self.has_gfni = enable,
"avxvnni" | "avx-vnni" => self.has_avxvnni = enable,
"avxifma" | "avx-ifma" => self.has_avxifma = enable,
"amx-bf16" => self.has_amx_bf16 = enable,
"amx-tile" => self.has_amx_tile = enable,
"amx-int8" => self.has_amx_int8 = enable,
"waitpkg" => self.has_waitpkg = enable,
"pku" => self.has_pku = enable,
"clflushopt" => self.has_clflushopt = enable,
"clwb" => self.has_clwb = enable,
"rdpid" => self.has_rdpid = enable,
_ => {
self.feature_names.push(flag_name.to_string());
}
}
}
pub fn has_feature(&self, name: &str) -> bool {
match name {
"sse" => self.has_sse,
"sse2" => self.has_sse2,
"sse3" => self.has_sse3,
"ssse3" => self.has_ssse3,
"sse4.1" => self.has_sse41,
"sse4.2" => self.has_sse42,
"avx" => self.has_avx,
"avx2" => self.has_avx2,
"avx512f" => self.has_avx512f,
"avx512bw" => self.has_avx512bw,
"avx512dq" => self.has_avx512dq,
"avx512vl" => self.has_avx512vl,
"fma" => self.has_fma,
"bmi" => self.has_bmi,
"bmi2" => self.has_bmi2,
"64bit" => self.is_64bit,
_ => false,
}
}
pub fn get_sched_model(&self) -> &str {
&self.sched_model
}
}
impl Default for X86MCSubtargetInfo {
fn default() -> Self {
Self::new("generic", vec!["+sse2".into()])
}
}
#[derive(Debug, Clone)]
pub struct X86MCInstrAnalysis {
pub instr_info: X86MCInstrInfo,
pub reg_info: X86MCRegisterInfo,
pub sti: X86MCSubtargetInfo,
}
impl X86MCInstrAnalysis {
pub fn new(
instr_info: X86MCInstrInfo,
reg_info: X86MCRegisterInfo,
sti: X86MCSubtargetInfo,
) -> Self {
Self {
instr_info,
reg_info,
sti,
}
}
pub fn evaluate_branch(&self, inst: &MCInst, addr: u64, size: u64) -> Option<u64> {
if !self.is_branch(inst) {
return None;
}
if inst.opcode == 15 || inst.opcode >= 16 && inst.opcode <= 17 {
for op in &inst.operands {
if let MCOperand::Imm(offset) = op {
let target = addr.wrapping_add(size).wrapping_add(*offset as u64);
return Some(target);
}
}
}
None
}
pub fn is_branch(&self, inst: &MCInst) -> bool {
inst.flags.is_branch
}
pub fn is_unconditional_branch(&self, inst: &MCInst) -> bool {
inst.flags.is_branch && !inst.flags.is_compare
}
pub fn is_conditional_branch(&self, inst: &MCInst) -> bool {
inst.flags.is_branch && inst.flags.is_compare
}
pub fn is_call(&self, inst: &MCInst) -> bool {
inst.flags.is_call
}
pub fn is_return(&self, inst: &MCInst) -> bool {
inst.flags.is_return
}
pub fn is_indirect_branch(&self, _inst: &MCInst) -> bool {
false
}
pub fn is_barrier(&self, inst: &MCInst) -> bool {
inst.flags.is_terminator || inst.flags.is_return
}
pub fn is_terminator(&self, inst: &MCInst) -> bool {
inst.flags.is_terminator
}
pub fn may_load(&self, inst: &MCInst) -> bool {
inst.flags.may_load
}
pub fn may_store(&self, inst: &MCInst) -> bool {
inst.flags.may_store
}
pub fn has_side_effects(&self, inst: &MCInst) -> bool {
inst.flags.has_side_effects
}
pub fn get_latency(&self, opcode: u16) -> u32 {
for wl in &self.sti.write_latencies {
if wl.opcode == opcode {
return wl.cycles;
}
}
match opcode {
1 => 1, 2 => 1, 3 => 1, 4 => 3, 5 => 20, 6 => 1, 7 => 1, 8 => 1, 9 => 1, 10 => 1, 18 => 1, 19 => 1, 24 => 0, 35 => 100, 14 => 1, _ => 1,
}
}
pub fn can_speculate(&self, inst: &MCInst) -> bool {
!self.has_side_effects(inst) && !self.is_branch(inst) && self.may_load(inst)
}
pub fn are_identical(&self, a: &MCInst, b: &MCInst) -> bool {
if a.opcode != b.opcode {
return false;
}
if a.operands.len() != b.operands.len() {
return false;
}
for (op_a, op_b) in a.operands.iter().zip(b.operands.iter()) {
use MCOperand::*;
match (op_a, op_b) {
(Reg(ra), Reg(rb)) if ra == rb => {}
(Imm(ia), Imm(ib)) if ia == ib => {}
(FpImm(fa), FpImm(fb)) if fa.to_bits() == fb.to_bits() => {}
(Expr(ea), Expr(eb)) if format!("{:?}", ea) == format!("{:?}", eb) => {}
_ => return false,
}
}
true
}
pub fn get_def_regs(&self, inst: &MCInst) -> Vec<u16> {
let mut defs = Vec::new();
if let Some(desc) = self.instr_info.get_desc(inst.opcode as u16) {
let num_defs = desc.num_defs as usize;
for i in 0..num_defs.min(inst.operands.len()) {
if let MCOperand::Reg(r) = &inst.operands[i] {
defs.push(*r as u16);
}
}
}
defs
}
pub fn get_use_regs(&self, inst: &MCInst) -> Vec<u16> {
let mut uses = Vec::new();
let start_idx = if let Some(desc) = self.instr_info.get_desc(inst.opcode as u16) {
desc.num_defs as usize
} else {
0
};
for i in start_idx..inst.operands.len() {
if let MCOperand::Reg(r) = &inst.operands[i] {
uses.push(*r as u16);
}
}
uses
}
pub fn get_instruction_size(&self, inst: &MCInst) -> u32 {
if let Some(desc) = self.instr_info.get_desc(inst.opcode as u16) {
desc.size as u32
} else {
let base: u32 = if inst.opcode >= 256 { 2 } else { 1 };
let has_modrm = inst.operands.len() >= 2;
let modrm_size: u32 = if has_modrm { 1 } else { 0 };
let sib_size: u32 = 0; let disp_size: u32 = if has_modrm { 4 } else { 0 };
let imm_size: u32 = if inst.operands.len() > 0 {
if let MCOperand::Imm(v) = &inst.operands[inst.operands.len() - 1] {
if *v >= -(1i64 << 31) && *v <= (1i64 << 31) - 1 {
if *v >= 0 && *v <= 0xFFFFFFFF {
4
} else {
8
}
} else {
1
}
} else {
0
}
} else {
0
};
base + modrm_size + sib_size + disp_size + imm_size
}
}
}
impl Default for X86MCInstrAnalysis {
fn default() -> Self {
Self::new(
X86MCInstrInfo::default(),
X86MCRegisterInfo::default(),
X86MCSubtargetInfo::default(),
)
}
}
pub struct X86MCAsmPrinter {
pub base: X86AsmPrinter,
pub instr_info: X86MCInstrInfo,
pub reg_info: X86MCRegisterInfo,
pub sti: X86MCSubtargetInfo,
pub output: String,
pub indent: usize,
pub current_section: Option<String>,
pub verbose_asm: bool,
pub show_encoding: bool,
pub show_inst_size: bool,
}
impl X86MCAsmPrinter {
pub fn new(syntax: AsmSyntax, sti: X86MCSubtargetInfo) -> Self {
let base = X86AsmPrinter::new(
X86Subtarget::new("x86_64-unknown-linux-gnu", "generic", ""),
syntax,
);
Self {
base,
instr_info: X86MCInstrInfo::default(),
reg_info: X86MCRegisterInfo::default(),
sti,
output: String::new(),
indent: 0,
current_section: None,
verbose_asm: false,
show_encoding: false,
show_inst_size: false,
}
}
fn write(&mut self, s: impl AsRef<str>) {
self.output.push_str(s.as_ref());
}
fn writeln(&mut self, s: impl AsRef<str>) {
for _ in 0..self.indent {
self.output.push('\t');
}
self.output.push_str(s.as_ref());
self.output.push('\n');
}
pub fn emit_module_header(&mut self, ctx: &X86MCContext) {
self.writeln(format!("\t.file\t\"output.s\"",));
if !ctx.target_triple.is_empty() {
self.writeln(format!("\t.target_triple\t\"{}\"", ctx.target_triple));
}
if ctx.is_pic {
self.writeln("\t.code64");
} else {
self.writeln("\t.code64");
}
self.writeln("");
}
pub fn emit_section_directive(&mut self, section: &MCSection) {
match §ion.section_type {
MCSectionType::Text => {
self.writeln("\t.text");
self.current_section = Some(".text".into());
}
MCSectionType::Data => {
self.writeln("\t.data");
self.current_section = Some(".data".into());
}
MCSectionType::Rodata => {
self.writeln("\t.section\t.rodata");
self.current_section = Some(".rodata".into());
}
MCSectionType::Bss => {
self.writeln("\t.bss");
self.current_section = Some(".bss".into());
}
MCSectionType::Custom => {
self.writeln(format!("\t.section\t{}", section.name));
self.current_section = Some(section.name.clone());
}
}
self.writeln("");
}
pub fn emit_elf_section_directive(&mut self, section: &MCSectionELF) {
let mut directive = format!("\t.section\t{}", section.name);
if section.is_allocatable() {
directive.push_str(",\"a");
if section.is_writable() {
directive.push('w');
}
if section.is_executable() {
directive.push('x');
}
directive.push('"');
}
self.writeln(directive);
self.current_section = Some(section.name.clone());
self.writeln("");
}
pub fn emit_coff_section_directive(&mut self, section: &MCSectionCOFF) {
let mut directive = format!("\t.section\t{}", section.name);
if section.is_executable() {
directive.push_str(",\"x\"");
} else if section.is_writable() {
directive.push_str(",\"w\"");
}
self.writeln(directive);
self.current_section = Some(section.name.clone());
self.writeln("");
}
pub fn emit_instruction(&mut self, inst: &MCInst) {
let mnemonic = self
.instr_info
.get_mnemonic(inst.opcode as u16)
.unwrap_or("???")
.to_lowercase();
let mut line = String::new();
for _ in 0..self.indent + 1 {
line.push('\t');
}
match self.base.syntax {
AsmSyntax::ATT => {
line.push_str(&mnemonic);
let mut ops = Vec::new();
for op in &inst.operands {
ops.push(self.format_operand_att(op));
}
if !ops.is_empty() {
line.push('\t');
line.push_str(&ops.join(", "));
}
}
AsmSyntax::Intel => {
line.push_str(&mnemonic);
let mut ops = Vec::new();
for op in &inst.operands {
ops.push(self.format_operand_intel(op));
}
if !ops.is_empty() {
line.push('\t');
line.push_str(&ops.join(", "));
}
}
}
if self.show_encoding {
let encoder = X86MCEncoder::new(
X86Mode::Mode64,
X86Subtarget::new("x86_64-unknown-linux-gnu", "generic", ""),
);
let mi = mc_inst_to_machine_instr(inst);
let encoded = encoder.encode_instruction(&mi);
line.push_str(" # encoding: [");
for (i, b) in encoded.iter().enumerate() {
if i > 0 {
line.push(',');
}
line.push_str(&format!("0x{:02x}", b));
}
line.push(']');
}
self.writeln(line);
}
fn format_operand_att(&self, op: &MCOperand) -> String {
match op {
MCOperand::Reg(r) => {
let reg_num = *r as u16;
format!("%{}", reg_name(reg_num))
}
MCOperand::Imm(v) => format!("${}", v),
MCOperand::FpImm(v) => {
if *v == 0.0 {
"$0.0".into()
} else {
format!("${}", v)
}
}
MCOperand::Expr(e) => format!("${}", e),
MCOperand::Inst(i) => {
let inst = i.as_ref();
format!("<inst {}>", inst.opcode)
}
}
}
fn format_operand_intel(&self, op: &MCOperand) -> String {
match op {
MCOperand::Reg(r) => {
let reg_num = *r as u16;
reg_name(reg_num).to_uppercase()
}
MCOperand::Imm(v) => format!("{}", v),
MCOperand::FpImm(v) => {
if *v == 0.0 {
"0.0".into()
} else {
format!("{}", v)
}
}
MCOperand::Expr(e) => format!("{}", e),
MCOperand::Inst(i) => {
let inst = i.as_ref();
format!("<inst {}>", inst.opcode)
}
}
}
pub fn emit_label(&mut self, name: &str) {
self.writeln(format!("{}:", name));
}
pub fn emit_local_label(&mut self, name: &str) {
self.writeln(format!(".L{}:", name));
}
pub fn emit_global(&mut self, name: &str) {
self.writeln(format!("\t.globl\t{}", name));
}
pub fn emit_weak(&mut self, name: &str) {
self.writeln(format!("\t.weak\t{}", name));
}
pub fn emit_local(&mut self, name: &str) {
self.writeln(format!("\t.local\t{}", name));
}
pub fn emit_type(&mut self, name: &str, sym_type: &str) {
self.writeln(format!("\t.type\t{},@{}", name, sym_type));
}
pub fn emit_size(&mut self, name: &str, size: u64) {
self.writeln(format!("\t.size\t{}, {}", name, size));
}
pub fn emit_alignment(&mut self, alignment: u32) {
if alignment > 1 {
self.writeln(format!("\t.align\t{}", alignment));
}
}
pub fn emit_p2align(&mut self, pow2: u32) {
self.writeln(format!("\t.p2align\t{}", pow2));
}
pub fn emit_balign(&mut self, alignment: u32, fill_value: u8) {
self.writeln(format!("\t.balign\t{}, {}", alignment, fill_value));
}
pub fn emit_zero_fill(&mut self, count: u64) {
self.writeln(format!("\t.zero\t{}", count));
}
pub fn emit_byte(&mut self, value: u8) {
self.writeln(format!("\t.byte\t{}", value));
}
pub fn emit_short(&mut self, value: u16) {
self.writeln(format!("\t.short\t{}", value));
}
pub fn emit_long(&mut self, value: u32) {
self.writeln(format!("\t.long\t{}", value));
}
pub fn emit_quad(&mut self, value: u64) {
self.writeln(format!("\t.quad\t{}", value));
}
pub fn emit_ascii(&mut self, text: &str) {
self.writeln(format!("\t.ascii\t\"{}\"", text));
}
pub fn emit_asciz(&mut self, text: &str) {
self.writeln(format!("\t.asciz\t\"{}\"", text));
}
pub fn emit_fill(&mut self, count: u64, size: u64, value: u64) {
self.writeln(format!("\t.fill\t{}, {}, {}", count, size, value));
}
pub fn emit_org(&mut self, location: u64) {
self.writeln(format!("\t.org\t{}", location));
}
pub fn emit_equ(&mut self, name: &str, value: u64) {
self.writeln(format!("\t.equ\t{}, {}", name, value));
}
pub fn emit_set(&mut self, name: &str, value: u64) {
self.writeln(format!("\t.set\t{}, {}", name, value));
}
pub fn emit_skip(&mut self, bytes: u64) {
self.writeln(format!("\t.skip\t{}", bytes));
}
pub fn emit_comment(&mut self, text: &str) {
self.writeln(format!("\t# {}", text));
}
pub fn emit_blank_line(&mut self) {
self.writeln("");
}
pub fn emit_cfi(&mut self, cfi: &X86CFIInstruction) {
self.writeln(format!("\t{}", cfi));
}
pub fn emit_function_prologue(&mut self, name: &str) {
self.emit_blank_line();
self.emit_global(name);
self.emit_type(name, "function");
self.emit_label(name);
self.emit_cfi(&X86CFIInstruction::StartProc);
self.emit_instruction(&make_mc_inst(11, vec![MCOperand::Reg(RBP as u32)])); self.emit_instruction(&make_mc_inst(
1, vec![MCOperand::Reg(RBP as u32), MCOperand::Reg(RSP as u32)],
)); self.emit_cfi(&X86CFIInstruction::DefCfa {
register: RBP,
offset: 16,
});
self.emit_cfi(&X86CFIInstruction::Offset {
register: RBP,
offset: -16,
});
}
pub fn emit_function_epilogue(&mut self, name: &str) {
self.emit_instruction(&make_mc_inst(12, vec![MCOperand::Reg(RBP as u32)])); self.emit_cfi(&X86CFIInstruction::DefCfa {
register: RSP,
offset: 8,
});
self.emit_instruction(&make_mc_inst(14, vec![])); self.emit_cfi(&X86CFIInstruction::EndProc);
self.emit_size(name, 0); self.emit_blank_line();
}
pub fn emit_file_directive(&mut self, file_id: u32, directory: &str, filename: &str) {
self.writeln(format!(
"\t.file\t{} \"{}/{}\"",
file_id, directory, filename
));
}
pub fn emit_loc_directive(&mut self, file_id: u32, line: u32, column: u32) {
self.writeln(format!("\t.loc\t{} {} {}", file_id, line, column));
}
pub fn emit_note_gnu_stack(&mut self, exec: bool) {
if exec {
self.writeln("\t.section\t.note.GNU-stack,\"\",@progbits");
} else {
self.writeln("\t.section\t.note.GNU-stack,\"\",@progbits\n\t.previous");
}
}
pub fn emit_module(&mut self, ctx: &X86MCContext, sections: &[(String, &[MCInst])]) {
self.emit_module_header(ctx);
for (sec_name, insts) in sections {
self.writeln(format!("\t{}", sec_name));
self.writeln("");
for inst in *insts {
self.emit_instruction(inst);
}
self.writeln("");
}
if ctx.use_cfi {
for cfi in &ctx.cfi_instructions {
self.emit_cfi(cfi);
}
}
if ctx.gen_dwarf {
for file_entry in &ctx.debug_files {
self.emit_file_directive(
file_entry.file_id,
&file_entry.directory,
&file_entry.name,
);
}
for line_entry in &ctx.debug_lines {
self.emit_loc_directive(line_entry.file_id, line_entry.line, line_entry.column);
}
}
if ctx.no_exec_stack {
self.emit_note_gnu_stack(false);
}
}
pub fn finish(&self) -> &str {
&self.output
}
}
impl fmt::Debug for X86MCAsmPrinter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86MCAsmPrinter")
.field("instr_info", &self.instr_info)
.field("sti", &self.sti)
.field("output", &self.output)
.finish()
}
}
impl Default for X86MCAsmPrinter {
fn default() -> Self {
Self::new(AsmSyntax::ATT, X86MCSubtargetInfo::default())
}
}
pub struct X86MCCodeEmitter {
pub encoder: X86MCEncoder,
pub full_encoder: X86FullMCEncoder,
pub fixups: Vec<X86MCFixup>,
pub current_offset: u64,
pub is_pic: bool,
pub ctx: Option<X86MCContext>,
pub emit_nop_padding: bool,
pub label_locations: HashMap<String, u64>,
pub pending_fixups: Vec<(u64, X86MCFixup)>,
}
impl fmt::Debug for X86MCCodeEmitter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("X86MCCodeEmitter")
.field("current_offset", &self.current_offset)
.field("is_pic", &self.is_pic)
.field("fixups", &self.fixups.len())
.finish()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct X86MCFixup {
pub offset: u64,
pub kind: X86FixupKind,
pub symbol: Option<String>,
pub value: i64,
pub size: u8,
pub is_pcrel: bool,
pub loc: Option<(u32, u32)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86FixupKind {
Abs32,
Abs64,
Abs8,
Abs16,
PcRel32,
PcRel8,
PcRel16,
RipRel4Byte,
RipRel4ByteMovqLoad,
Signed4Byte,
Unsigned4Byte,
Branch4BytePcrel,
GotPcRel,
Plt32,
GotOff,
TlsGd,
TlsLd,
GotTpOff,
TpOff32,
DtpOff32,
FirstVariantKind,
SizeDirective,
TlsDesc,
TlsDescCall,
SecRel32,
Custom(u32),
}
impl X86FixupKind {
pub fn size(&self) -> u8 {
match self {
Self::Abs8 | Self::PcRel8 => 1,
Self::Abs16 | Self::PcRel16 => 2,
Self::Abs32
| Self::PcRel32
| Self::RipRel4Byte
| Self::RipRel4ByteMovqLoad
| Self::Signed4Byte
| Self::Unsigned4Byte
| Self::Branch4BytePcrel
| Self::GotPcRel
| Self::Plt32
| Self::GotOff
| Self::TlsGd
| Self::TlsLd
| Self::GotTpOff
| Self::TpOff32
| Self::DtpOff32
| Self::SecRel32
| Self::SizeDirective
| Self::TlsDesc
| Self::TlsDescCall => 4,
Self::Abs64 => 8,
Self::FirstVariantKind => 0,
Self::Custom(_) => 4,
}
}
pub fn is_pc_relative(&self) -> bool {
matches!(
self,
Self::PcRel32
| Self::PcRel8
| Self::PcRel16
| Self::RipRel4Byte
| Self::RipRel4ByteMovqLoad
| Self::Branch4BytePcrel
| Self::GotPcRel
| Self::Plt32
)
}
pub fn to_elf_relocation(&self) -> Option<u32> {
match self {
Self::Abs32 => Some(R_X86_64_32),
Self::Abs64 => Some(R_X86_64_64),
Self::Abs8 => Some(R_X86_64_8),
Self::Abs16 => Some(R_X86_64_16),
Self::PcRel32 => Some(R_X86_64_PC32),
Self::PcRel8 => Some(R_X86_64_PC8),
Self::PcRel16 => Some(R_X86_64_PC16),
Self::Signed4Byte => Some(R_X86_64_32S),
Self::Plt32 => Some(R_X86_64_PLT32),
Self::GotPcRel => Some(R_X86_64_GOTPCREL),
Self::GotOff => Some(R_X86_64_GOTOFF64),
Self::TlsGd => Some(R_X86_64_TLSDESC),
_ => None,
}
}
}
impl fmt::Display for X86FixupKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Abs32 => write!(f, "abs32"),
Self::Abs64 => write!(f, "abs64"),
Self::Abs8 => write!(f, "abs8"),
Self::Abs16 => write!(f, "abs16"),
Self::PcRel32 => write!(f, "pcrel32"),
Self::PcRel8 => write!(f, "pcrel8"),
Self::PcRel16 => write!(f, "pcrel16"),
Self::RipRel4Byte => write!(f, "riprel4byte"),
Self::RipRel4ByteMovqLoad => write!(f, "riprel4byte_movqload"),
Self::Signed4Byte => write!(f, "signed4byte"),
Self::Unsigned4Byte => write!(f, "unsigned4byte"),
Self::Branch4BytePcrel => write!(f, "branch4byte_pcrel"),
Self::GotPcRel => write!(f, "gotpcrel"),
Self::Plt32 => write!(f, "plt32"),
Self::GotOff => write!(f, "gotoff"),
Self::TlsGd => write!(f, "tlsgd"),
Self::TlsLd => write!(f, "tlsld"),
Self::GotTpOff => write!(f, "gottpoff"),
Self::TpOff32 => write!(f, "tpoff32"),
Self::DtpOff32 => write!(f, "dtpoff32"),
Self::FirstVariantKind => write!(f, "firstvariant"),
Self::SizeDirective => write!(f, "size"),
Self::TlsDesc => write!(f, "tlsdesc"),
Self::TlsDescCall => write!(f, "tlsdesccall"),
Self::SecRel32 => write!(f, "secrel32"),
Self::Custom(n) => write!(f, "custom({})", n),
}
}
}
impl X86MCCodeEmitter {
pub fn new(mode: X86Mode, sti: X86MCSubtargetInfo) -> Self {
let subtarget = X86Subtarget::new("x86_64-unknown-linux-gnu", "generic", "");
let encoder = X86MCEncoder::new(mode, subtarget.clone());
let full_encoder = X86FullMCEncoder::new(mode, subtarget.clone());
Self {
encoder,
full_encoder,
fixups: Vec::new(),
current_offset: 0,
is_pic: false,
ctx: None,
emit_nop_padding: true,
label_locations: HashMap::new(),
pending_fixups: Vec::new(),
}
}
pub fn set_context(&mut self, ctx: X86MCContext) {
self.is_pic = ctx.is_pic;
self.ctx = Some(ctx);
}
pub fn encode_instruction(&mut self, inst: &MCInst) -> Vec<u8> {
let mi = mc_inst_to_machine_instr(inst);
let encoded = self.encoder.encode_instruction(&mi);
self.current_offset += encoded.len() as u64;
encoded
}
pub fn emit_instruction(&mut self, inst: &MCInst) -> Vec<u8> {
let bytes = self.encode_instruction(inst);
for op in &inst.operands {
match op {
MCOperand::Expr(expr) => {
if let Some(sym_name) = expr.get_symbol_name() {
let fixup = X86MCFixup {
offset: self.current_offset - bytes.len() as u64,
kind: X86FixupKind::PcRel32,
symbol: Some(sym_name.to_string()),
value: 0,
size: 4,
is_pcrel: true,
loc: inst.loc.map(|l| (l.line, l.column)),
};
self.fixups.push(fixup);
}
}
_ => {}
}
}
bytes
}
pub fn emit_data(&mut self, data: &[u8]) {
self.current_offset += data.len() as u64;
}
pub fn emit_alignment(&mut self, alignment: u32) -> Vec<u8> {
let current = self.current_offset;
let align = alignment as u64;
let pad = (align - (current % align)) % align;
let padding = if self.emit_nop_padding && pad > 0 {
self.generate_nop_padding(pad as usize)
} else {
vec![0u8; pad as usize]
};
self.current_offset += pad;
padding
}
fn generate_nop_padding(&self, size: usize) -> Vec<u8> {
let mut padding = Vec::with_capacity(size);
let mut remaining = size;
while remaining > 0 {
match remaining {
1 => {
padding.push(0x90);
remaining -= 1;
}
2 => {
padding.extend_from_slice(&[0x66, 0x90]);
remaining -= 2;
}
3 => {
padding.extend_from_slice(&[0x0F, 0x1F, 0x00]);
remaining -= 3;
}
4 => {
padding.extend_from_slice(&[0x0F, 0x1F, 0x40, 0x00]);
remaining -= 4;
}
5 => {
padding.extend_from_slice(&[0x0F, 0x1F, 0x44, 0x00, 0x00]);
remaining -= 5;
}
6 => {
padding.extend_from_slice(&[0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00]);
remaining -= 6;
}
7 => {
padding.extend_from_slice(&[0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00]);
remaining -= 7;
}
8 => {
padding.extend_from_slice(&[0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]);
remaining -= 8;
}
9 => {
padding
.extend_from_slice(&[0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]);
remaining -= 9;
}
_ if remaining >= 15 => {
padding.extend_from_slice(&[
0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x0F,
0x1F, 0x44, 0x00, 0x00,
]);
remaining -= 15;
}
_ => {
padding.push(0x90);
remaining -= 1;
}
}
}
padding
}
pub fn record_label(&mut self, name: &str) {
self.label_locations
.insert(name.to_string(), self.current_offset);
}
pub fn record_fixup(&mut self, fixup: X86MCFixup) {
self.pending_fixups.push((self.current_offset, fixup));
}
pub fn resolve_fixups(&mut self) {
for (offset, ref mut fixup) in &mut self.pending_fixups {
if let Some(ref sym_name) = fixup.symbol {
if let Some(label_offset) = self.label_locations.get(sym_name) {
fixup.value = *label_offset as i64;
}
}
}
for (_, fixup) in self.pending_fixups.drain(..) {
self.fixups.push(fixup);
}
}
pub fn reset(&mut self) {
self.fixups.clear();
self.current_offset = 0;
self.label_locations.clear();
self.pending_fixups.clear();
}
}
impl Default for X86MCCodeEmitter {
fn default() -> Self {
Self::new(X86Mode::Mode64, X86MCSubtargetInfo::default())
}
}
#[derive(Debug)]
pub struct X86MCObjectWriter {
pub format: ObjectFormat,
pub target_triple: String,
pub sections: Vec<X86ObjectSection>,
pub symbols: Vec<X86ObjectSymbol>,
pub relocations: Vec<X86ObjectRelocation>,
pub section_alignments: HashMap<String, u64>,
pub debug_sections: Vec<String>,
pub fixups: Vec<X86MCFixup>,
pub emit_gnu_stack: bool,
pub emit_build_id: bool,
pub build_id: Option<[u8; 20]>,
pub uuid: Option<[u8; 16]>,
}
#[derive(Debug, Clone)]
pub struct X86ObjectSection {
pub name: String,
pub data: Vec<u8>,
pub flags: X86SectionFlags,
pub section_type: X86SectionType,
pub alignment: u64,
pub relocations: Vec<X86ObjectRelocation>,
}
#[derive(Debug, Clone, Default)]
pub struct X86SectionFlags {
pub alloc: bool,
pub write: bool,
pub exec: bool,
pub merge: bool,
pub strings: bool,
pub tls: bool,
pub exclude: bool,
pub debug: bool,
pub comdat: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum X86SectionType {
Text,
Data,
Rodata,
Bss,
InitArray,
FiniArray,
DebugInfo,
DebugAbbrev,
DebugLine,
DebugFrame,
DebugStr,
DebugLoc,
DebugRanges,
DebugMacro,
DebugAddr,
DebugStrOffsets,
EhFrame,
GccExceptTable,
TlsData,
TlsBss,
Note,
Comment,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct X86ObjectSymbol {
pub name: String,
pub value: u64,
pub size: u64,
pub section_index: u32,
pub is_global: bool,
pub is_weak: bool,
pub is_undefined: bool,
pub is_function: bool,
pub is_object: bool,
pub is_tls: bool,
pub is_absolute: bool,
}
#[derive(Debug, Clone)]
pub struct X86ObjectRelocation {
pub offset: u64,
pub symbol_index: u32,
pub rel_type: u32,
pub addend: i64,
pub is_pcrel: bool,
pub size: u8,
}
impl X86MCObjectWriter {
pub fn new(format: ObjectFormat, target_triple: impl Into<String>) -> Self {
Self {
format,
target_triple: target_triple.into(),
sections: Vec::new(),
symbols: Vec::new(),
relocations: Vec::new(),
section_alignments: HashMap::new(),
debug_sections: Vec::new(),
fixups: Vec::new(),
emit_gnu_stack: true,
emit_build_id: false,
build_id: None,
uuid: None,
}
}
pub fn new_elf(target_triple: impl Into<String>) -> Self {
Self::new(ObjectFormat::ELF, target_triple)
}
pub fn new_coff(target_triple: impl Into<String>) -> Self {
Self::new(ObjectFormat::COFF, target_triple)
}
pub fn new_macho(target_triple: impl Into<String>) -> Self {
Self::new(ObjectFormat::MachO, target_triple)
}
pub fn add_section(&mut self, mut section: X86ObjectSection) -> usize {
let idx = self.sections.len();
if section.alignment > 0 {
self.section_alignments
.insert(section.name.clone(), section.alignment);
}
self.sections.push(section);
idx
}
pub fn add_bss_section(&mut self, name: impl Into<String>, size: u64, alignment: u64) -> usize {
let flags = X86SectionFlags {
alloc: true,
write: true,
..Default::default()
};
let section = X86ObjectSection {
name: name.into(),
data: Vec::new(),
flags,
section_type: X86SectionType::Bss,
alignment,
relocations: Vec::new(),
};
self.add_section(section)
}
pub fn add_symbol(&mut self, mut symbol: X86ObjectSymbol) -> u32 {
let idx = self.symbols.len() as u32;
self.symbols.push(symbol);
idx
}
pub fn add_relocation(&mut self, section_idx: usize, relocation: X86ObjectRelocation) {
if section_idx < self.sections.len() {
self.sections[section_idx].relocations.push(relocation);
}
}
pub fn add_debug_section(&mut self, name: impl Into<String>) {
self.debug_sections.push(name.into());
}
pub fn set_section_alignment(&mut self, name: impl Into<String>, alignment: u64) {
self.section_alignments.insert(name.into(), alignment);
}
pub fn import_fixups(&mut self, fixups: &[X86MCFixup]) {
self.fixups.extend_from_slice(fixups);
}
pub fn write(&self) -> Vec<u8> {
match self.format {
ObjectFormat::ELF => self.write_elf64(),
ObjectFormat::COFF => self.write_coff64(),
ObjectFormat::MachO => self.write_macho64(),
_ => {
self.write_elf64()
}
}
}
fn write_elf64(&self) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&self.build_elf_header());
let shdrs = self.build_elf_section_headers();
let symtab = self.build_elf_symtab();
let strtab = self.build_elf_strtab();
let shstrtab = self.build_elf_shstrtab();
let ehdr_size = 64u64; let shdr_size = 64u64;
let num_sections = 4u64 + self.sections.len() as u64
+ 3;
let section_offsets: Vec<u64> = self.compute_section_offsets(ehdr_size);
for (i, section) in self.sections.iter().enumerate() {
while buf.len() < section_offsets[i] as usize {
buf.push(0);
}
if section.section_type != X86SectionType::Bss {
buf.extend_from_slice(§ion.data);
}
}
let symtab_off = buf.len() as u64;
buf.extend_from_slice(&symtab);
let strtab_off = buf.len() as u64;
buf.extend_from_slice(&strtab);
let shstrtab_off = buf.len() as u64;
buf.extend_from_slice(&shstrtab);
let rela_offsets: Vec<u64> = self
.sections
.iter()
.map(|s| {
let off = buf.len() as u64;
let rela_data = self.build_elf_rela_section(s);
buf.extend_from_slice(&rela_data);
off
})
.collect();
let final_shdrs = self.build_elf_section_headers_with_offsets(
§ion_offsets,
symtab_off,
strtab_off,
shstrtab_off,
&rela_offsets,
);
let shdr_table_off = buf.len() as u64;
buf.extend_from_slice(&final_shdrs);
if buf.len() >= 48 {
let shoff_bytes = shdr_table_off.to_le_bytes();
buf[40..48].copy_from_slice(&shoff_bytes);
}
buf
}
fn compute_section_offsets(&self, start: u64) -> Vec<u64> {
let mut offsets = Vec::new();
let mut current = start;
for section in &self.sections {
offsets.push(current);
if section.section_type != X86SectionType::Bss {
current += section.data.len() as u64;
}
}
offsets
}
fn build_elf_header(&self) -> Vec<u8> {
let mut hdr = [0u8; 64];
hdr[0..4].copy_from_slice(&[0x7f, b'E', b'L', b'F']); hdr[4] = 2; hdr[5] = 1; hdr[6] = 1; hdr[7] = 0;
hdr[16..18].copy_from_slice(&1u16.to_le_bytes());
hdr[18..20].copy_from_slice(&62u16.to_le_bytes());
hdr[20..24].copy_from_slice(&1u32.to_le_bytes());
hdr[52..54].copy_from_slice(&64u16.to_le_bytes());
hdr[58..60].copy_from_slice(&64u16.to_le_bytes());
hdr.to_vec()
}
fn build_elf_section_headers(&self) -> Vec<u8> {
let mut shdrs = Vec::new();
let num_sections = 1 + self.sections.len() + self.sections.len() + 3;
shdrs.extend_from_slice(&[0u8; 64]);
for section in &self.sections {
let flags = self.elf_section_flags(§ion.flags);
let stype = self.elf_section_type(§ion.section_type);
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&stype.to_le_bytes());
shdr[8..16].copy_from_slice(&flags.to_le_bytes());
let size = if section.section_type == X86SectionType::Bss {
0u64
} else {
section.data.len() as u64
};
shdr[32..40].copy_from_slice(&size.to_le_bytes());
shdr[48..56].copy_from_slice(§ion.alignment.to_le_bytes());
shdrs.extend_from_slice(&shdr);
}
for _ in &self.sections {
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&4u32.to_le_bytes());
shdr[8..16].copy_from_slice(&0u64.to_le_bytes());
shdr[48..56].copy_from_slice(&8u64.to_le_bytes());
shdr[56..64].copy_from_slice(&24u64.to_le_bytes());
shdrs.extend_from_slice(&shdr);
}
{
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&2u32.to_le_bytes()); shdr[48..56].copy_from_slice(&8u64.to_le_bytes()); shdr[56..64].copy_from_slice(&24u64.to_le_bytes()); shdrs.extend_from_slice(&shdr);
}
{
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&3u32.to_le_bytes()); shdr[48..56].copy_from_slice(&1u64.to_le_bytes());
shdrs.extend_from_slice(&shdr);
}
{
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&3u32.to_le_bytes()); shdr[48..56].copy_from_slice(&1u64.to_le_bytes());
shdrs.extend_from_slice(&shdr);
}
shdrs
}
fn build_elf_section_headers_with_offsets(
&self,
section_offsets: &[u64],
symtab_off: u64,
strtab_off: u64,
shstrtab_off: u64,
rela_offsets: &[u64],
) -> Vec<u8> {
let num_data = self.sections.len();
let mut shdrs = Vec::new();
shdrs.extend_from_slice(&[0u8; 64]);
for (i, section) in self.sections.iter().enumerate() {
let flags = self.elf_section_flags(§ion.flags);
let stype = self.elf_section_type(§ion.section_type);
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&stype.to_le_bytes());
shdr[8..16].copy_from_slice(&flags.to_le_bytes());
shdr[24..32].copy_from_slice(§ion_offsets[i].to_le_bytes());
let size = if section.section_type == X86SectionType::Bss {
0u64
} else {
section.data.len() as u64
};
shdr[32..40].copy_from_slice(&size.to_le_bytes());
shdr[48..56].copy_from_slice(§ion.alignment.to_le_bytes());
shdrs.extend_from_slice(&shdr);
}
for i in 0..num_data {
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&4u32.to_le_bytes()); shdr[24..32].copy_from_slice(&rela_offsets[i].to_le_bytes());
let num_relocs = self.sections[i].relocations.len() as u64;
shdr[32..40].copy_from_slice(&(num_relocs * 24).to_le_bytes());
shdr[40..44].copy_from_slice(&((1 + num_data * 2 + 1) as u32).to_le_bytes()); shdr[44..48].copy_from_slice(&((i + 1) as u32).to_le_bytes()); shdr[48..56].copy_from_slice(&8u64.to_le_bytes());
shdr[56..64].copy_from_slice(&24u64.to_le_bytes());
shdrs.extend_from_slice(&shdr);
}
{
let sym_size = 24 * (self.symbols.len() + 1);
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&2u32.to_le_bytes());
shdr[24..32].copy_from_slice(&symtab_off.to_le_bytes());
shdr[32..40].copy_from_slice(&(sym_size as u64).to_le_bytes());
shdr[40..44].copy_from_slice(&((1 + num_data * 2 + 2) as u32).to_le_bytes());
shdr[44..48].copy_from_slice(&1u32.to_le_bytes());
shdr[48..56].copy_from_slice(&8u64.to_le_bytes());
shdr[56..64].copy_from_slice(&24u64.to_le_bytes());
shdrs.extend_from_slice(&shdr);
}
{
let strtab = self.build_elf_strtab();
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&3u32.to_le_bytes());
shdr[24..32].copy_from_slice(&strtab_off.to_le_bytes());
shdr[32..40].copy_from_slice(&(strtab.len() as u64).to_le_bytes());
shdr[48..56].copy_from_slice(&1u64.to_le_bytes());
shdrs.extend_from_slice(&shdr);
}
{
let shstrtab = self.build_elf_shstrtab();
let mut shdr = [0u8; 64];
shdr[4..8].copy_from_slice(&3u32.to_le_bytes());
shdr[24..32].copy_from_slice(&shstrtab_off.to_le_bytes());
shdr[32..40].copy_from_slice(&(shstrtab.len() as u64).to_le_bytes());
shdr[48..56].copy_from_slice(&1u64.to_le_bytes());
shdrs.extend_from_slice(&shdr);
}
shdrs
}
fn elf_section_flags(&self, flags: &X86SectionFlags) -> u64 {
let mut f: u64 = 0;
if flags.alloc {
f |= 1 << 1; }
if flags.write {
f |= 1 << 0; }
if flags.exec {
f |= 1 << 2; }
if flags.merge {
f |= 1 << 4; }
if flags.strings {
f |= 1 << 5; }
if flags.tls {
f |= 1 << 10; }
f
}
fn elf_section_type(&self, stype: &X86SectionType) -> u32 {
match stype {
X86SectionType::Text
| X86SectionType::Data
| X86SectionType::Rodata
| X86SectionType::InitArray
| X86SectionType::FiniArray
| X86SectionType::DebugInfo
| X86SectionType::DebugAbbrev
| X86SectionType::DebugLine
| X86SectionType::DebugFrame
| X86SectionType::DebugStr
| X86SectionType::DebugLoc
| X86SectionType::DebugRanges
| X86SectionType::DebugMacro
| X86SectionType::DebugAddr
| X86SectionType::DebugStrOffsets
| X86SectionType::EhFrame
| X86SectionType::GccExceptTable
| X86SectionType::TlsData
| X86SectionType::Comment
| X86SectionType::Custom(_) => 1, X86SectionType::Bss | X86SectionType::TlsBss => 8, X86SectionType::Note => 7, }
}
fn build_elf_symtab(&self) -> Vec<u8> {
let mut symtab = Vec::new();
symtab.extend_from_slice(&[0u8; 24]);
for sym in &self.symbols {
let mut entry = [0u8; 24];
let binding = if sym.is_global {
if sym.is_weak {
2u8
} else {
1u8
}
} else {
0u8
};
let st_type = if sym.is_function {
2u8
} else if sym.is_object {
1u8
} else {
0u8
};
entry[4] = (binding << 4) | (st_type & 0x0F);
entry[5] = 0;
entry[6..8].copy_from_slice(&sym.section_index.to_le_bytes());
entry[8..16].copy_from_slice(&sym.value.to_le_bytes());
entry[16..24].copy_from_slice(&sym.size.to_le_bytes());
symtab.extend_from_slice(&entry);
}
let strtab = self.build_elf_strtab();
for (i, sym) in self.symbols.iter().enumerate() {
let name_offset = strtab
.windows(sym.name.len())
.position(|w| w == sym.name.as_bytes())
.unwrap_or(0);
let entry_offset = 24 + i * 24;
symtab[entry_offset..entry_offset + 4]
.copy_from_slice(&(name_offset as u32).to_le_bytes());
}
symtab
}
fn build_elf_strtab(&self) -> Vec<u8> {
let mut strtab = vec![0u8]; for sym in &self.symbols {
let off = strtab.len();
strtab.extend_from_slice(sym.name.as_bytes());
strtab.push(0);
}
strtab
}
fn build_elf_shstrtab(&self) -> Vec<u8> {
let mut shstrtab = vec![0u8]; let mut names: Vec<(&str, u32)> = Vec::new();
for (i, section) in self.sections.iter().enumerate() {
if !shstrtab
.windows(section.name.len())
.any(|w| w == section.name.as_bytes())
{
let off = shstrtab.len() as u32;
shstrtab.extend_from_slice(section.name.as_bytes());
shstrtab.push(0);
names.push((§ion.name, off));
}
}
for std_name in &[".symtab", ".strtab", ".shstrtab"] {
if !shstrtab
.windows(std_name.len())
.any(|w| w == std_name.as_bytes())
{
shstrtab.extend_from_slice(std_name.as_bytes());
shstrtab.push(0);
}
}
shstrtab
}
fn build_elf_rela_section(&self, section: &X86ObjectSection) -> Vec<u8> {
let mut rela = Vec::new();
for reloc in §ion.relocations {
let mut entry = [0u8; 24];
entry[0..8].copy_from_slice(&reloc.offset.to_le_bytes());
let r_info: u64 = ((reloc.symbol_index as u64) << 32) | (reloc.rel_type as u64);
entry[8..16].copy_from_slice(&r_info.to_le_bytes());
entry[16..24].copy_from_slice(&reloc.addend.to_le_bytes());
rela.extend_from_slice(&entry);
}
rela
}
fn write_coff64(&self) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&0x8664u16.to_le_bytes());
buf.extend_from_slice(&(self.sections.len() as u16).to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&(self.symbols.len() as u32).to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
let section_header_size = 40u64;
let mut section_offsets: Vec<u64> = Vec::new();
let header_end = 20 + self.sections.len() as u64 * section_header_size;
let mut current_offset = header_end;
for section in &self.sections {
section_offsets.push(current_offset);
let mut name = [0u8; 8];
let name_bytes = section.name.as_bytes();
let len = name_bytes.len().min(8);
name[..len].copy_from_slice(&name_bytes[..len]);
buf.extend_from_slice(&name);
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
let raw_size = if section.section_type == X86SectionType::Bss {
0u32
} else {
section.data.len() as u32
};
buf.extend_from_slice(&raw_size.to_le_bytes());
if raw_size > 0 {
buf.extend_from_slice(&(current_offset as u32).to_le_bytes());
} else {
buf.extend_from_slice(&0u32.to_le_bytes());
}
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&(section.relocations.len() as u16).to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
let chars = self.coff_section_characteristics(§ion.flags);
buf.extend_from_slice(&chars.to_le_bytes());
current_offset += raw_size as u64;
}
for section in &self.sections {
if section.section_type != X86SectionType::Bss {
buf.extend_from_slice(§ion.data);
}
}
for section in &self.sections {
for reloc in §ion.relocations {
buf.extend_from_slice(&reloc.offset.to_le_bytes()); buf.extend_from_slice(&reloc.symbol_index.to_le_bytes()); let rel_type = match reloc.size {
4 => {
if reloc.is_pcrel {
4u16 } else {
3u16 }
}
8 => 1u16, _ => 0u16,
};
buf.extend_from_slice(&rel_type.to_le_bytes());
}
}
for sym in &self.symbols {
let mut name = [0u8; 8];
let name_bytes = sym.name.as_bytes();
let len = name_bytes.len().min(8);
name[..len].copy_from_slice(&name_bytes[..len]);
buf.extend_from_slice(&name);
buf.extend_from_slice(&(sym.value as u32).to_le_bytes());
buf.extend_from_slice(&(sym.section_index as i16).to_le_bytes());
let sym_type: u16 = if sym.is_function { 0x20 } else { 0 };
buf.extend_from_slice(&sym_type.to_le_bytes());
let storage_class: u8 = if sym.is_global {
2 } else {
3 };
buf.push(storage_class);
buf.push(0);
}
buf.extend_from_slice(&4u32.to_le_bytes());
buf
}
fn coff_section_characteristics(&self, flags: &X86SectionFlags) -> u32 {
let mut chars: u32 = 0;
if flags.exec {
chars |= 0x20000000; chars |= 0x00000020; } else if flags.write {
chars |= 0x80000000; if flags.alloc {
chars |= 0x00000040; } else {
chars |= 0x00000080; }
} else {
chars |= 0x00000040; }
if flags.alloc && !flags.exec {
chars |= 0x40000000; }
if flags.debug {
chars |= 0x02000000; }
if flags.exclude {
chars |= 0x00000800; }
chars
}
fn write_macho64(&self) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&0xFEEDFACFu32.to_le_bytes());
buf.extend_from_slice(&0x01000007u32.to_le_bytes());
buf.extend_from_slice(&3u32.to_le_bytes());
buf.extend_from_slice(&1u32.to_le_bytes());
let ncmds_pos = buf.len();
buf.extend_from_slice(&0u32.to_le_bytes());
let sizeofcmds_pos = buf.len();
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
let mut cmds = Vec::new();
let mut sections_data: Vec<Vec<u8>> = Vec::new();
for section in &self.sections {
let mut seg_cmd = Vec::new();
seg_cmd.extend_from_slice(&0x19u32.to_le_bytes());
seg_cmd.extend_from_slice(&152u32.to_le_bytes());
let mut segname = [0u8; 16];
let seg = if section.name.starts_with("__TEXT")
|| section.section_type == X86SectionType::Text
{
b"__TEXT"
} else if section.name.starts_with("__DATA")
|| section.section_type == X86SectionType::Data
{
b"__DATA"
} else {
b"__LLVM"
};
let len = seg.len().min(16);
segname[..len].copy_from_slice(&seg[..len]);
seg_cmd.extend_from_slice(&segname);
seg_cmd.extend_from_slice(&0u64.to_le_bytes());
seg_cmd.extend_from_slice(&0u64.to_le_bytes());
seg_cmd.extend_from_slice(&0u64.to_le_bytes());
seg_cmd.extend_from_slice(&0u64.to_le_bytes());
seg_cmd.extend_from_slice(&7u64.to_le_bytes());
seg_cmd.extend_from_slice(&7u64.to_le_bytes());
seg_cmd.extend_from_slice(&1u32.to_le_bytes());
seg_cmd.extend_from_slice(&0u32.to_le_bytes());
let mut sectname = [0u8; 16];
let sname = section.name.as_bytes();
let len = sname.len().min(16);
sectname[..len].copy_from_slice(&sname[..len]);
seg_cmd.extend_from_slice(§name);
seg_cmd.extend_from_slice(&segname);
seg_cmd.extend_from_slice(&0u64.to_le_bytes());
seg_cmd.extend_from_slice(&(section.data.len() as u64).to_le_bytes());
seg_cmd.extend_from_slice(&0u32.to_le_bytes());
let align = section.alignment.trailing_zeros();
seg_cmd.extend_from_slice(&align.to_le_bytes());
seg_cmd.extend_from_slice(&0u32.to_le_bytes());
seg_cmd.extend_from_slice(&(section.relocations.len() as u32).to_le_bytes());
let sflags: u32 = if section.flags.exec {
0x80000000u32
} else {
0u32
};
seg_cmd.extend_from_slice(&sflags.to_le_bytes());
seg_cmd.extend_from_slice(&0u32.to_le_bytes());
seg_cmd.extend_from_slice(&0u32.to_le_bytes());
seg_cmd.extend_from_slice(&0u32.to_le_bytes());
cmds.push(seg_cmd);
sections_data.push(section.data.clone());
}
let uuid_cmd = self.build_macho_uuid_cmd();
if !uuid_cmd.is_empty() {
cmds.push(uuid_cmd);
}
let total_cmds_size: u32 = cmds.iter().map(|c| c.len() as u32).sum();
let ncmds = cmds.len() as u32;
for cmd in &cmds {
buf.extend_from_slice(cmd);
}
let mut sec_data_offsets: Vec<u64> = Vec::new();
for data in §ions_data {
let off = buf.len() as u64;
sec_data_offsets.push(off);
buf.extend_from_slice(data);
}
for section in &self.sections {
for reloc in §ion.relocations {
buf.extend_from_slice(&(reloc.offset as u32).to_le_bytes()); let r_pcrel: u32 = if reloc.is_pcrel { 1 } else { 0 };
let r_length: u32 = match reloc.size {
8 => 3,
4 => 2,
2 => 1,
_ => 0,
};
let r_extern: u32 = 1;
let r_type: u32 = 0; let r_info = (reloc.symbol_index & 0xFFFFFF)
| (r_pcrel << 24)
| (r_length << 25)
| (r_extern << 27)
| (r_type << 28);
buf.extend_from_slice(&r_info.to_le_bytes());
}
}
let header_end = 32; let cmd_start = header_end;
let mut cmd_offset = cmd_start;
for (i, cmd) in cmds.iter().enumerate() {
if i < sections_data.len() {
let fileoff = sec_data_offsets[i];
let fileoff_pos = cmd_offset + 48;
if fileoff_pos + 8 <= buf.len() {
buf[fileoff_pos..fileoff_pos + 8].copy_from_slice(&fileoff.to_le_bytes());
}
let sec_offset_pos = cmd_offset + 72 + 48;
if sec_offset_pos + 4 <= buf.len() {
buf[sec_offset_pos..sec_offset_pos + 4]
.copy_from_slice(&(fileoff as u32).to_le_bytes());
}
}
cmd_offset += cmd.len();
}
buf[ncmds_pos..ncmds_pos + 4].copy_from_slice(&ncmds.to_le_bytes());
buf[sizeofcmds_pos..sizeofcmds_pos + 4].copy_from_slice(&total_cmds_size.to_le_bytes());
buf
}
fn build_macho_uuid_cmd(&self) -> Vec<u8> {
if let Some(uuid) = &self.uuid {
let mut cmd = Vec::new();
cmd.extend_from_slice(&0x1Bu32.to_le_bytes());
cmd.extend_from_slice(&24u32.to_le_bytes());
cmd.extend_from_slice(uuid);
cmd
} else {
Vec::new()
}
}
pub fn generate_uuid(&mut self) {
let mut uuid = [0u8; 16];
for (i, b) in self.target_triple.bytes().enumerate() {
uuid[i % 16] ^= b;
uuid[(i + 7) % 16] = uuid[(i + 7) % 16].wrapping_add(b);
}
for i in 0..16 {
uuid[i] = uuid[i].wrapping_mul(37).wrapping_add(17);
}
self.uuid = Some(uuid);
}
}
impl Default for X86MCObjectWriter {
fn default() -> Self {
Self::new_elf("x86_64-unknown-linux-gnu")
}
}
pub struct X86MCAsmBackend {
pub code_emitter: X86MCCodeEmitter,
pub object_writer: X86MCObjectWriter,
pub ctx: X86MCContext,
pub reg_info: X86MCRegisterInfo,
pub instr_info: X86MCInstrInfo,
pub sti: X86MCSubtargetInfo,
pub section_data: Vec<u8>,
pub section_stack: Vec<String>,
pub allow_relaxation: bool,
}
impl X86MCAsmBackend {
pub fn new(
format: ObjectFormat,
target_triple: impl Into<String>,
sti: X86MCSubtargetInfo,
) -> Self {
let reg_info = X86MCRegisterInfo::default();
let instr_info = X86MCInstrInfo::default();
let ctx = X86MCContext::new(target_triple.into(), sti.cpu.clone());
let code_emitter = X86MCCodeEmitter::new(X86Mode::Mode64, sti.clone());
let object_writer = X86MCObjectWriter::new(format, ctx.target_triple.clone());
Self {
code_emitter,
object_writer,
ctx,
reg_info,
instr_info,
sti,
section_data: Vec::new(),
section_stack: Vec::new(),
allow_relaxation: false,
}
}
pub fn target_name(&self) -> &str {
"x86_64"
}
pub fn create_code_emitter(&self) -> X86MCCodeEmitter {
X86MCCodeEmitter::default()
}
pub fn create_object_writer(&self) -> X86MCObjectWriter {
X86MCObjectWriter::default()
}
pub fn get_relaxed_instruction_size(&self) -> u32 {
17 }
pub fn start_section(
&mut self,
name: &str,
section_type: X86SectionType,
flags: X86SectionFlags,
) {
let section = X86ObjectSection {
name: name.to_string(),
data: Vec::new(),
flags,
section_type,
alignment: 16,
relocations: Vec::new(),
};
self.object_writer.add_section(section);
self.section_data.clear();
}
pub fn emit_instruction(&mut self, inst: &MCInst) {
let mut encoded = self.code_emitter.encode_instruction(inst);
self.section_data.append(&mut encoded);
}
pub fn emit_data(&mut self, data: &[u8]) {
self.section_data.extend_from_slice(data);
}
pub fn emit_alignment(&mut self, alignment: u32) {
let pad_bytes = self.code_emitter.emit_alignment(alignment);
self.section_data.extend_from_slice(&pad_bytes);
}
pub fn emit_label(&mut self, name: &str) {
self.code_emitter.record_label(name);
}
pub fn emit_fixup(&mut self, fixup: X86MCFixup) {
self.code_emitter.record_fixup(fixup);
}
pub fn end_section(&mut self) {
if let Some(section) = self.object_writer.sections.last_mut() {
section.data = std::mem::take(&mut self.section_data);
}
}
pub fn switch_section(&mut self, name: &str) {
self.end_section();
if let Some(idx) = self
.object_writer
.sections
.iter()
.position(|s| s.name == name)
{
} else {
let (stype, flags) = match name {
".text" => (
X86SectionType::Text,
X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
),
".data" => (
X86SectionType::Data,
X86SectionFlags {
alloc: true,
write: true,
..Default::default()
},
),
".rodata" | ".rodata.str1.1" | ".rodata.cst8" | ".rodata.cst16" => (
X86SectionType::Rodata,
X86SectionFlags {
alloc: true,
..Default::default()
},
),
".bss" => (
X86SectionType::Bss,
X86SectionFlags {
alloc: true,
write: true,
..Default::default()
},
),
".debug_info" => (
X86SectionType::DebugInfo,
X86SectionFlags {
debug: true,
..Default::default()
},
),
".debug_abbrev" => (
X86SectionType::DebugAbbrev,
X86SectionFlags {
debug: true,
..Default::default()
},
),
".debug_line" => (
X86SectionType::DebugLine,
X86SectionFlags {
debug: true,
..Default::default()
},
),
".debug_frame" | ".eh_frame" => (
X86SectionType::DebugFrame,
X86SectionFlags {
alloc: true,
..Default::default()
},
),
".debug_str" => (
X86SectionType::DebugStr,
X86SectionFlags {
debug: true,
merge: true,
strings: true,
..Default::default()
},
),
".debug_loc" => (
X86SectionType::DebugLoc,
X86SectionFlags {
debug: true,
..Default::default()
},
),
".debug_ranges" => (
X86SectionType::DebugRanges,
X86SectionFlags {
debug: true,
..Default::default()
},
),
".init_array" => (
X86SectionType::InitArray,
X86SectionFlags {
alloc: true,
write: true,
..Default::default()
},
),
".fini_array" => (
X86SectionType::FiniArray,
X86SectionFlags {
alloc: true,
write: true,
..Default::default()
},
),
".comment" => (
X86SectionType::Comment,
X86SectionFlags {
merge: true,
strings: true,
..Default::default()
},
),
".note.GNU-stack" => (X86SectionType::Note, X86SectionFlags::default()),
".tbss" => (
X86SectionType::TlsBss,
X86SectionFlags {
alloc: true,
write: true,
tls: true,
..Default::default()
},
),
".tdata" => (
X86SectionType::TlsData,
X86SectionFlags {
alloc: true,
write: true,
tls: true,
..Default::default()
},
),
_ => (
X86SectionType::Custom(name.to_string()),
X86SectionFlags::default(),
),
};
self.start_section(name, stype, flags);
}
}
pub fn push_section(&mut self) {
if let Some(section) = self.object_writer.sections.last() {
self.section_stack.push(section.name.clone());
}
}
pub fn pop_section(&mut self) {
if let Some(name) = self.section_stack.pop() {
self.switch_section(&name);
}
}
pub fn resolve_fixups(&mut self) {
self.code_emitter.resolve_fixups();
}
pub fn finish(&mut self) -> Vec<u8> {
self.end_section();
self.object_writer.import_fixups(&self.code_emitter.fixups);
for (name, sym) in &self.ctx.symbols {
let obj_sym = X86ObjectSymbol {
name: name.clone(),
value: sym.value,
size: sym.size,
section_index: sym.section_index.map(|i| i as u32).unwrap_or(0),
is_global: sym.is_global,
is_weak: sym.is_weak,
is_undefined: sym.is_undefined,
is_function: sym.is_function,
is_object: sym.is_object,
is_tls: false,
is_absolute: false,
};
self.object_writer.add_symbol(obj_sym);
}
self.object_writer.write()
}
}
impl Default for X86MCAsmBackend {
fn default() -> Self {
Self::new(
ObjectFormat::ELF,
"x86_64-unknown-linux-gnu",
X86MCSubtargetInfo::default(),
)
}
}
pub struct X86MCTargetStreamer {
pub streamer: MCObjectStreamer,
pub asm_printer: X86MCAsmPrinter,
pub backend: X86MCAsmBackend,
pub emit_asm: bool,
pub emit_obj: bool,
pub mode: X86Mode,
pub emit_cfi: bool,
pub current_function: Option<String>,
pub verbose: bool,
}
impl X86MCTargetStreamer {
pub fn new(format: ObjectFormat, target_triple: impl Into<String>) -> Self {
let triple: String = target_triple.into();
let sti = X86MCSubtargetInfo::default();
let backend = X86MCAsmBackend::new(format, triple.clone(), sti.clone());
let asm_printer = X86MCAsmPrinter::new(AsmSyntax::ATT, sti);
Self {
streamer: MCObjectStreamer::default(),
asm_printer,
backend,
emit_asm: true,
emit_obj: true,
mode: X86Mode::Mode64,
emit_cfi: true,
current_function: None,
verbose: false,
}
}
pub fn emit_header(&mut self) {
self.asm_printer.emit_module_header(&self.backend.ctx);
}
pub fn emit_instruction(&mut self, inst: &MCInst) {
if self.emit_asm {
self.asm_printer.emit_instruction(inst);
}
if self.emit_obj {
self.backend.emit_instruction(inst);
}
}
pub fn emit_label(&mut self, name: &str) {
if self.emit_asm {
self.asm_printer.emit_label(name);
}
if self.emit_obj {
self.backend.emit_label(name);
}
}
pub fn emit_comment(&mut self, text: &str) {
if self.emit_asm && self.verbose {
self.asm_printer.emit_comment(text);
}
}
pub fn emit_section(&mut self, name: &str) {
if self.emit_asm {
match name {
".text" => self.asm_printer.writeln("\t.text"),
".data" => self.asm_printer.writeln("\t.data"),
".bss" => self.asm_printer.writeln("\t.bss"),
".rodata" => self.asm_printer.writeln("\t.section\t.rodata"),
_ => self.asm_printer.writeln(format!("\t.section\t{}", name)),
}
}
if self.emit_obj {
self.backend.switch_section(name);
}
}
pub fn emit_global(&mut self, name: &str) {
if self.emit_asm {
self.asm_printer.emit_global(name);
}
if self.emit_obj {
let sym = self.backend.ctx.get_or_create_symbol(name).clone();
let mut sym_mut = sym.clone();
sym_mut.is_global = true;
self.backend.ctx.symbols.insert(name.to_string(), sym_mut);
}
}
pub fn emit_weak(&mut self, name: &str) {
if self.emit_asm {
self.asm_printer.emit_weak(name);
}
if self.emit_obj {
let sym = self.backend.ctx.get_or_create_symbol(name).clone();
let mut sym_mut = sym.clone();
sym_mut.is_weak = true;
self.backend.ctx.symbols.insert(name.to_string(), sym_mut);
}
}
pub fn emit_local(&mut self, name: &str) {
if self.emit_asm {
self.asm_printer.emit_local(name);
}
}
pub fn emit_type(&mut self, name: &str, sym_type: &str) {
if self.emit_asm {
self.asm_printer.emit_type(name, sym_type);
}
}
pub fn emit_size(&mut self, name: &str, size: u64) {
if self.emit_asm {
self.asm_printer.emit_size(name, size);
}
}
pub fn emit_alignment(&mut self, alignment: u32) {
if self.emit_asm {
self.asm_printer.emit_alignment(alignment);
}
if self.emit_obj {
self.backend.emit_alignment(alignment);
}
}
pub fn emit_p2align(&mut self, pow2: u32) {
if self.emit_asm {
self.asm_printer.emit_p2align(pow2);
}
if self.emit_obj {
self.backend.emit_alignment(1u32 << pow2);
}
}
pub fn emit_zero_fill(&mut self, count: u64) {
if self.emit_asm {
self.asm_printer.emit_zero_fill(count);
}
if self.emit_obj {
let zeros = vec![0u8; count as usize];
self.backend.emit_data(&zeros);
}
}
pub fn emit_byte(&mut self, value: u8) {
if self.emit_asm {
self.asm_printer.emit_byte(value);
}
if self.emit_obj {
self.backend.emit_data(&[value]);
}
}
pub fn emit_short(&mut self, value: u16) {
if self.emit_asm {
self.asm_printer.emit_short(value);
}
if self.emit_obj {
self.backend.emit_data(&value.to_le_bytes());
}
}
pub fn emit_long(&mut self, value: u32) {
if self.emit_asm {
self.asm_printer.emit_long(value);
}
if self.emit_obj {
self.backend.emit_data(&value.to_le_bytes());
}
}
pub fn emit_quad(&mut self, value: u64) {
if self.emit_asm {
self.asm_printer.emit_quad(value);
}
if self.emit_obj {
self.backend.emit_data(&value.to_le_bytes());
}
}
pub fn emit_ascii(&mut self, text: &str) {
if self.emit_asm {
self.asm_printer.emit_ascii(text);
}
if self.emit_obj {
self.backend.emit_data(text.as_bytes());
}
}
pub fn emit_asciz(&mut self, text: &str) {
if self.emit_asm {
self.asm_printer.emit_asciz(text);
}
if self.emit_obj {
let mut data = text.as_bytes().to_vec();
data.push(0);
self.backend.emit_data(&data);
}
}
pub fn emit_fill(&mut self, count: u64, size: u64, value: u64) {
if self.emit_asm {
self.asm_printer.emit_fill(count, size, value);
}
if self.emit_obj {
let total = count * size;
for _ in 0..count {
let val_bytes = match size {
1 => vec![value as u8],
2 => (value as u16).to_le_bytes().to_vec(),
4 => (value as u32).to_le_bytes().to_vec(),
8 => value.to_le_bytes().to_vec(),
_ => vec![value as u8],
};
self.backend.emit_data(&val_bytes);
}
}
}
pub fn emit_org(&mut self, location: u64) {
if self.emit_asm {
self.asm_printer.emit_org(location);
}
if self.emit_obj {
let current = self.backend.section_data.len() as u64;
if location > current {
let pad = vec![0u8; (location - current) as usize];
self.backend.emit_data(&pad);
}
}
}
pub fn emit_cfi(&mut self, cfi: &X86CFIInstruction) {
if self.emit_asm && self.emit_cfi {
self.asm_printer.emit_cfi(cfi);
}
if self.emit_obj {
self.backend.ctx.record_cfi(cfi.clone());
}
}
pub fn emit_file(&mut self, file_id: u32, directory: &str, filename: &str) {
if self.emit_asm {
self.asm_printer
.emit_file_directive(file_id, directory, filename);
}
if self.emit_obj {
self.backend.ctx.record_debug_file(X86DebugFileEntry {
file_id,
directory_id: 0,
name: filename.to_string(),
directory: directory.to_string(),
md5_checksum: None,
});
}
}
pub fn emit_loc(&mut self, file_id: u32, line: u32, column: u32) {
if self.emit_asm {
self.asm_printer.emit_loc_directive(file_id, line, column);
}
if self.emit_obj {
self.backend.ctx.record_debug_line(X86DebugLineEntry {
file_id,
line,
column,
..Default::default()
});
}
}
pub fn begin_function(&mut self, name: &str) {
self.current_function = Some(name.to_string());
if self.emit_asm {
self.asm_printer.emit_function_prologue(name);
}
if self.emit_obj && self.emit_cfi {
self.backend.ctx.record_cfi(X86CFIInstruction::StartProc);
self.backend.ctx.record_cfi(X86CFIInstruction::DefCfa {
register: RSP,
offset: 8,
});
self.backend.ctx.record_cfi(X86CFIInstruction::Offset {
register: RIP,
offset: -8,
});
}
}
pub fn end_function(&mut self) {
let name = self.current_function.take();
if self.emit_asm {
if let Some(n) = &name {
self.asm_printer.emit_function_epilogue(n);
}
}
if self.emit_obj && self.emit_cfi {
self.backend.ctx.record_cfi(X86CFIInstruction::EndProc);
}
}
pub fn finish(&mut self) -> (String, Vec<u8>) {
let asm = if self.emit_asm {
self.asm_printer.finish().to_string()
} else {
String::new()
};
let obj = if self.emit_obj {
self.backend.finish()
} else {
Vec::new()
};
(asm, obj)
}
pub fn add_relocation(&mut self, section: &str, relocation: X86ObjectRelocation) {
if let Some(idx) = self
.backend
.object_writer
.sections
.iter()
.position(|s| s.name == section)
{
self.backend.object_writer.add_relocation(idx, relocation);
}
}
pub fn set_no_exec_stack(&mut self, noexec: bool) {
if self.emit_asm {
self.asm_printer.emit_note_gnu_stack(!noexec);
}
self.backend.ctx.no_exec_stack = noexec;
}
}
impl Default for X86MCTargetStreamer {
fn default() -> Self {
Self::new(ObjectFormat::ELF, "x86_64-unknown-linux-gnu")
}
}
pub fn create_x86_mc_pipeline(
target_triple: &str,
cpu: &str,
features: &[&str],
format: ObjectFormat,
emit_asm: bool,
emit_obj: bool,
) -> (
X86MCContext,
X86MCAsmBackend,
X86MCCodeEmitter,
X86MCObjectWriter,
X86MCAsmPrinter,
X86MCTargetStreamer,
) {
let sti = X86MCSubtargetInfo::new(cpu, features.iter().map(|s| s.to_string()).collect());
let ctx = X86MCContext::new(target_triple, cpu);
let reg_info = X86MCRegisterInfo::default();
let instr_info = X86MCInstrInfo::default();
let mut backend = X86MCAsmBackend::new(format, target_triple, sti.clone());
let code_emitter = X86MCCodeEmitter::new(X86Mode::Mode64, sti.clone());
let object_writer = X86MCObjectWriter::new(format, target_triple);
let asm_printer = X86MCAsmPrinter::new(AsmSyntax::ATT, sti);
let target_streamer = X86MCTargetStreamer::new(format, target_triple);
(
ctx,
backend,
code_emitter,
object_writer,
asm_printer,
target_streamer,
)
}
pub fn encode_to_elf(inst: &MCInst, target_triple: &str) -> Vec<u8> {
let mut emitter = X86MCCodeEmitter::default();
let encoded = emitter.encode_instruction(inst);
encoded
}
pub fn create_simple_elf_object(instructions: &[MCInst], symbols: &[(&str, u64)]) -> Vec<u8> {
let mut writer = X86MCObjectWriter::new_elf("x86_64-unknown-linux-gnu");
let flags = X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
};
let section = X86ObjectSection {
name: ".text".into(),
data: {
let mut encoder = X86MCCodeEmitter::default();
let mut data = Vec::new();
for inst in instructions {
let encoded = encoder.encode_instruction(inst);
data.extend_from_slice(&encoded);
}
data
},
flags,
section_type: X86SectionType::Text,
alignment: 16,
relocations: Vec::new(),
};
writer.add_section(section);
for (name, value) in symbols {
writer.add_symbol(X86ObjectSymbol {
name: name.to_string(),
value: *value,
size: 0,
section_index: 1,
is_global: true,
is_weak: false,
is_undefined: false,
is_function: true,
is_object: false,
is_tls: false,
is_absolute: false,
});
}
writer.write()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_context_new() {
let ctx = X86MCContext::new("x86_64-unknown-linux-gnu", "skylake");
assert_eq!(ctx.target_triple, "x86_64-unknown-linux-gnu");
assert_eq!(ctx.cpu, "skylake");
assert!(ctx.symbols.is_empty());
assert!(ctx.sections.is_empty());
assert_eq!(ctx.fragment_count(), 0);
}
#[test]
fn test_context_default() {
let ctx = X86MCContext::default();
assert_eq!(ctx.target_triple, "x86_64-unknown-linux-gnu");
}
#[test]
fn test_context_symbol_creation() {
let mut ctx = X86MCContext::default();
let sym = ctx.get_or_create_symbol("main").clone();
assert_eq!(sym.name, "main");
assert_eq!(ctx.symbols.len(), 1);
let sym2 = ctx.get_or_create_symbol("main").clone();
assert_eq!(ctx.symbols.len(), 1);
}
#[test]
fn test_context_temp_symbol() {
let mut ctx = X86MCContext::default();
let sym1 = ctx.create_temp_symbol();
let sym2 = ctx.create_temp_symbol();
assert_ne!(sym1.name, sym2.name);
assert!(sym1.name.starts_with(".Ltmp"));
}
#[test]
fn test_context_emit_instruction() {
let mut ctx = X86MCContext::default();
let inst = make_mc_inst(24, vec![]); ctx.emit_instruction(inst);
assert_eq!(ctx.fragment_count(), 1);
assert_eq!(ctx.instruction_count(), 1);
}
#[test]
fn test_context_emit_data() {
let mut ctx = X86MCContext::default();
ctx.emit_data(vec![0x90, 0x90, 0x90]);
assert_eq!(ctx.fragment_count(), 1);
}
#[test]
fn test_context_emit_alignment() {
let mut ctx = X86MCContext::default();
ctx.emit_alignment(16, 0x90, 16);
let flat = ctx.flatten();
assert_eq!(flat.len() % 16, 0);
}
#[test]
fn test_context_emit_fill() {
let mut ctx = X86MCContext::default();
ctx.emit_fill(10, 0xCC);
let flat = ctx.flatten();
assert_eq!(flat.len(), 10);
assert!(flat.iter().all(|&b| b == 0xCC));
}
#[test]
fn test_context_flatten_empty() {
let ctx = X86MCContext::default();
assert!(ctx.flatten().is_empty());
}
#[test]
fn test_context_clear() {
let mut ctx = X86MCContext::default();
ctx.emit_data(vec![1, 2, 3]);
ctx.get_or_create_symbol("test");
ctx.record_cfi(X86CFIInstruction::StartProc);
assert_eq!(ctx.fragment_count(), 1);
assert_eq!(ctx.symbols.len(), 1);
ctx.clear();
assert_eq!(ctx.fragment_count(), 0);
assert_eq!(ctx.symbols.len(), 0);
assert!(ctx.cfi_instructions.is_empty());
}
#[test]
fn test_context_code_model() {
let mut ctx = X86MCContext::default();
assert_eq!(ctx.code_model, X86CodeModel::Small);
ctx.set_code_model(X86CodeModel::Large);
assert_eq!(ctx.code_model, X86CodeModel::Large);
}
#[test]
fn test_context_pic() {
let mut ctx = X86MCContext::default();
assert!(!ctx.is_pic);
ctx.set_pic(true);
assert!(ctx.is_pic);
}
#[test]
fn test_context_display() {
let ctx = X86MCContext::default();
let s = format!("{}", ctx);
assert!(s.contains("x86_64-unknown-linux-gnu"));
assert!(s.contains("fragments: 0"));
}
#[test]
fn test_context_section_management() {
let mut ctx = X86MCContext::default();
let sec1 = MCSection::new("section1", MCSectionType::Text);
let sec2 = MCSection::new("section2", MCSectionType::Data);
let idx1 = ctx.get_or_create_section(sec1.clone());
let idx2 = ctx.get_or_create_section(sec2.clone());
assert_ne!(idx1, idx2);
let idx1_again = ctx.get_or_create_section(sec1);
assert_eq!(idx1, idx1_again);
}
#[test]
fn test_reg_info_new() {
let info = X86MCRegisterInfo::new_x86_64();
assert!(!info.reg_classes.is_empty());
}
#[test]
fn test_reg_info_dwarf_mapping() {
let info = X86MCRegisterInfo::new_x86_64();
assert_eq!(info.get_dwarf_reg_num(X86Reg(RAX)), Some(0));
assert_eq!(info.get_dwarf_reg_num(X86Reg(RBP)), Some(6));
assert_eq!(info.get_dwarf_reg_num(X86Reg(RSP)), Some(7));
assert_eq!(info.get_dwarf_reg_num(X86Reg(R15)), Some(15));
}
#[test]
fn test_reg_info_allocatable() {
let info = X86MCRegisterInfo::new_x86_64();
assert!(info.is_allocatable(X86Reg(RAX)));
assert!(info.is_allocatable(X86Reg(RBX)));
assert!(!info.is_allocatable(X86Reg(CS))); }
#[test]
fn test_reg_info_callee_saved() {
let info = X86MCRegisterInfo::new_x86_64();
assert!(info.is_callee_saved(X86Reg(RBX)));
assert!(info.is_callee_saved(X86Reg(RBP)));
assert!(info.is_callee_saved(X86Reg(R12)));
assert!(!info.is_callee_saved(X86Reg(RAX)));
assert!(!info.is_callee_saved(X86Reg(RCX)));
}
#[test]
fn test_reg_info_get_class() {
let info = X86MCRegisterInfo::new_x86_64();
let class = info.get_reg_class(X86Reg(RAX));
assert!(class.is_some());
assert_eq!(class.unwrap().name, "GR64");
}
#[test]
fn test_reg_info_get_super_reg() {
let info = X86MCRegisterInfo::new_x86_64();
let super_reg = info.get_super_reg(X86Reg(EAX));
assert!(super_reg.is_some());
assert_eq!(super_reg.unwrap().0, RAX);
}
#[test]
fn test_reg_info_get_sub_regs() {
let info = X86MCRegisterInfo::new_x86_64();
let sub = info.get_sub_regs(X86Reg(RAX));
assert!(!sub.is_empty());
let has_eax = sub.iter().any(|(r, _)| r.0 == EAX);
assert!(has_eax);
}
#[test]
fn test_instr_info_lookup() {
let info = X86MCInstrInfo::new();
assert_eq!(info.lookup_opcode("MOV"), Some(1));
assert_eq!(info.lookup_opcode("ADD"), Some(2));
assert_eq!(info.lookup_opcode("RET"), Some(14));
assert_eq!(info.lookup_opcode("NOP"), Some(24));
}
#[test]
fn test_instr_info_get_mnemonic() {
let info = X86MCInstrInfo::new();
assert_eq!(info.get_mnemonic(1), Some("MOV"));
assert_eq!(info.get_mnemonic(14), Some("RET"));
assert_eq!(info.get_mnemonic(999), None);
}
#[test]
fn test_instr_info_properties() {
let info = X86MCInstrInfo::new();
assert!(info.is_branch_opcode(15)); assert!(info.is_call_opcode(13)); assert!(info.is_return_opcode(14)); assert!(info.is_barrier_opcode(14)); assert!(!info.is_call_opcode(1)); assert!(info.is_terminator_opcode(14)); assert!(info.is_terminator_opcode(15)); }
#[test]
fn test_instr_info_cond_vs_uncond() {
let info = X86MCInstrInfo::new();
assert!(info.is_uncond_branch_opcode(15)); assert!(info.is_cond_branch_opcode(16)); assert!(!info.is_cond_branch_opcode(15)); }
#[test]
fn test_instr_info_num_instructions() {
let info = X86MCInstrInfo::new();
assert!(info.num_instructions() > 0);
}
#[test]
fn test_subtarget_default() {
let sti = X86MCSubtargetInfo::default();
assert!(sti.has_sse2);
assert!(sti.is_64bit);
}
#[test]
fn test_subtarget_cpu_skylake() {
let sti = X86MCSubtargetInfo::new("skylake", vec![]);
assert!(sti.has_sse2);
assert!(sti.has_avx);
assert!(sti.has_avx2);
assert!(sti.has_fma);
assert!(sti.has_bmi);
assert!(sti.has_bmi2);
}
#[test]
fn test_subtarget_cpu_haswell() {
let sti = X86MCSubtargetInfo::new("haswell", vec![]);
assert!(sti.has_sse2);
assert!(sti.has_avx);
assert!(sti.has_avx2);
assert!(sti.has_fma);
}
#[test]
fn test_subtarget_feature_flag_enable() {
let mut sti = X86MCSubtargetInfo::new("generic", vec![]);
assert!(!sti.has_avx);
sti.apply_feature_flag("+avx");
assert!(sti.has_avx);
}
#[test]
fn test_subtarget_feature_flag_disable() {
let mut sti = X86MCSubtargetInfo::new("generic", vec!["+sse2".into()]);
assert!(sti.has_sse2);
sti.apply_feature_flag("-sse2");
assert!(!sti.has_sse2);
}
#[test]
fn test_subtarget_has_feature() {
let sti = X86MCSubtargetInfo::new("skylake", vec![]);
assert!(sti.has_feature("sse2"));
assert!(sti.has_feature("avx"));
assert!(sti.has_feature("avx2"));
assert!(!sti.has_feature("avx512f")); }
#[test]
fn test_subtarget_avx512_cpu() {
let sti = X86MCSubtargetInfo::new("skylake-avx512", vec![]);
assert!(sti.has_feature("avx512f"));
assert!(sti.has_feature("avx512bw"));
assert!(sti.has_feature("avx512dq"));
}
#[test]
fn test_subtarget_znver4() {
let sti = X86MCSubtargetInfo::new("znver4", vec![]);
assert!(sti.has_avx);
assert!(sti.has_avx2);
assert!(sti.has_avx512f);
}
#[test]
fn test_analysis_is_branch() {
let analysis = X86MCInstrAnalysis::default();
let jmp_inst = make_mc_inst(15, vec![MCOperand::Imm(42)]);
let mov_inst = make_mc_inst(1, vec![]);
assert!(analysis.is_branch(&jmp_inst));
assert!(!analysis.is_branch(&mov_inst));
}
#[test]
fn test_analysis_is_call() {
let analysis = X86MCInstrAnalysis::default();
let call_inst = make_mc_inst(13, vec![]);
let ret_inst = make_mc_inst(14, vec![]);
assert!(analysis.is_call(&call_inst));
assert!(!analysis.is_call(&ret_inst));
}
#[test]
fn test_analysis_is_return() {
let analysis = X86MCInstrAnalysis::default();
let ret_inst = make_mc_inst(14, vec![]);
let nop_inst = make_mc_inst(24, vec![]);
assert!(analysis.is_return(&ret_inst));
assert!(!analysis.is_return(&nop_inst));
}
#[test]
fn test_analysis_is_barrier() {
let analysis = X86MCInstrAnalysis::default();
let ret_inst = make_mc_inst(14, vec![]);
let syscall_inst = make_mc_inst(35, vec![]);
assert!(analysis.is_barrier(&ret_inst));
assert!(analysis.is_barrier(&syscall_inst));
}
#[test]
fn test_analysis_evaluate_branch() {
let analysis = X86MCInstrAnalysis::default();
let jmp_inst = make_mc_inst(15, vec![MCOperand::Imm(100)]);
let target = analysis.evaluate_branch(&jmp_inst, 0x1000, 5);
assert_eq!(target, Some(0x1169));
}
#[test]
fn test_analysis_get_latency() {
let analysis = X86MCInstrAnalysis::default();
assert_eq!(analysis.get_latency(4), 3); assert_eq!(analysis.get_latency(5), 20); assert_eq!(analysis.get_latency(24), 0); assert_eq!(analysis.get_latency(35), 100); }
#[test]
fn test_analysis_get_instruction_size() {
let analysis = X86MCInstrAnalysis::default();
let inst = make_mc_inst(
1,
vec![MCOperand::Reg(RAX as u32), MCOperand::Reg(RBX as u32)],
);
let size = analysis.get_instruction_size(&inst);
assert_eq!(size, 3);
}
#[test]
fn test_analysis_identical() {
let analysis = X86MCInstrAnalysis::default();
let a = make_mc_inst(2, vec![MCOperand::Reg(RAX as u32), MCOperand::Imm(5)]);
let b = make_mc_inst(2, vec![MCOperand::Reg(RAX as u32), MCOperand::Imm(5)]);
let c = make_mc_inst(2, vec![MCOperand::Reg(RBX as u32), MCOperand::Imm(5)]);
assert!(analysis.are_identical(&a, &b));
assert!(!analysis.are_identical(&a, &c));
}
#[test]
fn test_asm_printer_new() {
let printer = X86MCAsmPrinter::new(AsmSyntax::ATT, X86MCSubtargetInfo::default());
assert!(printer.output.is_empty());
}
#[test]
fn test_asm_printer_emit_instruction_att() {
let mut printer = X86MCAsmPrinter::new(AsmSyntax::ATT, X86MCSubtargetInfo::default());
let inst = make_mc_inst(
1,
vec![MCOperand::Reg(RAX as u32), MCOperand::Reg(RBX as u32)],
);
printer.emit_instruction(&inst);
let out = printer.finish();
assert!(out.contains("mov"));
assert!(out.contains("%rax"));
assert!(out.contains("%rbx"));
}
#[test]
fn test_asm_printer_emit_instruction_intel() {
let mut printer = X86MCAsmPrinter::new(AsmSyntax::Intel, X86MCSubtargetInfo::default());
let inst = make_mc_inst(2, vec![MCOperand::Reg(RAX as u32), MCOperand::Imm(42)]);
printer.emit_instruction(&inst);
let out = printer.finish();
assert!(out.contains("add"));
assert!(out.contains("RAX"));
assert!(out.contains("42"));
}
#[test]
fn test_asm_printer_emit_label() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_label("main");
let out = printer.finish();
assert!(out.contains("main:"));
}
#[test]
fn test_asm_printer_emit_global() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_global("main");
let out = printer.finish();
assert!(out.contains(".globl\tmain"));
}
#[test]
fn test_asm_printer_emit_cfi() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_cfi(&X86CFIInstruction::StartProc);
printer.emit_cfi(&X86CFIInstruction::DefCfa {
register: RBP,
offset: 16,
});
let out = printer.finish();
assert!(out.contains(".cfi_startproc"));
assert!(out.contains(".cfi_def_cfa"));
}
#[test]
fn test_asm_printer_emit_function_prologue() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_function_prologue("my_func");
let out = printer.finish();
assert!(out.contains("my_func:"));
assert!(out.contains("push"));
assert!(out.contains(".cfi_startproc"));
}
#[test]
fn test_asm_printer_emit_alignment() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_alignment(16);
let out = printer.finish();
assert!(out.contains(".align\t16"));
}
#[test]
fn test_asm_printer_section_directive() {
let mut printer = X86MCAsmPrinter::default();
let text_section = MCSection::new("my_text", MCSectionType::Text);
printer.emit_section_directive(&text_section);
let out = printer.finish();
assert!(out.contains(".text"));
}
#[test]
fn test_cfi_display_startproc() {
let cfi = X86CFIInstruction::StartProc;
assert_eq!(format!("{}", cfi), ".cfi_startproc");
}
#[test]
fn test_cfi_display_endproc() {
let cfi = X86CFIInstruction::EndProc;
assert_eq!(format!("{}", cfi), ".cfi_endproc");
}
#[test]
fn test_code_emitter_nop() {
let mut emitter = X86MCCodeEmitter::default();
let inst = make_mc_inst(24, vec![]);
let encoded = emitter.encode_instruction(&inst);
assert_eq!(encoded, vec![0x90]);
}
#[test]
fn test_code_emitter_ret() {
let mut emitter = X86MCCodeEmitter::default();
let inst = make_mc_inst(14, vec![]);
let encoded = emitter.encode_instruction(&inst);
assert_eq!(encoded, vec![0xC3]);
}
#[test]
fn test_code_emitter_offset_tracking() {
let mut emitter = X86MCCodeEmitter::default();
assert_eq!(emitter.current_offset, 0);
let inst = make_mc_inst(24, vec![]);
emitter.encode_instruction(&inst);
assert!(emitter.current_offset > 0);
}
#[test]
fn test_code_emitter_alignment() {
let mut emitter = X86MCCodeEmitter::default();
emitter.emit_data(&[1, 2, 3]); let padding = emitter.emit_alignment(16);
assert_eq!(padding.len() + 3, 16);
}
#[test]
fn test_code_emitter_record_label() {
let mut emitter = X86MCCodeEmitter::default();
emitter.emit_data(&[0u8; 10]);
emitter.record_label("my_label");
assert_eq!(emitter.label_locations.get("my_label"), Some(&10));
}
#[test]
fn test_code_emitter_reset() {
let mut emitter = X86MCCodeEmitter::default();
emitter.emit_data(&[1, 2, 3]);
emitter.record_label("test");
emitter.reset();
assert_eq!(emitter.current_offset, 0);
assert!(emitter.label_locations.is_empty());
assert!(emitter.fixups.is_empty());
}
#[test]
fn test_fixup_kind_size() {
assert_eq!(X86FixupKind::Abs32.size(), 4);
assert_eq!(X86FixupKind::Abs64.size(), 8);
assert_eq!(X86FixupKind::PcRel8.size(), 1);
assert_eq!(X86FixupKind::PcRel32.size(), 4);
}
#[test]
fn test_fixup_kind_pcrel() {
assert!(X86FixupKind::PcRel32.is_pc_relative());
assert!(X86FixupKind::RipRel4Byte.is_pc_relative());
assert!(X86FixupKind::Plt32.is_pc_relative());
assert!(!X86FixupKind::Abs64.is_pc_relative());
assert!(!X86FixupKind::Abs32.is_pc_relative());
}
#[test]
fn test_fixup_to_elf_reloc() {
assert_eq!(X86FixupKind::Abs64.to_elf_relocation(), Some(R_X86_64_64));
assert_eq!(
X86FixupKind::PcRel32.to_elf_relocation(),
Some(R_X86_64_PC32)
);
assert_eq!(
X86FixupKind::Plt32.to_elf_relocation(),
Some(R_X86_64_PLT32)
);
}
#[test]
fn test_nop_padding_lengths() {
let emitter = X86MCCodeEmitter::default();
for size in 1..=16 {
let padding = emitter.generate_nop_padding(size);
assert_eq!(
padding.len(),
size,
"Padding should be exactly {} bytes, got {}",
size,
padding.len()
);
}
}
#[test]
fn test_object_writer_new_elf() {
let writer = X86MCObjectWriter::new_elf("x86_64-unknown-linux-gnu");
assert!(matches!(writer.format, ObjectFormat::ELF));
assert_eq!(writer.target_triple, "x86_64-unknown-linux-gnu");
}
#[test]
fn test_object_writer_add_section() {
let mut writer = X86MCObjectWriter::default();
let section = X86ObjectSection {
name: ".text".into(),
data: vec![0x90, 0xC3],
flags: X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
section_type: X86SectionType::Text,
alignment: 16,
relocations: Vec::new(),
};
let idx = writer.add_section(section);
assert_eq!(idx, 0);
assert_eq!(writer.sections.len(), 1);
}
#[test]
fn test_object_writer_add_symbol() {
let mut writer = X86MCObjectWriter::default();
let sym = X86ObjectSymbol {
name: "main".into(),
value: 0,
size: 0,
section_index: 1,
is_global: true,
is_weak: false,
is_undefined: false,
is_function: true,
is_object: false,
is_tls: false,
is_absolute: false,
};
let idx = writer.add_symbol(sym);
assert_eq!(idx, 0);
assert_eq!(writer.symbols.len(), 1);
}
#[test]
fn test_object_writer_write_elf_empty() {
let writer = X86MCObjectWriter::new_elf("x86_64-unknown-linux-gnu");
let result = writer.write();
assert_eq!(&result[0..4], &[0x7f, b'E', b'L', b'F']);
}
#[test]
fn test_object_writer_write_elf_with_text() {
let mut writer = X86MCObjectWriter::new_elf("x86_64-unknown-linux-gnu");
let section = X86ObjectSection {
name: ".text".into(),
data: vec![0x90, 0x90, 0xC3], flags: X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
section_type: X86SectionType::Text,
alignment: 16,
relocations: Vec::new(),
};
writer.add_section(section);
let result = writer.write();
assert!(result.len() > 64); assert_eq!(&result[0..4], &[0x7f, b'E', b'L', b'F']);
}
#[test]
fn test_object_writer_elf_header_fields() {
let writer = X86MCObjectWriter::new_elf("x86_64-unknown-linux-gnu");
let result = writer.write();
assert_eq!(result[4], 2);
assert_eq!(result[5], 1);
let machine = u16::from_le_bytes([result[18], result[19]]);
assert_eq!(machine, 62);
}
#[test]
fn test_object_writer_export_fixups() {
let mut writer = X86MCObjectWriter::default();
let fixups = vec![X86MCFixup {
offset: 0,
kind: X86FixupKind::PcRel32,
symbol: Some("foo".into()),
value: 0,
size: 4,
is_pcrel: true,
loc: None,
}];
writer.import_fixups(&fixups);
assert_eq!(writer.fixups.len(), 1);
}
#[test]
fn test_object_writer_section_elf_flags() {
let writer = X86MCObjectWriter::default();
let flags = X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
};
let elf_flags = writer.elf_section_flags(&flags);
assert!(elf_flags & (1 << 1) != 0); assert!(elf_flags & (1 << 2) != 0); assert!(elf_flags & (1 << 0) == 0); }
#[test]
fn test_create_simple_elf_object() {
let inst = make_mc_inst(24, vec![]); let symbols = vec![("main", 0u64)];
let obj = create_simple_elf_object(&[inst], &symbols);
assert!(!obj.is_empty());
assert_eq!(&obj[0..4], &[0x7f, b'E', b'L', b'F']);
}
#[test]
fn test_object_writer_uuid_generation() {
let mut writer = X86MCObjectWriter::new_macho("x86_64-apple-darwin");
assert!(writer.uuid.is_none());
writer.generate_uuid();
assert!(writer.uuid.is_some());
let uuid = writer.uuid.unwrap();
assert_eq!(uuid.len(), 16);
}
#[test]
fn test_asm_backend_new() {
let backend = X86MCAsmBackend::default();
assert_eq!(backend.target_name(), "x86_64");
}
#[test]
fn test_asm_backend_emit_instruction() {
let mut backend = X86MCAsmBackend::default();
backend.start_section(
".text",
X86SectionType::Text,
X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
);
let inst = make_mc_inst(24, vec![]); backend.emit_instruction(&inst);
assert!(!backend.section_data.is_empty());
}
#[test]
fn test_asm_backend_switch_section() {
let mut backend = X86MCAsmBackend::default();
backend.switch_section(".text");
assert!(!backend.object_writer.sections.is_empty());
assert_eq!(backend.object_writer.sections[0].name, ".text");
assert_eq!(
backend.object_writer.sections[0].section_type,
X86SectionType::Text
);
backend.switch_section(".data");
assert_eq!(backend.object_writer.sections.len(), 2);
assert_eq!(backend.object_writer.sections[1].name, ".data");
}
#[test]
fn test_asm_backend_section_push_pop() {
let mut backend = X86MCAsmBackend::default();
backend.switch_section(".text");
backend.push_section();
backend.switch_section(".data");
assert_eq!(backend.section_stack.len(), 1);
assert_eq!(backend.section_stack[0], ".text");
backend.pop_section();
assert_eq!(backend.section_stack.len(), 0);
}
#[test]
fn test_asm_backend_get_relaxed_size() {
let backend = X86MCAsmBackend::default();
assert_eq!(backend.get_relaxed_instruction_size(), 17);
}
#[test]
fn test_target_streamer_new() {
let streamer = X86MCTargetStreamer::default();
assert!(streamer.emit_asm);
assert!(streamer.emit_obj);
}
#[test]
fn test_target_streamer_emit_instruction() {
let mut streamer = X86MCTargetStreamer::default();
let inst = make_mc_inst(24, vec![]);
streamer.emit_instruction(&inst);
let (asm, obj) = streamer.finish();
assert!(asm.contains("nop"));
assert!(!obj.is_empty());
}
#[test]
fn test_target_streamer_emit_global() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_global("my_symbol");
let (asm, _) = streamer.finish();
assert!(asm.contains(".globl\tmy_symbol"));
}
#[test]
fn test_target_streamer_begin_end_function() {
let mut streamer = X86MCTargetStreamer::default();
streamer.begin_function("test_func");
assert_eq!(streamer.current_function, Some("test_func".into()));
streamer.end_function();
assert_eq!(streamer.current_function, None);
let (asm, _) = streamer.finish();
assert!(asm.contains("test_func"));
assert!(asm.contains(".cfi_startproc"));
}
#[test]
fn test_target_streamer_set_no_exec_stack() {
let mut streamer = X86MCTargetStreamer::default();
streamer.set_no_exec_stack(true);
assert!(streamer.backend.ctx.no_exec_stack);
}
#[test]
fn test_create_x86_mc_pipeline() {
let (ctx, backend, code_emitter, obj_writer, asm_printer, streamer) =
create_x86_mc_pipeline(
"x86_64-unknown-linux-gnu",
"skylake",
&["+sse2", "+avx2"],
ObjectFormat::ELF,
true,
true,
);
assert_eq!(ctx.target_triple, "x86_64-unknown-linux-gnu");
assert_eq!(backend.target_name(), "x86_64");
assert!(!obj_writer.target_triple.is_empty());
}
#[test]
fn test_encode_to_elf() {
let inst = make_mc_inst(24, vec![]);
let encoded = encode_to_elf(&inst, "x86_64-unknown-linux-gnu");
assert!(!encoded.is_empty());
assert_eq!(encoded, vec![0x90]);
}
#[test]
fn test_full_elf_roundtrip_construction() {
let mut writer = X86MCObjectWriter::new_elf("x86_64-unknown-linux-gnu");
let text_section = X86ObjectSection {
name: ".text".into(),
data: vec![0x90, 0x90, 0xC3],
flags: X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
section_type: X86SectionType::Text,
alignment: 16,
relocations: Vec::new(),
};
writer.add_section(text_section);
writer.add_symbol(X86ObjectSymbol {
name: "my_function".into(),
value: 0,
size: 3,
section_index: 1,
is_global: true,
is_weak: false,
is_undefined: false,
is_function: true,
is_object: false,
is_tls: false,
is_absolute: false,
});
let result = writer.write();
assert_eq!(&result[0..4], &[0x7f, b'E', b'L', b'F']);
assert_eq!(result[4], 2);
assert!(result.len() > 100);
}
#[test]
fn test_coff_writer_header() {
let mut writer = X86MCObjectWriter::new_coff("x86_64-pc-windows-msvc");
let section = X86ObjectSection {
name: ".text".into(),
data: vec![0x90, 0xC3],
flags: X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
section_type: X86SectionType::Text,
alignment: 16,
relocations: Vec::new(),
};
writer.add_section(section);
writer.add_symbol(X86ObjectSymbol {
name: "main".into(),
value: 0,
size: 2,
section_index: 1,
is_global: true,
is_weak: false,
is_undefined: false,
is_function: true,
is_object: false,
is_tls: false,
is_absolute: false,
});
let result = writer.write();
assert!(result.len() >= 20);
let machine = u16::from_le_bytes([result[0], result[1]]);
assert_eq!(machine, 0x8664);
}
#[test]
fn test_macho_writer_header() {
let mut writer = X86MCObjectWriter::new_macho("x86_64-apple-darwin");
writer.generate_uuid();
let section = X86ObjectSection {
name: "__text".into(),
data: vec![0x90, 0xC3],
flags: X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
section_type: X86SectionType::Text,
alignment: 16,
relocations: Vec::new(),
};
writer.add_section(section);
let result = writer.write();
assert!(result.len() > 32);
let magic = u32::from_le_bytes([result[0], result[1], result[2], result[3]]);
assert_eq!(magic, 0xFEEDFACF);
}
#[test]
fn test_section_type_elf_mapping() {
let writer = X86MCObjectWriter::default();
assert_eq!(writer.elf_section_type(&X86SectionType::Text), 1); assert_eq!(writer.elf_section_type(&X86SectionType::Bss), 8); assert_eq!(writer.elf_section_type(&X86SectionType::Note), 7); }
#[test]
fn test_asm_printer_emit_ascii() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_ascii("hello world");
let out = printer.finish();
assert!(out.contains(".ascii"));
assert!(out.contains("hello world"));
}
#[test]
fn test_asm_printer_emit_asciz() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_asciz("hello");
let out = printer.finish();
assert!(out.contains(".asciz"));
assert!(out.contains("hello"));
}
#[test]
fn test_asm_printer_emit_quad() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_quad(0xDEADBEEF_CAFEBABE);
let out = printer.finish();
assert!(out.contains(".quad"));
}
#[test]
fn test_asm_printer_emit_module() {
let mut printer = X86MCAsmPrinter::default();
let ctx = X86MCContext::default();
printer.emit_module(
&ctx,
&[(
".text".into(),
&[make_mc_inst(24, vec![]), make_mc_inst(14, vec![])],
)],
);
let out = printer.finish();
assert!(out.contains(".file"));
assert!(out.contains(".text"));
assert!(out.contains("nop"));
assert!(out.contains("ret"));
}
#[test]
fn test_target_streamer_emit_cfi_full() {
let mut streamer = X86MCTargetStreamer::default();
streamer.begin_function("func");
streamer.emit_cfi(&X86CFIInstruction::DefCfa {
register: RBP,
offset: 16,
});
streamer.emit_cfi(&X86CFIInstruction::Offset {
register: RAX,
offset: -8,
});
streamer.end_function();
let (asm, _) = streamer.finish();
assert!(asm.contains(".cfi_def_cfa"));
assert!(asm.contains(".cfi_offset"));
assert!(asm.contains(".cfi_endproc"));
}
#[test]
fn test_analysis_needs_scheduling_metadata() {
let analysis = X86MCInstrAnalysis::default();
let syscall = make_mc_inst(35, vec![]);
assert!(analysis.has_side_effects(&syscall));
assert!(!analysis.can_speculate(&syscall));
let nop = make_mc_inst(24, vec![]);
assert!(!analysis.has_side_effects(&nop));
}
#[test]
fn test_object_writer_add_relocation() {
let mut writer = X86MCObjectWriter::default();
let section = X86ObjectSection {
name: ".text".into(),
data: vec![0u8; 16],
flags: X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
section_type: X86SectionType::Text,
alignment: 16,
relocations: Vec::new(),
};
writer.add_section(section);
let reloc = X86ObjectRelocation {
offset: 4,
symbol_index: 0,
rel_type: R_X86_64_PC32,
addend: -4,
is_pcrel: true,
size: 4,
};
writer.add_relocation(0, reloc);
assert_eq!(writer.sections[0].relocations.len(), 1);
}
#[test]
fn test_code_emitter_with_pic() {
let mut emitter = X86MCCodeEmitter::default();
assert!(!emitter.is_pic);
let ctx = X86MCContext {
is_pic: true,
..Default::default()
};
emitter.set_context(ctx);
assert!(emitter.is_pic);
}
#[test]
fn test_subtarget_i386() {
let sti = X86MCSubtargetInfo::new("i386", vec![]);
assert!(!sti.is_64bit);
assert_eq!(sti.sched_model, "i686");
}
#[test]
fn test_dwarf_reg_number_roundtrip() {
let info = X86MCRegisterInfo::new_x86_64();
let dwarf_num = info.get_dwarf_reg_num(X86Reg(RBX));
assert!(dwarf_num.is_some());
let reg = info.get_reg_from_dwarf(dwarf_num.unwrap());
assert_eq!(reg, Some(X86Reg(RBX)));
}
#[test]
fn test_llvm_reg_num() {
let info = X86MCRegisterInfo::new_x86_64();
assert_eq!(info.get_llvm_reg_num(X86Reg(RAX)), RAX);
assert_eq!(info.get_llvm_reg_num(X86Reg(R15)), R15);
}
#[test]
fn test_fixup_display() {
assert_eq!(format!("{}", X86FixupKind::Abs64), "abs64");
assert_eq!(format!("{}", X86FixupKind::PcRel32), "pcrel32");
assert_eq!(format!("{}", X86FixupKind::Custom(42)), "custom(42)");
}
#[test]
fn test_asm_printer_note_gnu_stack() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_note_gnu_stack(true);
let out = printer.finish();
assert!(out.contains(".note.GNU-stack"));
let mut printer2 = X86MCAsmPrinter::default();
printer2.emit_note_gnu_stack(false);
let out2 = printer2.finish();
assert!(out2.contains(".note.GNU-stack"));
assert!(out2.contains(".previous"));
}
#[test]
fn test_target_streamer_full_module() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_header();
streamer.emit_section(".text");
streamer.emit_global("main");
streamer.emit_type("main", "function");
streamer.emit_label("main");
streamer.begin_function("main");
let mov_inst = make_mc_inst(1, vec![MCOperand::Reg(RAX as u32), MCOperand::Imm(42)]);
streamer.emit_instruction(&mov_inst);
let ret_inst = make_mc_inst(14, vec![]);
streamer.emit_instruction(&ret_inst);
streamer.end_function();
streamer.emit_size("main", 10);
streamer.set_no_exec_stack(true);
let (asm, obj) = streamer.finish();
assert!(asm.contains("main:"));
assert!(asm.contains("mov"));
assert!(asm.contains("$42"));
assert!(asm.contains("ret"));
assert!(asm.contains(".cfi_startproc"));
assert!(asm.contains(".cfi_endproc"));
assert!(asm.contains(".size"));
assert!(asm.contains(".note.GNU-stack"));
assert!(!obj.is_empty());
}
#[test]
fn test_code_emitter_multi_byte_nop() {
let emitter = X86MCCodeEmitter::default();
for size in [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 30, 31,
] {
let padding = emitter.generate_nop_padding(size);
assert_eq!(padding.len(), size, "Failed for size {}", size);
assert!(
padding[0] == 0x90 || padding[0] == 0x66 || padding[0] == 0x0F,
"First byte of NOP padding should be 0x90, 0x66, or 0x0F at size {}: got 0x{:02X}",
size,
padding[0]
);
}
}
#[test]
fn test_relocation_model() {
let mut ctx = X86MCContext::default();
assert!(matches!(ctx.relocation_model, X86RelocationModel::Static));
ctx.set_relocation_model(X86RelocationModel::PIC);
assert!(matches!(ctx.relocation_model, X86RelocationModel::PIC));
}
#[test]
fn test_context_add_flag() {
let mut ctx = X86MCContext::default();
ctx.add_flag(AssemblerFlag::Code64);
assert_eq!(ctx.flags.len(), 1);
ctx.add_flag(AssemblerFlag::NoExecStack);
assert_eq!(ctx.flags.len(), 2);
}
#[test]
fn test_context_debug_entries() {
let mut ctx = X86MCContext::default();
ctx.record_debug_file(X86DebugFileEntry {
file_id: 1,
directory_id: 0,
name: "test.c".into(),
directory: "/tmp".into(),
md5_checksum: None,
});
assert_eq!(ctx.debug_files.len(), 1);
ctx.record_debug_line(X86DebugLineEntry {
file_id: 1,
line: 42,
column: 10,
..Default::default()
});
assert_eq!(ctx.debug_lines.len(), 1);
ctx.record_debug_frame(X86DebugFrameEntry {
is_cie: true,
length: 16,
cie_id: 0,
version: 1,
augmentation: "zR".into(),
code_alignment_factor: 1,
data_alignment_factor: -8,
return_address_register: 16,
initial_instructions: vec![0x0C, 0x07, 0x08],
fde_instructions: vec![],
pc_begin: 0,
pc_range: 0,
lsda_pointer: None,
});
assert_eq!(ctx.debug_frame_entries.len(), 1);
}
#[test]
fn test_section_flags_default() {
let flags = X86SectionFlags::default();
assert!(!flags.alloc);
assert!(!flags.write);
assert!(!flags.exec);
assert!(!flags.merge);
assert!(!flags.strings);
}
#[test]
fn test_subtarget_atom_cpu() {
let sti = X86MCSubtargetInfo::new("atom", vec![]);
assert!(sti.has_sse2);
assert!(sti.has_movbe);
assert!(!sti.has_avx);
assert_eq!(sti.sched_model, "atom");
}
#[test]
fn test_asm_printer_verbose_encoding() {
let mut printer = X86MCAsmPrinter::new(AsmSyntax::ATT, X86MCSubtargetInfo::default());
printer.show_encoding = true;
let inst = make_mc_inst(24, vec![]);
printer.emit_instruction(&inst);
let out = printer.finish();
assert!(out.contains("encoding:"));
}
#[test]
fn test_register_name_lookup() {
assert_eq!(reg_name(RAX), "rax");
assert_eq!(reg_name(RBP), "rbp");
assert_eq!(reg_name(RSP), "rsp");
assert_eq!(reg_name(R12), "r12");
assert_eq!(reg_name(R15), "r15");
}
#[test]
fn test_dwarf_reg_names_xmm() {
let info = X86MCRegisterInfo::new_x86_64();
assert_eq!(info.get_dwarf_reg_num(X86Reg(XMM0)), Some(17));
assert_eq!(info.get_dwarf_reg_num(X86Reg(XMM15)), Some(32));
assert_eq!(info.get_dwarf_reg_num(X86Reg(K0)), Some(118));
}
#[test]
fn test_code_emitter_multiple_instructions() {
let mut emitter = X86MCCodeEmitter::default();
let nop = make_mc_inst(24, vec![]);
let ret = make_mc_inst(14, vec![]);
let enc1 = emitter.encode_instruction(&nop);
let enc2 = emitter.encode_instruction(&ret);
assert_eq!(enc1, vec![0x90]);
assert_eq!(enc2, vec![0xC3]);
assert_eq!(emitter.current_offset, 2);
}
#[test]
fn test_object_writer_multiple_sections() {
let mut writer = X86MCObjectWriter::new_elf("x86_64-unknown-linux-gnu");
let text = X86ObjectSection {
name: ".text".into(),
data: vec![0x90, 0xC3],
flags: X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
section_type: X86SectionType::Text,
alignment: 16,
relocations: Vec::new(),
};
let data = X86ObjectSection {
name: ".data".into(),
data: vec![0x42, 0x00, 0x00, 0x00],
flags: X86SectionFlags {
alloc: true,
write: true,
..Default::default()
},
section_type: X86SectionType::Data,
alignment: 8,
relocations: Vec::new(),
};
writer.add_section(text);
writer.add_section(data);
assert_eq!(writer.sections.len(), 2);
let result = writer.write();
assert!(result.len() > 100);
}
#[test]
fn test_object_writer_with_relocations() {
let mut writer = X86MCObjectWriter::new_elf("x86_64-unknown-linux-gnu");
let mut text = X86ObjectSection {
name: ".text".into(),
data: vec![0xE8, 0x00, 0x00, 0x00, 0x00], flags: X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
section_type: X86SectionType::Text,
alignment: 16,
relocations: Vec::new(),
};
text.relocations.push(X86ObjectRelocation {
offset: 1,
symbol_index: 0,
rel_type: R_X86_64_PC32,
addend: -4,
is_pcrel: true,
size: 4,
});
writer.add_section(text);
writer.add_symbol(X86ObjectSymbol {
name: "target".into(),
value: 0x1000,
size: 0,
section_index: 1,
is_global: true,
is_weak: false,
is_undefined: false,
is_function: true,
is_object: false,
is_tls: false,
is_absolute: false,
});
let result = writer.write();
assert!(!result.is_empty());
}
#[test]
fn test_asm_backend_full_function_emission() {
let mut backend = X86MCAsmBackend::default();
backend.switch_section(".text");
backend.emit_instruction(&make_mc_inst(11, vec![MCOperand::Reg(RBP as u32)]));
backend.emit_instruction(&make_mc_inst(
1,
vec![MCOperand::Reg(RBP as u32), MCOperand::Reg(RSP as u32)],
));
backend.emit_instruction(&make_mc_inst(
1,
vec![MCOperand::Reg(RAX as u32), MCOperand::Imm(0)],
));
backend.emit_instruction(&make_mc_inst(12, vec![MCOperand::Reg(RBP as u32)]));
backend.emit_instruction(&make_mc_inst(14, vec![]));
let result = backend.finish();
assert!(!result.is_empty());
assert_eq!(&result[0..4], &[0x7f, b'E', b'L', b'F']);
}
#[test]
fn test_target_streamer_data_directives() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_section(".data");
streamer.emit_byte(0x42);
streamer.emit_short(0x1234);
streamer.emit_long(0xDEADBEEF);
streamer.emit_quad(0x0123456789ABCDEF);
let (asm, obj) = streamer.finish();
assert!(asm.contains(".byte"));
assert!(asm.contains(".short"));
assert!(asm.contains(".long"));
assert!(asm.contains(".quad"));
assert!(!obj.is_empty());
}
#[test]
fn test_target_streamer_asciz_emission() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_section(".rodata");
streamer.emit_asciz("test_string");
let (asm, _) = streamer.finish();
assert!(asm.contains("test_string"));
}
#[test]
fn test_analysis_cond_branch_properties() {
let analysis = X86MCInstrAnalysis::default();
let je = make_mc_inst(16, vec![MCOperand::Imm(100)]);
let jne = make_mc_inst(17, vec![MCOperand::Imm(200)]);
assert!(analysis.is_conditional_branch(&je));
assert!(analysis.is_conditional_branch(&jne));
assert!(!analysis.is_unconditional_branch(&je));
assert!(analysis.is_branch(&je));
assert!(!analysis.is_call(&je));
assert!(!analysis.is_return(&je));
}
#[test]
fn test_instr_info_side_effects() {
let info = X86MCInstrInfo::new();
assert!(info.has_side_effects.contains(&35)); assert!(info.has_side_effects.contains(&13)); assert!(!info.has_side_effects.contains(&1)); }
#[test]
fn test_context_multiple_sections() {
let mut ctx = X86MCContext::default();
let s1 = ctx.get_or_create_section(MCSection::new("sec1", MCSectionType::Text));
let s2 = ctx.get_or_create_section(MCSection::new("sec2", MCSectionType::Data));
assert_eq!(s1, 0);
assert_eq!(s2, 1);
assert_eq!(ctx.sections.len(), 2);
}
#[test]
fn test_code_emitter_padding_alignment() {
let mut emitter = X86MCCodeEmitter::default();
emitter.emit_data(&[1, 2, 3, 4, 5]);
let padding = emitter.emit_alignment(16);
assert_eq!(padding.len(), 11);
assert_eq!(emitter.current_offset, 16);
}
#[test]
fn test_asm_printer_data_directives() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_byte(0xFF);
printer.emit_short(0xABCD);
printer.emit_long(0x12345678);
printer.emit_quad(0xFFFFFFFFFFFFFFFF);
let out = printer.finish();
assert!(out.contains(".byte\t255"));
assert!(out.contains(".short"));
assert!(out.contains(".long"));
assert!(out.contains(".quad"));
}
#[test]
fn test_asm_printer_skip_directive() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_skip(128);
let out = printer.finish();
assert!(out.contains(".skip\t128"));
}
#[test]
fn test_asm_printer_equ_directive() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_equ("SIZE", 4096);
let out = printer.finish();
assert!(out.contains(".equ\tSIZE, 4096"));
}
#[test]
fn test_target_streamer_fill_directive() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_section(".bss");
streamer.emit_fill(10, 4, 0);
let (asm, _) = streamer.finish();
assert!(asm.contains(".fill"));
}
#[test]
fn test_target_streamer_org_directive() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_section(".text");
streamer.emit_byte(0x90);
streamer.emit_org(0x100);
let (asm, obj) = streamer.finish();
assert!(asm.contains(".org"));
}
#[test]
fn test_subtarget_feature_flag_unknown() {
let mut sti = X86MCSubtargetInfo::new("generic", vec![]);
sti.apply_feature_flag("+my-custom-feat");
assert!(sti.feature_names.contains(&"my-custom-feat".to_string()));
}
#[test]
fn test_subtarget_feature_negative_flag() {
let mut sti = X86MCSubtargetInfo::new("skylake", vec![]);
assert!(sti.has_avx);
sti.apply_feature_flag("-avx");
assert!(!sti.has_avx);
}
#[test]
fn test_analysis_def_use_regs() {
let analysis = X86MCInstrAnalysis::default();
let inst = make_mc_inst(
2,
vec![MCOperand::Reg(RAX as u32), MCOperand::Reg(RBX as u32)],
);
let defs = analysis.get_def_regs(&inst);
let uses = analysis.get_use_regs(&inst);
assert!(defs.contains(&RAX));
assert!(uses.contains(&RBX));
}
#[test]
fn test_analysis_terminator_flag() {
let analysis = X86MCInstrAnalysis::default();
let ret = make_mc_inst(14, vec![]);
let nop = make_mc_inst(24, vec![]);
assert!(analysis.is_terminator(&ret));
assert!(!analysis.is_terminator(&nop));
}
#[test]
fn test_object_writer_section_alignment() {
let mut writer = X86MCObjectWriter::default();
writer.set_section_alignment(".text", 64);
assert_eq!(writer.section_alignments.get(".text"), Some(&64));
}
#[test]
fn test_object_writer_bss_section() {
let mut writer = X86MCObjectWriter::default();
let idx = writer.add_bss_section(".bss", 4096, 16);
assert_eq!(idx, 0);
assert_eq!(writer.sections[0].section_type, X86SectionType::Bss);
assert!(writer.sections[0].flags.write);
}
#[test]
fn test_object_writer_debug_section() {
let mut writer = X86MCObjectWriter::default();
writer.add_debug_section(".debug_line");
assert_eq!(writer.debug_sections.len(), 1);
assert_eq!(writer.debug_sections[0], ".debug_line");
}
#[test]
fn test_section_flags_all_combinations() {
let combos = vec![
(
X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
"text",
),
(
X86SectionFlags {
alloc: true,
write: true,
..Default::default()
},
"data",
),
(
X86SectionFlags {
alloc: true,
..Default::default()
},
"rodata",
),
(
X86SectionFlags {
alloc: true,
write: true,
tls: true,
..Default::default()
},
"tls",
),
(
X86SectionFlags {
debug: true,
..Default::default()
},
"debug",
),
];
for (flags, _name) in &combos {
let writer = X86MCObjectWriter::default();
let elf_flags = writer.elf_section_flags(flags);
assert!(elf_flags > 0 || !flags.alloc && !flags.exec && !flags.write);
}
}
#[test]
fn test_elf_section_type_all() {
let writer = X86MCObjectWriter::default();
let types = vec![
(X86SectionType::Text, 1),
(X86SectionType::Data, 1),
(X86SectionType::Rodata, 1),
(X86SectionType::Bss, 8),
(X86SectionType::Note, 7),
(X86SectionType::DebugInfo, 1),
(X86SectionType::EhFrame, 1),
(X86SectionType::InitArray, 1),
(X86SectionType::FiniArray, 1),
(X86SectionType::TlsData, 1),
(X86SectionType::TlsBss, 8),
(X86SectionType::Comment, 1),
(X86SectionType::Custom("test".into()), 1),
];
for (st, expected) in &types {
assert_eq!(
writer.elf_section_type(st),
*expected,
"Failed for {:?}",
st
);
}
}
#[test]
fn test_coff_section_characteristics() {
let writer = X86MCObjectWriter::default();
let exec_flags = X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
};
let chars = writer.coff_section_characteristics(&exec_flags);
assert!(chars & 0x20000000 != 0); assert!(chars & 0x00000020 != 0); }
#[test]
fn test_target_streamer_begin_function_multiple() {
let mut streamer = X86MCTargetStreamer::default();
streamer.begin_function("func1");
streamer.end_function();
streamer.begin_function("func2");
streamer.end_function();
let (asm, _) = streamer.finish();
assert!(asm.contains("func1"));
assert!(asm.contains("func2"));
}
#[test]
fn test_target_streamer_emit_label_then_code() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_section(".text");
streamer.emit_label("start");
streamer.emit_instruction(&make_mc_inst(24, vec![]));
let (asm, _) = streamer.finish();
assert!(asm.contains("start:"));
}
#[test]
fn test_cfi_instruction_complex_scenario() {
let mut ctx = X86MCContext::default();
ctx.record_cfi(X86CFIInstruction::StartProc);
ctx.record_cfi(X86CFIInstruction::DefCfa {
register: RSP,
offset: 8,
});
ctx.record_cfi(X86CFIInstruction::Offset {
register: RIP,
offset: -8,
});
ctx.record_cfi(X86CFIInstruction::DefCfaRegister(RBP));
ctx.record_cfi(X86CFIInstruction::DefCfaOffset(16));
ctx.record_cfi(X86CFIInstruction::RememberState);
ctx.record_cfi(X86CFIInstruction::Restore(RAX));
ctx.record_cfi(X86CFIInstruction::RestoreState);
ctx.record_cfi(X86CFIInstruction::EndProc);
assert_eq!(ctx.cfi_instructions.len(), 9);
}
#[test]
fn test_asm_printer_all_directives() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_section_directive(&MCSection::new("", MCSectionType::Text));
printer.emit_global("main");
printer.emit_label("main");
printer.emit_cfi(&X86CFIInstruction::StartProc);
printer.emit_instruction(&make_mc_inst(11, vec![MCOperand::Reg(RBP as u32)]));
printer.emit_instruction(&make_mc_inst(14, vec![]));
printer.emit_cfi(&X86CFIInstruction::EndProc);
printer.emit_size("main", 4);
let out = printer.finish();
assert!(out.contains(".text"));
assert!(out.contains(".globl\tmain"));
assert!(out.contains("main:"));
assert!(out.contains("push"));
assert!(out.contains("ret"));
assert!(out.contains(".size"));
}
#[test]
fn test_context_fragment_org() {
let mut ctx = X86MCContext::default();
ctx.emit_org(64);
let flat = ctx.flatten();
assert_eq!(flat.len(), 64);
assert!(flat.iter().all(|&b| b == 0));
}
#[test]
fn test_context_fragment_fill_expr() {
let mut ctx = X86MCContext::default();
ctx.emit_fill_expr(8, 0xAB);
let flat = ctx.flatten();
assert_eq!(flat.len(), 8);
}
#[test]
fn test_context_fragment_relaxable() {
let mut ctx = X86MCContext::default();
let inst = make_mc_inst(15, vec![MCOperand::Imm(0x1000)]);
ctx.emit_relaxable(inst, vec![0xEB, 0x02], vec![0xE9, 0x00, 0x10, 0x00, 0x00]);
let flat = ctx.flatten();
assert_eq!(&flat, &[0xEB, 0x02]); }
#[test]
fn test_reg_info_segment_class() {
let info = X86MCRegisterInfo::new_x86_64();
let seg_class = info.reg_classes.iter().find(|c| c.name == "SEGMENT");
assert!(seg_class.is_some());
assert!(!seg_class.unwrap().is_allocatable);
}
#[test]
fn test_reg_info_vector_classes() {
let info = X86MCRegisterInfo::new_x86_64();
let vr128 = info.reg_classes.iter().find(|c| c.name == "VR128").unwrap();
let vr256 = info.reg_classes.iter().find(|c| c.name == "VR256").unwrap();
let vr512 = info.reg_classes.iter().find(|c| c.name == "VR512").unwrap();
assert_eq!(vr128.alignment, 16);
assert_eq!(vr256.alignment, 32);
assert_eq!(vr512.alignment, 64);
}
#[test]
fn test_subtarget_empty_feature_flag() {
let mut sti = X86MCSubtargetInfo::new("generic", vec![]);
sti.apply_feature_flag("");
assert!(sti.is_64bit);
}
#[test]
fn test_subtarget_pentium4() {
let sti = X86MCSubtargetInfo::new("pentium4", vec![]);
assert!(sti.has_sse2);
assert!(!sti.is_64bit);
assert_eq!(sti.sched_model, "pentium4");
}
#[test]
fn test_subtarget_zen3() {
let sti = X86MCSubtargetInfo::new("znver3", vec![]);
assert!(sti.has_avx2);
assert!(sti.has_fma);
assert!(!sti.has_avx512f); }
#[test]
fn test_code_emitter_nop_padding_first_byte() {
let emitter = X86MCCodeEmitter::default();
for size in 1..=31 {
let padding = emitter.generate_nop_padding(size);
assert!(!padding.is_empty());
let first = padding[0];
assert!(
first == 0x90 || first == 0x66 || first == 0x0F,
"Unexpected first byte 0x{:02X} for size {}",
first,
size
);
}
}
#[test]
fn test_object_writer_coff_exclude_flag() {
let writer = X86MCObjectWriter::default();
let flags = X86SectionFlags {
exclude: true,
..Default::default()
};
let chars = writer.coff_section_characteristics(&flags);
assert!(chars & 0x00000800 != 0); }
#[test]
fn test_pipeline_default_config() {
let (ctx, backend, emitter, writer, printer, streamer) = create_x86_mc_pipeline(
"x86_64-unknown-linux-gnu",
"generic",
&[],
ObjectFormat::ELF,
true,
true,
);
assert_eq!(ctx.cpu, "generic");
assert!(matches!(writer.format, ObjectFormat::ELF));
assert!(streamer.emit_asm);
assert!(streamer.emit_obj);
}
#[test]
fn test_fixup_kind_display_all() {
let kinds = vec![
(X86FixupKind::Abs32, "abs32"),
(X86FixupKind::Abs64, "abs64"),
(X86FixupKind::Abs8, "abs8"),
(X86FixupKind::Abs16, "abs16"),
(X86FixupKind::PcRel32, "pcrel32"),
(X86FixupKind::PcRel8, "pcrel8"),
(X86FixupKind::PcRel16, "pcrel16"),
(X86FixupKind::RipRel4Byte, "riprel4byte"),
(X86FixupKind::RipRel4ByteMovqLoad, "riprel4byte_movqload"),
(X86FixupKind::Signed4Byte, "signed4byte"),
(X86FixupKind::Unsigned4Byte, "unsigned4byte"),
(X86FixupKind::Branch4BytePcrel, "branch4byte_pcrel"),
(X86FixupKind::GotPcRel, "gotpcrel"),
(X86FixupKind::Plt32, "plt32"),
(X86FixupKind::GotOff, "gotoff"),
(X86FixupKind::TlsGd, "tlsgd"),
(X86FixupKind::TlsLd, "tlsld"),
(X86FixupKind::GotTpOff, "gottpoff"),
(X86FixupKind::TpOff32, "tpoff32"),
(X86FixupKind::DtpOff32, "dtpoff32"),
(X86FixupKind::TlsDesc, "tlsdesc"),
(X86FixupKind::SecRel32, "secrel32"),
(X86FixupKind::SizeDirective, "size"),
];
for (kind, expected) in &kinds {
assert_eq!(format!("{}", kind), *expected);
}
}
#[test]
fn test_cfi_instruction_signal_frame() {
assert_eq!(
format!("{}", X86CFIInstruction::SignalFrame),
".cfi_signal_frame"
);
}
#[test]
fn test_cfi_instruction_escape() {
let cfi = X86CFIInstruction::Escape(vec![0x01, 0x02, 0x03]);
let s = format!("{}", cfi);
assert!(s.contains(".cfi_escape"));
}
#[test]
fn test_cfi_instruction_lsda() {
let cfi = X86CFIInstruction::Lsda {
encoding: 0x1B,
personality: "__gxx_personality_v0".into(),
};
let s = format!("{}", cfi);
assert!(s.contains(".cfi_lsda"));
assert!(s.contains("__gxx_personality_v0"));
}
#[test]
fn test_cfi_instruction_personality() {
let cfi = X86CFIInstruction::Personality {
encoding: 0x9B,
function: "__gcc_personality_v0".into(),
};
let s = format!("{}", cfi);
assert!(s.contains(".cfi_personality"));
}
#[test]
fn test_asm_printer_elf_section_directive() {
let mut printer = X86MCAsmPrinter::default();
let section = MCSectionELF::text();
printer.emit_elf_section_directive(§ion);
let out = printer.finish();
assert!(out.contains(".section"));
assert!(out.contains("ax"));
}
#[test]
fn test_asm_printer_coff_section_directive() {
let mut printer = X86MCAsmPrinter::default();
let section = MCSectionCOFF::text();
printer.emit_coff_section_directive(§ion);
let out = printer.finish();
assert!(out.contains(".section"));
assert!(out.contains("my_text"));
}
#[test]
fn test_macho_cpu_type_x86_64() {
let _cpu_type: u32 = 0x01000007;
assert_eq!(_cpu_type & 0x00FFFFFF, 7);
}
#[test]
fn test_analysis_may_load_store() {
let analysis = X86MCInstrAnalysis::default();
let mov = make_mc_inst(1, vec![]);
let nop = make_mc_inst(24, vec![]);
let ret = make_mc_inst(14, vec![]);
assert!(analysis.may_load(&mov));
assert!(analysis.may_load(&nop));
assert!(!analysis.may_load(&ret));
}
#[test]
fn test_context_section_offsets() {
let mut ctx = X86MCContext::default();
const _: bool = ctx.section_offsets.len() == 0;
}
#[test]
fn test_instruction_builder_construction() {
let inst: MCInst = MCInstBuilder::new(1)
.add_reg(RAX as u32)
.add_imm(42)
.build();
assert_eq!(inst.opcode, 1);
assert_eq!(inst.operands.len(), 2);
}
#[test]
fn test_target_streamer_complete_invocations() {
let mut streamer = X86MCTargetStreamer::default();
streamer.set_no_exec_stack(true);
streamer.emit_header();
streamer.emit_section(".text");
streamer.emit_alignment(16);
streamer.emit_global("_start");
streamer.emit_type("_start", "function");
streamer.emit_label("_start");
streamer.begin_function("_start");
streamer.emit_instruction(&make_mc_inst(
1,
vec![MCOperand::Reg(RAX as u32), MCOperand::Imm(1)],
));
streamer.emit_instruction(&make_mc_inst(
1,
vec![MCOperand::Reg(RDI as u32), MCOperand::Imm(1)],
));
streamer.emit_instruction(&make_mc_inst(35, vec![])); streamer.emit_instruction(&make_mc_inst(
1,
vec![MCOperand::Reg(RAX as u32), MCOperand::Imm(60)],
));
streamer.emit_instruction(&make_mc_inst(
1,
vec![MCOperand::Reg(RDI as u32), MCOperand::Imm(0)],
));
streamer.emit_instruction(&make_mc_inst(35, vec![]));
streamer.end_function();
streamer.emit_size("_start", 0);
streamer.emit_section(".rodata");
streamer.emit_label("msg");
streamer.emit_asciz("hello, world!");
let (asm, obj) = streamer.finish();
assert!(asm.contains("_start"));
assert!(asm.contains("hello, world!"));
assert!(asm.contains("syscall"));
assert!(!obj.is_empty());
}
#[test]
fn test_fixup_kind_size_all() {
let expectations = vec![
(X86FixupKind::Abs8, 1),
(X86FixupKind::PcRel8, 1),
(X86FixupKind::Abs16, 2),
(X86FixupKind::PcRel16, 2),
(X86FixupKind::Abs32, 4),
(X86FixupKind::PcRel32, 4),
(X86FixupKind::RipRel4Byte, 4),
(X86FixupKind::Plt32, 4),
(X86FixupKind::GotPcRel, 4),
(X86FixupKind::Abs64, 8),
];
for (kind, size) in &expectations {
assert_eq!(kind.size(), *size, "Wrong size for {:?}", kind);
}
}
#[test]
fn test_subtarget_advanced_features() {
let mut sti = X86MCSubtargetInfo::new("icelake-server", vec![]);
assert!(sti.has_avx512f);
assert!(sti.has_avx512bw);
assert!(sti.has_avx512dq);
assert!(sti.has_avx512vl);
}
#[test]
fn test_subtarget_pad_short_functions_flag() {
let mut sti = X86MCSubtargetInfo::new("generic", vec![]);
assert!(!sti.pad_short_functions);
sti.apply_feature_flag("+pad-short-functions");
assert!(sti.pad_short_functions);
}
#[test]
fn test_subtarget_slow_lea_flag() {
let mut sti = X86MCSubtargetInfo::new("generic", vec![]);
assert!(!sti.slow_3ops_lea);
sti.apply_feature_flag("+slow-3ops-lea");
assert!(sti.slow_3ops_lea);
}
#[test]
fn test_code_emitter_reset_between_uses() {
let mut emitter = X86MCCodeEmitter::default();
emitter.emit_data(&[0x90, 0x90]);
emitter.record_label("L1");
assert_eq!(emitter.current_offset, 2);
assert_eq!(emitter.label_locations.len(), 1);
emitter.reset();
assert_eq!(emitter.current_offset, 0);
assert_eq!(emitter.label_locations.len(), 0);
emitter.emit_data(&[0xC3]);
assert_eq!(emitter.current_offset, 1);
}
#[test]
fn test_asm_printer_comment() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_comment("This is a test");
let out = printer.finish();
assert!(out.contains("# This is a test"));
}
#[test]
fn test_asm_printer_blank_line() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_blank_line();
let out = printer.finish();
assert!(out.lines().any(|l| l.trim().is_empty()));
}
#[test]
fn test_asm_printer_verbose_comment() {
let mut streamer = X86MCTargetStreamer::default();
streamer.verbose = true;
streamer.emit_comment("optimized tail call");
let (asm, _) = streamer.finish();
assert!(asm.contains("optimized tail call"));
}
#[test]
fn test_asm_printer_non_verbose_comment() {
let mut streamer = X86MCTargetStreamer::default();
streamer.verbose = false;
streamer.emit_comment("should not appear");
let (asm, _) = streamer.finish();
assert!(!asm.contains("should not appear"));
}
#[test]
fn test_object_writer_symbol_table_basic() {
let mut writer = X86MCObjectWriter::default();
writer.add_symbol(X86ObjectSymbol {
name: "func1".into(),
value: 0,
size: 16,
section_index: 1,
is_global: true,
is_weak: false,
is_undefined: false,
is_function: true,
is_object: false,
is_tls: false,
is_absolute: false,
});
writer.add_symbol(X86ObjectSymbol {
name: "data1".into(),
value: 0x1000,
size: 8,
section_index: 2,
is_global: false,
is_weak: false,
is_undefined: false,
is_function: false,
is_object: true,
is_tls: false,
is_absolute: false,
});
assert_eq!(writer.symbols.len(), 2);
let symtab = writer.build_elf_symtab();
assert_eq!(symtab.len(), 72);
}
#[test]
fn test_elf_strtab_building() {
let mut writer = X86MCObjectWriter::default();
writer.add_symbol(X86ObjectSymbol {
name: "test_func".into(),
value: 0,
size: 0,
section_index: 1,
is_global: true,
is_weak: false,
is_undefined: false,
is_function: true,
is_object: false,
is_tls: false,
is_absolute: false,
});
let strtab = writer.build_elf_strtab();
assert!(strtab.windows(9).any(|w| w == b"test_func"));
}
#[test]
fn test_elf_shstrtab_building() {
let mut writer = X86MCObjectWriter::default();
writer.add_section(X86ObjectSection {
name: ".text".into(),
data: vec![],
flags: X86SectionFlags::default(),
section_type: X86SectionType::Text,
alignment: 1,
relocations: Vec::new(),
});
let shstrtab = writer.build_elf_shstrtab();
assert!(shstrtab.windows(5).any(|w| w == b".text"));
assert!(shstrtab.windows(7).any(|w| w == b".symtab"));
}
#[test]
fn test_elf_rela_building() {
let section = X86ObjectSection {
name: ".text".into(),
data: vec![0; 16],
flags: X86SectionFlags::default(),
section_type: X86SectionType::Text,
alignment: 1,
relocations: vec![
X86ObjectRelocation {
offset: 4,
symbol_index: 1,
rel_type: R_X86_64_PC32,
addend: -4,
is_pcrel: true,
size: 4,
},
X86ObjectRelocation {
offset: 8,
symbol_index: 2,
rel_type: R_X86_64_64,
addend: 0,
is_pcrel: false,
size: 8,
},
],
};
let writer = X86MCObjectWriter::default();
let rela = writer.build_elf_rela_section(§ion);
assert_eq!(rela.len(), 48);
}
#[test]
fn test_macho_uuid_command_generation() {
let mut writer = X86MCObjectWriter::new_macho("x86_64-apple-darwin");
writer.generate_uuid();
let uuid_cmd = writer.build_macho_uuid_cmd();
assert!(!uuid_cmd.is_empty());
let cmd = u32::from_le_bytes([uuid_cmd[0], uuid_cmd[1], uuid_cmd[2], uuid_cmd[3]]);
assert_eq!(cmd, 0x1B);
let cmdsize = u32::from_le_bytes([uuid_cmd[4], uuid_cmd[5], uuid_cmd[6], uuid_cmd[7]]);
assert_eq!(cmdsize, 24);
assert_eq!(uuid_cmd.len(), 24);
}
#[test]
fn test_macho_no_uuid_command() {
let writer = X86MCObjectWriter::new_macho("x86_64-apple-darwin");
let uuid_cmd = writer.build_macho_uuid_cmd();
assert!(uuid_cmd.is_empty());
}
#[test]
fn test_asm_backend_alignment_handling() {
let mut backend = X86MCAsmBackend::default();
backend.start_section(
".text",
X86SectionType::Text,
X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
);
backend.emit_data(&[0x90]); backend.emit_alignment(4); assert_eq!(backend.section_data.len(), 4);
}
#[test]
fn test_asm_backend_fixup_import() {
let mut backend = X86MCAsmBackend::default();
let fixup = X86MCFixup {
offset: 0,
kind: X86FixupKind::PcRel32,
symbol: Some("target".into()),
value: 0,
size: 4,
is_pcrel: true,
loc: None,
};
backend.emit_fixup(fixup);
assert_eq!(backend.code_emitter.pending_fixups.len(), 1);
}
#[test]
fn test_target_streamer_local_symbol() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_section(".text");
streamer.emit_local("helper");
streamer.emit_label("helper");
let (asm, _) = streamer.finish();
assert!(asm.contains(".local\thelper"));
assert!(asm.contains("helper:"));
}
#[test]
fn test_target_streamer_p2align() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_section(".text");
streamer.emit_p2align(4); let (asm, _) = streamer.finish();
assert!(asm.contains(".p2align\t4"));
}
#[test]
fn test_target_streamer_balign() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_balign(16, 0xCC);
let out = printer.finish();
assert!(out.contains(".balign\t16, 204"));
}
#[test]
fn test_context_construction_with_flags() {
let mut ctx = X86MCContext::new("x86_64-unknown-linux-gnu", "skylake");
ctx.add_flag(AssemblerFlag::Code64);
ctx.add_flag(AssemblerFlag::UseCFI);
ctx.add_flag(AssemblerFlag::NoExecStack);
ctx.add_flag(AssemblerFlag::SubsectionsViaSymbols);
assert_eq!(ctx.flags.len(), 4);
}
#[test]
fn test_reg_info_default() {
let info = X86MCRegisterInfo::default();
assert!(info.is_allocatable(X86Reg(RAX)));
assert!(info.reg_classes.len() > 5);
}
#[test]
fn test_instr_info_default() {
let info = X86MCInstrInfo::default();
assert!(info.num_instructions() > 0);
assert!(info.lookup_opcode("RET").is_some());
}
#[test]
fn test_analysis_default() {
let analysis = X86MCInstrAnalysis::default();
let nop = make_mc_inst(24, vec![]);
assert!(!analysis.is_branch(&nop));
assert!(!analysis.is_return(&nop));
}
#[test]
fn test_code_model_variants() {
assert_eq!(X86CodeModel::Small as u8, 0);
assert_eq!(X86CodeModel::Medium as u8, 1);
assert_eq!(X86CodeModel::Large as u8, 2);
assert_eq!(X86CodeModel::Kernel as u8, 3);
assert_eq!(X86CodeModel::Tiny as u8, 4);
}
#[test]
fn test_relocation_model_variants() {
let models = vec![
X86RelocationModel::Static,
X86RelocationModel::PIC,
X86RelocationModel::DynamicNoPic,
X86RelocationModel::ROPI,
X86RelocationModel::RWPI,
X86RelocationModel::ROPIRWPI,
];
assert_eq!(models.len(), 6);
}
#[test]
fn test_asm_printer_equ_set() {
let mut printer = X86MCAsmPrinter::default();
printer.emit_equ("STACK_SIZE", 8192);
printer.emit_set("BUFFER_SIZE", 4096);
let out = printer.finish();
assert!(out.contains(".equ\tSTACK_SIZE"));
assert!(out.contains(".set\tBUFFER_SIZE"));
}
#[test]
fn test_object_writer_import_fixups_multiple() {
let mut writer = X86MCObjectWriter::default();
let fixups = vec![
X86MCFixup {
offset: 0,
kind: X86FixupKind::Abs64,
symbol: Some("a".into()),
value: 0,
size: 8,
is_pcrel: false,
loc: None,
},
X86MCFixup {
offset: 8,
kind: X86FixupKind::PcRel32,
symbol: Some("b".into()),
value: 0,
size: 4,
is_pcrel: true,
loc: None,
},
X86MCFixup {
offset: 12,
kind: X86FixupKind::GotPcRel,
symbol: Some("c".into()),
value: 0,
size: 4,
is_pcrel: true,
loc: None,
},
];
writer.import_fixups(&fixups);
assert_eq!(writer.fixups.len(), 3);
}
#[test]
fn test_instr_info_get_desc() {
let info = X86MCInstrInfo::new();
let desc = info.get_desc(1);
assert!(desc.is_some());
assert_eq!(desc.unwrap().mnemonic, "MOV");
}
#[test]
fn test_asm_backend_end_section_preserves_data() {
let mut backend = X86MCAsmBackend::default();
backend.start_section(
".text",
X86SectionType::Text,
X86SectionFlags {
alloc: true,
exec: true,
..Default::default()
},
);
backend.emit_data(&[1, 2, 3, 4]);
backend.end_section();
assert_eq!(backend.object_writer.sections[0].data, vec![1, 2, 3, 4]);
assert!(backend.section_data.is_empty());
}
#[test]
fn test_create_pipeline_rejects_bad_cpu() {
let (_ctx, backend, _emitter, _writer, _printer, _streamer) = create_x86_mc_pipeline(
"x86_64-unknown-linux-gnu",
"nonexistent-cpu-v999",
&[],
ObjectFormat::ELF,
true,
true,
);
assert_eq!(backend.target_name(), "x86_64");
}
#[test]
fn test_debug_line_entry_default() {
let entry = X86DebugLineEntry::default();
assert_eq!(entry.address, 0);
assert_eq!(entry.file_id, 1);
assert_eq!(entry.line, 0);
assert!(!entry.is_stmt);
}
#[test]
fn test_debug_file_entry_fields() {
let entry = X86DebugFileEntry {
file_id: 2,
directory_id: 1,
name: "test.c".into(),
directory: "/src".into(),
md5_checksum: Some([0xAB; 16]),
};
assert_eq!(entry.file_id, 2);
assert_eq!(entry.name, "test.c");
assert!(entry.md5_checksum.is_some());
}
#[test]
fn test_debug_frame_entry_cie() {
let entry = X86DebugFrameEntry {
is_cie: true,
length: 16,
cie_id: 0,
version: 1,
augmentation: "zR".into(),
code_alignment_factor: 1,
data_alignment_factor: -8,
return_address_register: 16,
initial_instructions: vec![0x0c, 0x07, 0x08],
fde_instructions: vec![],
pc_begin: 0,
pc_range: 0,
lsda_pointer: None,
};
assert!(entry.is_cie);
assert_eq!(entry.cie_id, 0);
assert_eq!(entry.return_address_register, 16);
}
#[test]
fn test_debug_frame_entry_fde() {
let entry = X86DebugFrameEntry {
is_cie: false,
length: 24,
cie_id: 1,
version: 1,
augmentation: String::new(),
code_alignment_factor: 1,
data_alignment_factor: -8,
return_address_register: 16,
initial_instructions: vec![],
fde_instructions: vec![0x41, 0x0e, 0x10],
pc_begin: 0x4000,
pc_range: 0x100,
lsda_pointer: Some(0x5000),
};
assert!(!entry.is_cie);
assert_eq!(entry.pc_begin, 0x4000);
assert_eq!(entry.pc_range, 0x100);
assert_eq!(entry.lsda_pointer, Some(0x5000));
}
#[test]
fn test_fixup_kind_pcrel_matrix() {
let pcrel = vec![
X86FixupKind::PcRel32,
X86FixupKind::PcRel8,
X86FixupKind::PcRel16,
X86FixupKind::RipRel4Byte,
X86FixupKind::RipRel4ByteMovqLoad,
X86FixupKind::Branch4BytePcrel,
X86FixupKind::GotPcRel,
X86FixupKind::Plt32,
];
for k in &pcrel {
assert!(k.is_pc_relative(), "{:?} should be PC-relative", k);
}
let non_pcrel = vec![
X86FixupKind::Abs32,
X86FixupKind::Abs64,
X86FixupKind::Abs8,
X86FixupKind::Abs16,
X86FixupKind::Signed4Byte,
X86FixupKind::Unsigned4Byte,
];
for k in &non_pcrel {
assert!(!k.is_pc_relative(), "{:?} should NOT be PC-relative", k);
}
}
#[test]
fn test_target_streamer_push_pop_section() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_section(".text");
streamer.emit_byte(0x90);
streamer.backend.push_section();
streamer.emit_section(".data");
streamer.emit_long(0xDEADBEEF);
streamer.backend.pop_section();
streamer.emit_byte(0xC3);
let (_asm, _obj) = streamer.finish();
}
#[test]
fn test_context_temp_symbols_are_unique() {
let mut ctx = X86MCContext::default();
let mut names = HashSet::new();
for _ in 0..100 {
let sym = ctx.create_temp_symbol();
assert!(names.insert(sym.name.clone()));
}
assert_eq!(names.len(), 100);
}
#[test]
fn test_analysis_can_speculate_safe_instructions() {
let analysis = X86MCInstrAnalysis::default();
let mov = make_mc_inst(1, vec![]);
assert!(analysis.can_speculate(&mov));
}
#[test]
fn test_analysis_cannot_speculate_side_effect() {
let analysis = X86MCInstrAnalysis::default();
let syscall = make_mc_inst(35, vec![]);
assert!(!analysis.can_speculate(&syscall));
}
#[test]
fn test_section_flags_coff_debug() {
let writer = X86MCObjectWriter::default();
let flags = X86SectionFlags {
debug: true,
..Default::default()
};
let chars = writer.coff_section_characteristics(&flags);
assert!(chars & 0x02000000 != 0); }
#[test]
fn test_asm_printer_file_loc_directives() {
let mut streamer = X86MCTargetStreamer::default();
streamer.emit_file(1, "/src", "test.c");
streamer.emit_section(".text");
streamer.emit_loc(1, 10, 5);
streamer.emit_instruction(&make_mc_inst(24, vec![]));
let (asm, _) = streamer.finish();
assert!(asm.contains(".file\t1"));
assert!(asm.contains(".loc\t1 10 5"));
}
#[test]
fn test_context_dwarf_generation_flag() {
let mut ctx = X86MCContext::default();
assert!(!ctx.gen_dwarf);
ctx.set_gen_dwarf(true);
assert!(ctx.gen_dwarf);
}
#[test]
fn test_context_cfi_generation_flag() {
let mut ctx = X86MCContext::default();
assert!(!ctx.use_cfi);
ctx.set_use_cfi(true);
assert!(ctx.use_cfi);
}
#[test]
fn test_asm_printer_no_exec_stack_flag() {
let mut ctx = X86MCContext::default();
assert!(!ctx.no_exec_stack);
ctx.no_exec_stack = true;
assert!(ctx.no_exec_stack);
}
#[test]
fn test_cfi_instruction_advance_loc() {
let cfi = X86CFIInstruction::AdvanceLoc(42);
assert_eq!(format!("{}", cfi), ".cfi_advance_loc 42");
}
#[test]
fn test_cfi_instruction_window_save() {
let cfi = X86CFIInstruction::WindowSave;
assert_eq!(format!("{}", cfi), ".cfi_window_save");
}
#[test]
fn test_cfi_instruction_undefined() {
let cfi = X86CFIInstruction::Undefined(RAX);
let s = format!("{}", cfi);
assert!(s.contains(".cfi_undefined"));
}
#[test]
fn test_cfi_instruction_register() {
let cfi = X86CFIInstruction::Register {
reg1: RAX,
reg2: RBX,
};
let s = format!("{}", cfi);
assert!(s.contains(".cfi_register"));
}
#[test]
fn test_asm_printer_att_immediate_formatting() {
let mut printer = X86MCAsmPrinter::new(AsmSyntax::ATT, X86MCSubtargetInfo::default());
let inst = make_mc_inst(2, vec![MCOperand::Reg(RAX as u32), MCOperand::Imm(-42)]);
printer.emit_instruction(&inst);
let out = printer.finish();
assert!(out.contains("$-42"));
}
#[test]
fn test_asm_printer_intel_immediate_formatting() {
let mut printer = X86MCAsmPrinter::new(AsmSyntax::Intel, X86MCSubtargetInfo::default());
let inst = make_mc_inst(2, vec![MCOperand::Reg(RAX as u32), MCOperand::Imm(100)]);
printer.emit_instruction(&inst);
let out = printer.finish();
assert!(out.contains("add"));
assert!(out.contains("RAX"));
}
#[test]
fn test_fixup_kind_plenty_of_tests() {
let all_kinds = vec![
X86FixupKind::Abs32,
X86FixupKind::Abs64,
X86FixupKind::Abs8,
X86FixupKind::Abs16,
X86FixupKind::PcRel32,
X86FixupKind::PcRel8,
X86FixupKind::PcRel16,
X86FixupKind::RipRel4Byte,
X86FixupKind::RipRel4ByteMovqLoad,
X86FixupKind::Signed4Byte,
X86FixupKind::Unsigned4Byte,
X86FixupKind::Branch4BytePcrel,
X86FixupKind::GotPcRel,
X86FixupKind::Plt32,
X86FixupKind::GotOff,
X86FixupKind::TlsGd,
X86FixupKind::TlsLd,
X86FixupKind::GotTpOff,
X86FixupKind::TpOff32,
X86FixupKind::DtpOff32,
X86FixupKind::FirstVariantKind,
X86FixupKind::SizeDirective,
X86FixupKind::TlsDesc,
X86FixupKind::TlsDescCall,
X86FixupKind::SecRel32,
X86FixupKind::Custom(99),
];
for kind in &all_kinds {
let _s = format!("{}", kind);
let _size = kind.size();
let _pcrel = kind.is_pc_relative();
let _elf = kind.to_elf_relocation();
}
}
#[test]
fn test_asm_printer_module_header() {
let mut printer = X86MCAsmPrinter::default();
let ctx = X86MCContext::new("x86_64-unknown-linux-gnu", "skylake");
printer.emit_module_header(&ctx);
let out = printer.finish();
assert!(out.contains(".file"));
assert!(out.contains("x86_64-unknown-linux-gnu"));
}
}
#[derive(Debug, Clone)]
pub struct X86RelocationHandler;
impl X86RelocationHandler {
pub fn apply(
buf: &mut [u8],
offset: u64,
rel_type: u32,
symbol_value: u64,
addend: i64,
pc: u64,
) -> Result<(), String> {
let off = offset as usize;
match rel_type {
R_X86_64_NONE => Ok(()),
R_X86_64_64 => {
let value = symbol_value.wrapping_add(addend as u64);
if off + 8 <= buf.len() {
buf[off..off + 8].copy_from_slice(&value.to_le_bytes());
}
Ok(())
}
R_X86_64_PC32 => {
let value = symbol_value.wrapping_add(addend as u64).wrapping_sub(pc);
if off + 4 <= buf.len() {
buf[off..off + 4].copy_from_slice(&(value as u32).to_le_bytes());
}
Ok(())
}
R_X86_64_32 => {
let value = symbol_value.wrapping_add(addend as u64);
if off + 4 <= buf.len() {
buf[off..off + 4].copy_from_slice(&(value as u32).to_le_bytes());
}
Ok(())
}
R_X86_64_32S => {
let value = (symbol_value as i64).wrapping_add(addend);
if off + 4 <= buf.len() {
buf[off..off + 4].copy_from_slice(&(value as u32).to_le_bytes());
}
Ok(())
}
R_X86_64_PC8 => {
let value = (symbol_value as i64)
.wrapping_add(addend)
.wrapping_sub(pc as i64);
if off + 1 <= buf.len() {
buf[off] = value as u8;
}
Ok(())
}
R_X86_64_8 => {
let value = (symbol_value as i64).wrapping_add(addend);
if off + 1 <= buf.len() {
buf[off] = value as u8;
}
Ok(())
}
R_X86_64_PC16 => {
let value = (symbol_value as i64)
.wrapping_add(addend)
.wrapping_sub(pc as i64);
if off + 2 <= buf.len() {
buf[off..off + 2].copy_from_slice(&(value as u16).to_le_bytes());
}
Ok(())
}
R_X86_64_16 => {
let value = (symbol_value as i64).wrapping_add(addend);
if off + 2 <= buf.len() {
buf[off..off + 2].copy_from_slice(&(value as u16).to_le_bytes());
}
Ok(())
}
R_X86_64_GOTPCREL | R_X86_64_GOT32 | R_X86_64_PLT32 => {
if off + 4 <= buf.len() {
buf[off..off + 4].copy_from_slice(&(addend as u32).to_le_bytes());
}
Ok(())
}
R_X86_64_RELATIVE => {
let value = (pc as i64)
.wrapping_add(addend)
.wrapping_add(symbol_value as i64);
if off + 8 <= buf.len() {
buf[off..off + 8].copy_from_slice(&(value as u64).to_le_bytes());
}
Ok(())
}
R_X86_64_COPY | R_X86_64_GLOB_DAT | R_X86_64_JUMP_SLOT => {
if off + 8 <= buf.len() {
buf[off..off + 8]
.copy_from_slice(&(symbol_value.wrapping_add(addend as u64)).to_le_bytes());
}
Ok(())
}
_ => Err(format!("Unsupported X86-64 relocation type: {}", rel_type)),
}
}
pub fn reloc_name(rel_type: u32) -> &'static str {
match rel_type {
R_X86_64_NONE => "R_X86_64_NONE",
R_X86_64_64 => "R_X86_64_64",
R_X86_64_PC32 => "R_X86_64_PC32",
R_X86_64_GOT32 => "R_X86_64_GOT32",
R_X86_64_PLT32 => "R_X86_64_PLT32",
R_X86_64_COPY => "R_X86_64_COPY",
R_X86_64_GLOB_DAT => "R_X86_64_GLOB_DAT",
R_X86_64_JUMP_SLOT => "R_X86_64_JUMP_SLOT",
R_X86_64_RELATIVE => "R_X86_64_RELATIVE",
R_X86_64_GOTPCREL => "R_X86_64_GOTPCREL",
R_X86_64_32 => "R_X86_64_32",
R_X86_64_32S => "R_X86_64_32S",
R_X86_64_16 => "R_X86_64_16",
R_X86_64_PC16 => "R_X86_64_PC16",
R_X86_64_8 => "R_X86_64_8",
R_X86_64_PC8 => "R_X86_64_PC8",
R_X86_64_GOTOFF64 => "R_X86_64_GOTOFF64",
R_X86_64_GOTPC32 => "R_X86_64_GOTPC32",
R_X86_64_SIZE32 => "R_X86_64_SIZE32",
R_X86_64_SIZE64 => "R_X86_64_SIZE64",
R_X86_64_PC64 => "R_X86_64_PC64",
_ => "R_X86_64_UNKNOWN",
}
}
pub fn is_pcrel(rel_type: u32) -> bool {
matches!(
rel_type,
R_X86_64_PC32
| R_X86_64_PC8
| R_X86_64_PC16
| R_X86_64_PC64
| R_X86_64_GOTPCREL
| R_X86_64_PLT32
| R_X86_64_GOTPC32
| R_X86_64_GOTPC32_TLSDESC
)
}
pub fn reloc_size(rel_type: u32) -> u8 {
match rel_type {
R_X86_64_NONE => 0,
R_X86_64_64 | R_X86_64_PC64 | R_X86_64_SIZE64 | R_X86_64_GOTOFF64 | R_X86_64_COPY
| R_X86_64_GLOB_DAT | R_X86_64_JUMP_SLOT | R_X86_64_RELATIVE => 8,
R_X86_64_PC32
| R_X86_64_32
| R_X86_64_32S
| R_X86_64_GOT32
| R_X86_64_PLT32
| R_X86_64_GOTPCREL
| R_X86_64_GOTPC32
| R_X86_64_SIZE32
| R_X86_64_GOTPC32_TLSDESC
| R_X86_64_TLSDESC => 4,
R_X86_64_16 | R_X86_64_PC16 => 2,
R_X86_64_8 | R_X86_64_PC8 => 1,
_ => 4,
}
}
}
#[derive(Debug)]
pub struct X86DwarfLineEmitter {
pub line_program: Vec<u8>,
pub files: Vec<X86DebugFileEntry>,
pub directories: Vec<String>,
current_address: u64,
current_file: u32,
current_line: u32,
current_column: u32,
is_stmt: bool,
basic_block: bool,
prologue_end: bool,
epilogue_begin: bool,
isa: u32,
discriminator: u32,
min_inst_length: u8,
max_ops_per_inst: u8,
default_is_stmt: bool,
line_base: i8,
line_range: u8,
opcode_base: u8,
}
impl X86DwarfLineEmitter {
pub fn new() -> Self {
Self {
line_program: Vec::new(),
files: Vec::new(),
directories: Vec::new(),
current_address: 0,
current_file: 1,
current_line: 1,
current_column: 0,
is_stmt: false,
basic_block: false,
prologue_end: false,
epilogue_begin: false,
isa: 0,
discriminator: 0,
min_inst_length: 1,
max_ops_per_inst: 1,
default_is_stmt: true,
line_base: -5,
line_range: 14,
opcode_base: 13,
}
}
pub fn add_directory(&mut self, dir: impl Into<String>) -> u32 {
let idx = self.directories.len() as u32;
self.directories.push(dir.into());
idx
}
pub fn add_file(&mut self, name: impl Into<String>, directory_idx: u32) -> u32 {
let file_id = self.files.len() as u32 + 1;
self.files.push(X86DebugFileEntry {
file_id,
directory_id: directory_idx,
name: name.into(),
directory: String::new(),
md5_checksum: None,
});
file_id
}
pub fn set_address(&mut self, address: u64) {
if address != self.current_address {
self.emit_set_address(address);
self.current_address = address;
}
}
pub fn emit_row(&mut self, address: u64, file_id: u32, line: u32, column: u32, is_stmt: bool) {
if file_id != self.current_file {
self.emit_set_file(file_id);
self.current_file = file_id;
}
if address != self.current_address {
self.emit_advance_pc(address - self.current_address);
self.current_address = address;
}
let line_delta = line as i64 - self.current_line as i64;
let op_offset = (line_delta - self.line_base as i64) as u8;
if op_offset < self.line_range {
let opcode = self.opcode_base + op_offset;
self.line_program.push(opcode);
} else {
self.emit_advance_line(line_delta);
self.line_program.push(DW_LNS_copy);
}
self.current_line = line;
self.current_column = column;
self.is_stmt = is_stmt;
}
pub fn build_prologue(&mut self) -> Vec<u8> {
let mut header = Vec::new();
header.extend_from_slice(&0u32.to_le_bytes());
header.extend_from_slice(&2u16.to_le_bytes());
let prologue_len_pos = header.len();
header.extend_from_slice(&0u32.to_le_bytes());
header.push(self.min_inst_length);
header.push(self.max_ops_per_inst);
header.push(self.default_is_stmt as u8);
header.push(self.line_base as u8);
header.push(self.line_range);
header.push(self.opcode_base);
let std_opcode_lengths: [u8; 12] = [0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1];
for &len in &std_opcode_lengths[..(self.opcode_base as usize - 1)] {
header.push(len);
}
for dir in &self.directories {
header.extend_from_slice(dir.as_bytes());
header.push(0);
}
header.push(0);
for file in &self.files {
header.extend_from_slice(file.name.as_bytes());
header.push(0);
header.append(&mut write_uleb128(file.directory_id as u64));
header.push(0);
header.push(0);
}
header.push(0);
let prologue_len = (header.len() - prologue_len_pos - 4) as u32;
header[prologue_len_pos..prologue_len_pos + 4].copy_from_slice(&prologue_len.to_le_bytes());
header
}
pub fn finish(&mut self) -> Vec<u8> {
let mut result = Vec::new();
let prologue = self.build_prologue();
let unit_length = (prologue.len() + self.line_program.len() + 4) as u32;
result.extend_from_slice(&unit_length.to_le_bytes());
result.extend_from_slice(&prologue[4..]); result.extend_from_slice(&self.line_program);
result.push(0); result.push(1); result.push(DW_LNE_end_sequence);
result
}
fn emit_set_address(&mut self, address: u64) {
self.line_program.push(0); self.line_program.push(9); self.line_program.push(DW_LNE_set_address);
self.line_program.extend_from_slice(&address.to_le_bytes());
}
fn emit_set_file(&mut self, file_id: u32) {
self.line_program.push(DW_LNS_set_file);
self.line_program.append(&mut write_uleb128(file_id as u64));
}
fn emit_advance_pc(&mut self, delta: u64) {
self.line_program.push(DW_LNS_advance_pc);
self.line_program
.append(&mut write_uleb128(delta / self.min_inst_length as u64));
}
fn emit_advance_line(&mut self, delta: i64) {
self.line_program.push(DW_LNS_advance_line);
self.line_program.append(&mut write_sleb128(delta));
}
pub fn emit_set_column(&mut self, column: u32) {
self.line_program.push(DW_LNS_set_column);
self.line_program.append(&mut write_uleb128(column as u64));
}
pub fn emit_set_basic_block(&mut self) {
self.line_program.push(DW_LNS_set_basic_block);
}
pub fn emit_set_prologue_end(&mut self) {
self.line_program.push(DW_LNS_set_prologue_end);
}
pub fn emit_set_epilogue_begin(&mut self) {
self.line_program.push(DW_LNS_set_epilogue_begin);
}
}
const DW_LNS_copy: u8 = 1;
const DW_LNS_advance_pc: u8 = 2;
const DW_LNS_advance_line: u8 = 3;
const DW_LNS_set_file: u8 = 4;
const DW_LNS_set_column: u8 = 5;
const DW_LNS_negate_stmt: u8 = 6;
const DW_LNS_set_basic_block: u8 = 7;
const DW_LNS_const_add_pc: u8 = 8;
const DW_LNS_fixed_advance_pc: u8 = 9;
const DW_LNS_set_prologue_end: u8 = 10;
const DW_LNS_set_epilogue_begin: u8 = 11;
const DW_LNS_set_isa: u8 = 12;
const DW_LNE_end_sequence: u8 = 1;
const DW_LNE_set_address: u8 = 2;
const DW_LNE_define_file: u8 = 3;
const DW_LNE_set_discriminator: u8 = 4;
pub fn write_uleb128(value: u64) -> Vec<u8> {
let mut result = Vec::new();
let mut v = value;
loop {
let mut byte = (v & 0x7F) as u8;
v >>= 7;
if v != 0 {
byte |= 0x80;
}
result.push(byte);
if v == 0 {
break;
}
}
result
}
pub fn write_sleb128(value: i64) -> Vec<u8> {
let mut result = Vec::new();
let mut v = value;
loop {
let mut byte = (v & 0x7F) as u8;
v >>= 7;
if (v == 0 && (byte & 0x40) == 0) || (v == -1 && (byte & 0x40) != 0) {
result.push(byte);
break;
}
byte |= 0x80;
result.push(byte);
}
result
}
impl Default for X86DwarfLineEmitter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct X86EHFrameEmitter {
pub frame_data: Vec<u8>,
pub cfi_buf: Vec<u8>,
pc: u64,
cie_count: u32,
fde_count: u32,
}
impl X86EHFrameEmitter {
pub fn new() -> Self {
Self {
frame_data: Vec::new(),
cfi_buf: Vec::new(),
pc: 0,
cie_count: 0,
fde_count: 0,
}
}
pub fn emit_cie(
&mut self,
version: u8,
augmentation: &str,
code_alignment_factor: u32,
data_alignment_factor: i32,
return_address_register: u8,
) {
let cie_start = self.frame_data.len();
self.frame_data.extend_from_slice(&0u32.to_le_bytes());
self.frame_data.extend_from_slice(&0u32.to_le_bytes());
self.frame_data.push(version);
self.frame_data.extend_from_slice(augmentation.as_bytes());
self.frame_data.push(0);
self.frame_data
.append(&mut write_uleb128(code_alignment_factor as u64));
self.frame_data
.append(&mut write_sleb128(data_alignment_factor as i64));
self.frame_data.push(return_address_register);
if augmentation.contains('z') {
self.frame_data.push(0); }
self.frame_data.push(0x0C); self.frame_data.push(7); self.frame_data.push(8); self.frame_data.push(0x80 | 16); self.frame_data.push(1);
let cie_len = (self.frame_data.len() - cie_start - 4) as u32;
self.frame_data[cie_start..cie_start + 4].copy_from_slice(&cie_len.to_le_bytes());
self.cie_count += 1;
}
pub fn emit_fde(&mut self, pc_begin: u64, pc_range: u64, cie_pointer: u32) {
let fde_start = self.frame_data.len();
self.frame_data.extend_from_slice(&0u32.to_le_bytes());
self.frame_data
.extend_from_slice(&cie_pointer.to_le_bytes());
self.frame_data.extend_from_slice(&pc_begin.to_le_bytes());
self.frame_data.extend_from_slice(&pc_range.to_le_bytes());
self.frame_data.extend_from_slice(&self.cfi_buf);
self.cfi_buf.clear();
let fde_len = (self.frame_data.len() - fde_start - 4) as u32;
self.frame_data[fde_start..fde_start + 4].copy_from_slice(&fde_len.to_le_bytes());
self.fde_count += 1;
}
pub fn add_cfi(&mut self, cfi: &X86CFIInstruction) {
match cfi {
X86CFIInstruction::AdvanceLoc(delta) => {
let d = *delta;
if d < 0x40 {
self.cfi_buf.push(0x40 | (d as u8));
} else {
self.cfi_buf.push(0); self.cfi_buf.append(&mut write_uleb128(d));
}
self.pc += d;
}
X86CFIInstruction::DefCfa { register, offset } => {
self.cfi_buf.push(0x0C); self.cfi_buf.append(&mut write_uleb128(*register as u64));
self.cfi_buf.append(&mut write_uleb128(*offset as u64));
}
X86CFIInstruction::Offset { register, offset } => {
let factored = (*offset / -8) as u8; if *register < 0x40 {
self.cfi_buf.push(0x80 | (*register as u8));
self.cfi_buf.append(&mut write_uleb128(factored as u64));
} else {
self.cfi_buf.push(0x80);
self.cfi_buf.append(&mut write_uleb128(*register as u64));
self.cfi_buf.append(&mut write_uleb128(factored as u64));
}
}
X86CFIInstruction::Restore(r) => {
self.cfi_buf.push(0xC0 | (*r as u8));
}
X86CFIInstruction::DefCfaRegister(r) => {
self.cfi_buf.push(0x0D); self.cfi_buf.append(&mut write_uleb128(*r as u64));
}
X86CFIInstruction::DefCfaOffset(o) => {
self.cfi_buf.push(0x0E); self.cfi_buf.append(&mut write_uleb128(*o as u64));
}
X86CFIInstruction::RememberState => {
self.cfi_buf.push(0x0A); }
X86CFIInstruction::RestoreState => {
self.cfi_buf.push(0x0B); }
X86CFIInstruction::Register { reg1, reg2 } => {
self.cfi_buf.push(0x09); self.cfi_buf.append(&mut write_uleb128(*reg1 as u64));
self.cfi_buf.append(&mut write_uleb128(*reg2 as u64));
}
_ => {
}
}
}
pub fn finish(&self) -> &[u8] {
&self.frame_data
}
}
impl Default for X86EHFrameEmitter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TLSModel {
GeneralDynamic,
LocalDynamic,
InitialExec,
LocalExec,
}
#[derive(Debug, Clone)]
pub struct X86TLSDescriptor {
pub model: X86TLSModel,
pub symbol: String,
pub offset: u64,
pub is_local: bool,
pub is_initialized: bool,
pub size: u64,
pub alignment: u64,
}
impl X86TLSDescriptor {
pub fn new(symbol: impl Into<String>, model: X86TLSModel) -> Self {
Self {
model,
symbol: symbol.into(),
offset: 0,
is_local: false,
is_initialized: false,
size: 0,
alignment: 1,
}
}
pub fn select_model(code_model: X86CodeModel, is_local: bool, is_pic: bool) -> X86TLSModel {
if is_local {
match code_model {
X86CodeModel::Small | X86CodeModel::Kernel => X86TLSModel::LocalExec,
_ => {
if is_pic {
X86TLSModel::LocalDynamic
} else {
X86TLSModel::LocalExec
}
}
}
} else {
if is_pic {
X86TLSModel::GeneralDynamic
} else {
match code_model {
X86CodeModel::Small | X86CodeModel::Kernel => X86TLSModel::InitialExec,
_ => X86TLSModel::GeneralDynamic,
}
}
}
}
pub fn fixup_kind(&self) -> X86FixupKind {
match self.model {
X86TLSModel::GeneralDynamic => X86FixupKind::TlsGd,
X86TLSModel::LocalDynamic => X86FixupKind::TlsLd,
X86TLSModel::InitialExec => X86FixupKind::GotTpOff,
X86TLSModel::LocalExec => X86FixupKind::TpOff32,
}
}
pub fn emit_asm_access(&self, printer: &mut X86MCAsmPrinter, dest_reg: u16) {
match self.model {
X86TLSModel::GeneralDynamic => {
printer.emit_comment("TLS general dynamic access");
}
X86TLSModel::LocalDynamic => {
printer.emit_comment("TLS local dynamic access");
}
X86TLSModel::InitialExec => {
printer.emit_comment("TLS initial exec access");
}
X86TLSModel::LocalExec => {
printer.emit_comment("TLS local exec access");
}
}
}
}
#[derive(Debug)]
pub struct X86ELFProgramHeaderBuilder {
pub headers: Vec<X86ProgramHeader>,
pub entry: u64,
pub base_address: u64,
pub page_size: u64,
}
#[derive(Debug, Clone)]
pub struct X86ProgramHeader {
pub segment_type: u32,
pub flags: u32,
pub offset: u64,
pub vaddr: u64,
pub paddr: u64,
pub filesz: u64,
pub memsz: u64,
pub align: u64,
}
impl X86ProgramHeader {
pub fn load(flags: u32, offset: u64, vaddr: u64, filesz: u64, memsz: u64, align: u64) -> Self {
Self {
segment_type: 1, flags,
offset,
vaddr,
paddr: vaddr,
filesz,
memsz,
align,
}
}
pub fn phdr(offset: u64, vaddr: u64, filesz: u64, memsz: u64) -> Self {
Self {
segment_type: 6, flags: 4, offset,
vaddr,
paddr: vaddr,
filesz,
memsz,
align: 8,
}
}
pub fn gnu_stack(exec: bool) -> Self {
Self {
segment_type: 0x6474E551, flags: if exec { 6 } else { 6 }, offset: 0,
vaddr: 0,
paddr: 0,
filesz: 0,
memsz: 0,
align: 16,
}
}
pub fn gnu_relro(vaddr: u64, memsz: u64) -> Self {
Self {
segment_type: 0x6474E552, flags: 4, offset: vaddr,
vaddr,
paddr: vaddr,
filesz: memsz,
memsz,
align: 1,
}
}
pub fn note(offset: u64, vaddr: u64, filesz: u64) -> Self {
Self {
segment_type: 4, flags: 4, offset,
vaddr,
paddr: vaddr,
filesz,
memsz: filesz,
align: 4,
}
}
pub fn dynamic(offset: u64, vaddr: u64, filesz: u64) -> Self {
Self {
segment_type: 2, flags: 6, offset,
vaddr,
paddr: vaddr,
filesz,
memsz: filesz,
align: 8,
}
}
pub fn interp(offset: u64, vaddr: u64, filesz: u64) -> Self {
Self {
segment_type: 3, flags: 4, offset,
vaddr,
paddr: vaddr,
filesz,
memsz: filesz,
align: 1,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = vec![0u8; 56];
buf[0..4].copy_from_slice(&self.segment_type.to_le_bytes());
buf[4..8].copy_from_slice(&self.flags.to_le_bytes());
buf[8..16].copy_from_slice(&self.offset.to_le_bytes());
buf[16..24].copy_from_slice(&self.vaddr.to_le_bytes());
buf[24..32].copy_from_slice(&self.paddr.to_le_bytes());
buf[32..40].copy_from_slice(&self.filesz.to_le_bytes());
buf[40..48].copy_from_slice(&self.memsz.to_le_bytes());
buf[48..56].copy_from_slice(&self.align.to_le_bytes());
buf
}
}
impl X86ELFProgramHeaderBuilder {
pub fn new() -> Self {
Self {
headers: Vec::new(),
entry: 0,
base_address: 0x400000,
page_size: 4096,
}
}
pub fn add(&mut self, header: X86ProgramHeader) {
self.headers.push(header);
}
pub fn build_standard_executable(
&mut self,
text_offset: u64,
text_size: u64,
data_offset: u64,
data_size: u64,
) {
let text_vaddr = self.base_address + text_offset;
let data_vaddr = self.base_address + data_offset;
let page = self.page_size;
let text_vaddr_aligned = text_vaddr & !(page - 1);
let text_mem_end = ((text_vaddr + text_size + page - 1) / page) * page;
let data_vaddr_aligned = data_vaddr & !(page - 1);
let data_mem_end = ((data_vaddr + data_size + page - 1) / page) * page;
self.add(X86ProgramHeader::load(
5, text_vaddr_aligned - self.base_address,
text_vaddr_aligned,
text_size,
text_mem_end - text_vaddr_aligned,
page,
));
self.add(X86ProgramHeader::load(
6, data_vaddr_aligned - self.base_address,
data_vaddr_aligned,
data_size,
data_mem_end - data_vaddr_aligned,
page,
));
self.add(X86ProgramHeader::gnu_stack(false));
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::new();
for h in &self.headers {
buf.extend_from_slice(&h.to_bytes());
}
buf
}
pub fn count(&self) -> usize {
self.headers.len()
}
}
impl Default for X86ELFProgramHeaderBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod extended_tests {
use super::*;
#[test]
fn test_reloc_apply_abs64() {
let mut buf = vec![0u8; 16];
X86RelocationHandler::apply(&mut buf, 4, R_X86_64_64, 0x1000, 0, 0).unwrap();
let val = u64::from_le_bytes([
buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11],
]);
assert_eq!(val, 0x1000);
}
#[test]
fn test_reloc_apply_pc32() {
let mut buf = vec![0u8; 16];
X86RelocationHandler::apply(&mut buf, 4, R_X86_64_PC32, 0x4200, -4, 0x4000).unwrap();
let val = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]);
assert_eq!(val, 0x1FC);
}
#[test]
fn test_reloc_apply_32s() {
let mut buf = vec![0u8; 8];
X86RelocationHandler::apply(&mut buf, 0, R_X86_64_32S, 0x7FFFFFFF, 0, 0).unwrap();
let val = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
assert_eq!(val, 0x7FFFFFFF);
}
#[test]
fn test_reloc_apply_pc8() {
let mut buf = vec![0u8; 4];
X86RelocationHandler::apply(&mut buf, 1, R_X86_64_PC8, 0x100, 0, 0x100).unwrap();
assert_eq!(buf[1], 0);
}
#[test]
fn test_reloc_apply_none() {
let mut buf = vec![1u8, 2, 3, 4];
X86RelocationHandler::apply(&mut buf, 0, R_X86_64_NONE, 0, 0, 0).unwrap();
assert_eq!(buf, vec![1, 2, 3, 4]);
}
#[test]
fn test_reloc_name() {
assert_eq!(X86RelocationHandler::reloc_name(R_X86_64_64), "R_X86_64_64");
assert_eq!(
X86RelocationHandler::reloc_name(R_X86_64_PC32),
"R_X86_64_PC32"
);
assert_eq!(
X86RelocationHandler::reloc_name(R_X86_64_NONE),
"R_X86_64_NONE"
);
assert_eq!(X86RelocationHandler::reloc_name(999), "R_X86_64_UNKNOWN");
}
#[test]
fn test_reloc_is_pcrel() {
assert!(X86RelocationHandler::is_pcrel(R_X86_64_PC32));
assert!(X86RelocationHandler::is_pcrel(R_X86_64_PC8));
assert!(X86RelocationHandler::is_pcrel(R_X86_64_PLT32));
assert!(!X86RelocationHandler::is_pcrel(R_X86_64_64));
assert!(!X86RelocationHandler::is_pcrel(R_X86_64_32));
}
#[test]
fn test_reloc_size() {
assert_eq!(X86RelocationHandler::reloc_size(R_X86_64_64), 8);
assert_eq!(X86RelocationHandler::reloc_size(R_X86_64_PC32), 4);
assert_eq!(X86RelocationHandler::reloc_size(R_X86_64_8), 1);
assert_eq!(X86RelocationHandler::reloc_size(R_X86_64_16), 2);
assert_eq!(X86RelocationHandler::reloc_size(R_X86_64_NONE), 0);
}
#[test]
fn test_dwarf_line_emitter_new() {
let emitter = X86DwarfLineEmitter::new();
assert!(emitter.line_program.is_empty());
assert!(emitter.files.is_empty());
assert!(emitter.directories.is_empty());
}
#[test]
fn test_dwarf_line_add_directory() {
let mut emitter = X86DwarfLineEmitter::new();
let idx = emitter.add_directory("/home/user/src");
assert_eq!(idx, 0);
assert_eq!(emitter.directories.len(), 1);
}
#[test]
fn test_dwarf_line_add_file() {
let mut emitter = X86DwarfLineEmitter::new();
let dir_idx = emitter.add_directory("/src");
let file_id = emitter.add_file("test.c", dir_idx);
assert_eq!(file_id, 1);
assert_eq!(emitter.files.len(), 1);
}
#[test]
fn test_dwarf_line_prologue() {
let mut emitter = X86DwarfLineEmitter::new();
emitter.add_directory("/src");
emitter.add_file("test.c", 0);
let prologue = emitter.build_prologue();
assert!(prologue.len() > 20);
assert_eq!(u16::from_le_bytes([prologue[4], prologue[5]]), 2);
}
#[test]
fn test_dwarf_line_finish() {
let mut emitter = X86DwarfLineEmitter::new();
emitter.add_directory("/src");
emitter.add_file("test.c", 0);
emitter.set_address(0x1000);
emitter.emit_row(0x1000, 1, 5, 0, true);
emitter.set_address(0x1010);
emitter.emit_row(0x1010, 1, 10, 0, true);
let result = emitter.finish();
assert!(result.len() > 30);
}
#[test]
fn test_dwarf_line_set_column() {
let mut emitter = X86DwarfLineEmitter::new();
emitter.emit_set_column(8);
assert_eq!(emitter.line_program[0], DW_LNS_set_column);
}
#[test]
fn test_dwarf_line_set_basic_block() {
let mut emitter = X86DwarfLineEmitter::new();
emitter.emit_set_basic_block();
assert_eq!(emitter.line_program[0], DW_LNS_set_basic_block);
}
#[test]
fn test_dwarf_line_set_prologue_end() {
let mut emitter = X86DwarfLineEmitter::new();
emitter.emit_set_prologue_end();
assert_eq!(emitter.line_program[0], DW_LNS_set_prologue_end);
}
#[test]
fn test_dwarf_line_set_epilogue_begin() {
let mut emitter = X86DwarfLineEmitter::new();
emitter.emit_set_epilogue_begin();
assert_eq!(emitter.line_program[0], DW_LNS_set_epilogue_begin);
}
#[test]
fn test_uleb128_zero() {
let encoded = write_uleb128(0);
assert_eq!(encoded, vec![0]);
}
#[test]
fn test_uleb128_small() {
let encoded = write_uleb128(127);
assert_eq!(encoded, vec![127]);
}
#[test]
fn test_uleb128_medium() {
let encoded = write_uleb128(128);
assert_eq!(encoded, vec![0x80, 0x01]);
}
#[test]
fn test_uleb128_large() {
let encoded = write_uleb128(0xFFFFFFFF);
assert!(encoded.len() >= 5);
}
#[test]
fn test_sleb128_zero() {
let encoded = write_sleb128(0);
assert_eq!(encoded, vec![0]);
}
#[test]
fn test_sleb128_positive() {
let encoded = write_sleb128(42);
assert_eq!(encoded, vec![42]);
}
#[test]
fn test_sleb128_negative() {
let encoded = write_sleb128(-1);
assert_eq!(encoded, vec![0x7F]);
}
#[test]
fn test_sleb128_negative_small() {
let encoded = write_sleb128(-128);
assert_eq!(encoded, vec![0x80, 0x7F]);
}
#[test]
fn test_uleb128_sleb128_roundtrip_small() {
for val in 0i64..128i64 {
let encoded = write_uleb128(val as u64);
assert!(!encoded.is_empty());
}
}
#[test]
fn test_eh_frame_emitter_new() {
let emitter = X86EHFrameEmitter::new();
assert!(emitter.frame_data.is_empty());
assert_eq!(emitter.cie_count, 0);
assert_eq!(emitter.fde_count, 0);
}
#[test]
fn test_eh_frame_emit_cie() {
let mut emitter = X86EHFrameEmitter::new();
emitter.emit_cie(1, "zR", 1, -8, 16);
assert_eq!(emitter.cie_count, 1);
assert!(!emitter.frame_data.is_empty());
let cie_id = u32::from_le_bytes([
emitter.frame_data[4],
emitter.frame_data[5],
emitter.frame_data[6],
emitter.frame_data[7],
]);
assert_eq!(cie_id, 0);
}
#[test]
fn test_eh_frame_emit_fde() {
let mut emitter = X86EHFrameEmitter::new();
emitter.emit_cie(1, "", 1, -8, 16);
emitter.emit_fde(0x4000, 0x80, 0);
assert_eq!(emitter.fde_count, 1);
}
#[test]
fn test_eh_frame_add_cfi_def_cfa() {
let mut emitter = X86EHFrameEmitter::new();
emitter.add_cfi(&X86CFIInstruction::DefCfa {
register: RSP,
offset: 8,
});
assert_eq!(emitter.cfi_buf[0], 0x0C); }
#[test]
fn test_eh_frame_add_cfi_offset() {
let mut emitter = X86EHFrameEmitter::new();
emitter.add_cfi(&X86CFIInstruction::Offset {
register: 16, offset: -8,
});
assert!(!emitter.cfi_buf.is_empty());
assert_eq!(emitter.cfi_buf[0], 0x80 | 16);
}
#[test]
fn test_eh_frame_add_cfi_advance_loc() {
let mut emitter = X86EHFrameEmitter::new();
emitter.add_cfi(&X86CFIInstruction::AdvanceLoc(10));
assert_eq!(emitter.cfi_buf[0], 0x40 | 10);
}
#[test]
fn test_eh_frame_finish() {
let mut emitter = X86EHFrameEmitter::new();
emitter.emit_cie(1, "", 1, -8, 16);
emitter.add_cfi(&X86CFIInstruction::DefCfaOffset(16));
emitter.emit_fde(0x4000, 0x100, 0);
let result = emitter.finish();
assert!(!result.is_empty());
}
#[test]
fn test_tls_descriptor_new() {
let tls = X86TLSDescriptor::new("my_tls_var", X86TLSModel::GeneralDynamic);
assert_eq!(tls.symbol, "my_tls_var");
assert_eq!(tls.model, X86TLSModel::GeneralDynamic);
}
#[test]
fn test_tls_model_selection_local_pic() {
let model = X86TLSDescriptor::select_model(X86CodeModel::Small, true, true);
assert_eq!(model, X86TLSModel::LocalExec);
}
#[test]
fn test_tls_model_selection_global_pic() {
let model = X86TLSDescriptor::select_model(X86CodeModel::Small, false, true);
assert_eq!(model, X86TLSModel::GeneralDynamic);
}
#[test]
fn test_tls_model_selection_global_no_pic() {
let model = X86TLSDescriptor::select_model(X86CodeModel::Small, false, false);
assert_eq!(model, X86TLSModel::InitialExec);
}
#[test]
fn test_tls_fixup_kind() {
let gd = X86TLSDescriptor::new("x", X86TLSModel::GeneralDynamic);
assert_eq!(gd.fixup_kind(), X86FixupKind::TlsGd);
let ld = X86TLSDescriptor::new("x", X86TLSModel::LocalDynamic);
assert_eq!(ld.fixup_kind(), X86FixupKind::TlsLd);
let ie = X86TLSDescriptor::new("x", X86TLSModel::InitialExec);
assert_eq!(ie.fixup_kind(), X86FixupKind::GotTpOff);
let le = X86TLSDescriptor::new("x", X86TLSModel::LocalExec);
assert_eq!(le.fixup_kind(), X86FixupKind::TpOff32);
}
#[test]
fn test_tls_asm_access_all_models() {
let mut printer = X86MCAsmPrinter::default();
for model in &[
X86TLSModel::GeneralDynamic,
X86TLSModel::LocalDynamic,
X86TLSModel::InitialExec,
X86TLSModel::LocalExec,
] {
let tls = X86TLSDescriptor::new("v", *model);
tls.emit_asm_access(&mut printer, RAX);
}
let out = printer.finish();
assert!(out.contains("TLS"));
}
#[test]
fn test_program_header_load() {
let phdr = X86ProgramHeader::load(5, 0, 0x400000, 0x1000, 0x1000, 0x1000);
assert_eq!(phdr.segment_type, 1); assert_eq!(phdr.flags, 5);
assert_eq!(phdr.vaddr, 0x400000);
}
#[test]
fn test_program_header_gnu_stack() {
let phdr = X86ProgramHeader::gnu_stack(false);
assert_eq!(phdr.segment_type, 0x6474E551);
}
#[test]
fn test_program_header_gnu_relro() {
let phdr = X86ProgramHeader::gnu_relro(0x600000, 0x1000);
assert_eq!(phdr.segment_type, 0x6474E552);
assert_eq!(phdr.memsz, 0x1000);
}
#[test]
fn test_program_header_note() {
let phdr = X86ProgramHeader::note(0x200, 0x400200, 0x40);
assert_eq!(phdr.segment_type, 4);
assert_eq!(phdr.filesz, 0x40);
}
#[test]
fn test_program_header_dynamic() {
let phdr = X86ProgramHeader::dynamic(0x2000, 0x600000, 0x200);
assert_eq!(phdr.segment_type, 2);
assert_eq!(phdr.flags, 6); }
#[test]
fn test_program_header_interp() {
let phdr = X86ProgramHeader::interp(0x200, 0x400200, 28);
assert_eq!(phdr.segment_type, 3);
}
#[test]
fn test_program_header_phdr() {
let phdr = X86ProgramHeader::phdr(0x40, 0x400040, 0x1C0, 0x1C0);
assert_eq!(phdr.segment_type, 6);
}
#[test]
fn test_program_header_to_bytes() {
let phdr = X86ProgramHeader::load(5, 0, 0x400000, 0x1000, 0x2000, 0x1000);
let bytes = phdr.to_bytes();
assert_eq!(bytes.len(), 56);
let p_type = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
assert_eq!(p_type, 1);
let vaddr = u64::from_le_bytes([
bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23],
]);
assert_eq!(vaddr, 0x400000);
}
#[test]
fn test_program_header_builder_new() {
let builder = X86ELFProgramHeaderBuilder::new();
assert!(builder.headers.is_empty());
assert_eq!(builder.count(), 0);
}
#[test]
fn test_program_header_builder_add() {
let mut builder = X86ELFProgramHeaderBuilder::new();
builder.add(X86ProgramHeader::load(
5, 0, 0x400000, 0x1000, 0x1000, 0x1000,
));
assert_eq!(builder.count(), 1);
}
#[test]
fn test_program_header_builder_standard_exe() {
let mut builder = X86ELFProgramHeaderBuilder::new();
builder.build_standard_executable(0, 0x1000, 0x1000, 0x500);
assert_eq!(builder.count(), 3);
}
#[test]
fn test_program_header_builder_to_bytes() {
let mut builder = X86ELFProgramHeaderBuilder::new();
builder.add(X86ProgramHeader::gnu_stack(false));
let bytes = builder.to_bytes();
assert_eq!(bytes.len(), 56);
}
#[test]
fn test_uleb128_boundary_values() {
let test_values = vec![0u64, 1, 127, 128, 255, 16383, 16384, 0x1FFFFF, 0x200000];
for val in test_values {
let encoded = write_uleb128(val);
assert!(!encoded.is_empty(), "Failed for {}", val);
assert!(
encoded.last().unwrap() & 0x80 == 0,
"Last byte should not have bit 7 set for {}",
val
);
}
}
#[test]
fn test_sleb128_boundary_values() {
let test_values = vec![0i64, 1, -1, 63, -64, 64, -65, 8191, -8192, 8192, -8193];
for val in test_values {
let encoded = write_sleb128(val);
assert!(!encoded.is_empty(), "Failed for {}", val);
assert!(
encoded.last().unwrap() & 0x80 == 0,
"Last byte should not have bit 7 set for {}",
val
);
}
}
#[test]
fn test_reloc_apply_rex64_relative() {
let mut buf = vec![0u8; 16];
X86RelocationHandler::apply(&mut buf, 0, R_X86_64_RELATIVE, 0, 0x1000, 0x4000).unwrap();
let val = u64::from_le_bytes([
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
]);
assert_eq!(val, 0x5000);
}
#[test]
fn test_reloc_apply_gotpcrel_initial() {
let mut buf = vec![0u8; 8];
X86RelocationHandler::apply(&mut buf, 0, R_X86_64_GOTPCREL, 0, -8, 0).unwrap();
let val = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
assert_eq!(val as i32, -8);
}
#[test]
fn test_reloc_apply_plt32() {
let mut buf = vec![0u8; 8];
X86RelocationHandler::apply(&mut buf, 0, R_X86_64_PLT32, 0, -4, 0).unwrap();
let val = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
assert_eq!(val as i32, -4);
}
#[test]
fn test_program_header_default_builder() {
let builder = X86ELFProgramHeaderBuilder::default();
assert_eq!(builder.base_address, 0x400000);
assert_eq!(builder.page_size, 4096);
}
#[test]
fn test_dwarf_line_default() {
let emitter = X86DwarfLineEmitter::default();
assert!(emitter.line_program.is_empty());
}
#[test]
fn test_eh_frame_default() {
let emitter = X86EHFrameEmitter::default();
assert!(emitter.frame_data.is_empty());
}
#[test]
fn test_uleb128_max_u32() {
let encoded = write_uleb128(0xFFFFFFFFu64);
assert!(encoded.len() <= 7);
assert!(encoded.last().unwrap() & 0x80 == 0);
}
#[test]
fn test_sleb128_negative_max() {
let encoded = write_sleb128(i64::MIN);
assert!(encoded.len() <= 10);
}
#[test]
fn test_sleb128_positive_max() {
let encoded = write_sleb128(i64::MAX);
assert!(encoded.len() <= 10);
}
#[test]
fn test_dwarf_line_multiple_files() {
let mut emitter = X86DwarfLineEmitter::new();
let d = emitter.add_directory("/work");
emitter.add_file("a.c", d);
emitter.add_file("b.c", d);
emitter.add_file("c.h", d);
assert_eq!(emitter.files.len(), 3);
let result = emitter.finish();
assert!(result.len() > 40);
}
#[test]
fn test_eh_frame_multiple_fdes() {
let mut emitter = X86EHFrameEmitter::new();
emitter.emit_cie(1, "", 1, -8, 16);
emitter.emit_fde(0x4000, 0x50, 0);
emitter.emit_fde(0x4100, 0x80, 0);
assert_eq!(emitter.fde_count, 2);
}
#[test]
fn test_program_header_all_types() {
let types = vec![
X86ProgramHeader::load(5, 0, 0x400000, 0x1000, 0x1000, 0x1000),
X86ProgramHeader::phdr(0x40, 0x400040, 56, 56),
X86ProgramHeader::interp(0x200, 0x400200, 28),
X86ProgramHeader::dynamic(0x2000, 0x600000, 0x200),
X86ProgramHeader::note(0x300, 0x400300, 64),
X86ProgramHeader::gnu_stack(false),
X86ProgramHeader::gnu_relro(0x600000, 0x1000),
];
for phdr in &types {
let bytes = phdr.to_bytes();
assert_eq!(bytes.len(), 56);
}
}
#[test]
fn test_full_pipeline_create_to_write() {
let (_ctx, mut _backend, _emitter, writer, _printer, _streamer) = create_x86_mc_pipeline(
"x86_64-unknown-linux-gnu",
"skylake",
&["+sse2", "+avx2"],
ObjectFormat::ELF,
true,
true,
);
let obj = writer.write();
assert!(!obj.is_empty());
assert_eq!(&obj[0..4], &[0x7f, b'E', b'L', b'F']);
}
#[test]
fn test_all_section_types_roundtrip() {
let stypes = [
X86SectionType::Text,
X86SectionType::Data,
X86SectionType::Rodata,
X86SectionType::Bss,
X86SectionType::InitArray,
X86SectionType::FiniArray,
X86SectionType::DebugInfo,
X86SectionType::EhFrame,
X86SectionType::TlsData,
X86SectionType::TlsBss,
X86SectionType::Note,
X86SectionType::Comment,
];
for st in &stypes {
let mut writer = X86MCObjectWriter::default();
let section = X86ObjectSection {
name: "test".into(),
data: vec![],
flags: X86SectionFlags::default(),
section_type: st.clone(),
alignment: 1,
relocations: vec![],
};
writer.add_section(section);
let result = writer.write();
assert!(!result.is_empty());
}
}
#[test]
fn test_reloc_handler_all_known_types_no_panic() {
let known = vec![
R_X86_64_NONE,
R_X86_64_64,
R_X86_64_PC32,
R_X86_64_GOT32,
R_X86_64_PLT32,
R_X86_64_COPY,
R_X86_64_GLOB_DAT,
R_X86_64_JUMP_SLOT,
R_X86_64_RELATIVE,
R_X86_64_GOTPCREL,
R_X86_64_32,
R_X86_64_32S,
R_X86_64_16,
R_X86_64_PC16,
R_X86_64_8,
R_X86_64_PC8,
R_X86_64_GOTOFF64,
R_X86_64_GOTPC32,
R_X86_64_SIZE32,
R_X86_64_SIZE64,
R_X86_64_PC64,
];
let mut buf = vec![0u8; 64];
for t in known {
let _ = X86RelocationHandler::apply(&mut buf, 8, t, 0x1000, 0, 0x1000);
}
}
#[test]
fn test_context_full_lifecycle() {
let mut ctx = X86MCContext::new("x86_64-pc-linux-gnu", "znver4");
ctx.set_code_model(X86CodeModel::Small);
ctx.set_relocation_model(X86RelocationModel::PIC);
ctx.set_pic(true);
ctx.set_gen_dwarf(true);
ctx.set_use_cfi(true);
ctx.get_or_create_section(MCSection::new(".text", MCSectionType::Text));
ctx.get_or_create_section(MCSection::new(".data", MCSectionType::Data));
ctx.get_or_create_section(MCSection::new(".bss", MCSectionType::Bss));
ctx.emit_instruction(make_mc_inst(24, vec![]));
ctx.emit_instruction(make_mc_inst(11, vec![MCOperand::Reg(RBP as u32)]));
ctx.emit_instruction(make_mc_inst(
1,
vec![MCOperand::Reg(RBP as u32), MCOperand::Reg(RSP as u32)],
));
ctx.record_debug_file(X86DebugFileEntry {
file_id: 1,
directory_id: 0,
name: "test.c".into(),
directory: ".".into(),
md5_checksum: None,
});
ctx.record_debug_line(X86DebugLineEntry {
file_id: 1,
line: 1,
column: 0,
..Default::default()
});
ctx.record_cfi(X86CFIInstruction::StartProc);
ctx.record_cfi(X86CFIInstruction::DefCfa {
register: RSP,
offset: 8,
});
ctx.record_cfi(X86CFIInstruction::EndProc);
let flat = ctx.flatten();
assert!(!flat.is_empty());
assert!(ctx.instruction_count() >= 3);
}
}