#![allow(non_upper_case_globals, dead_code)]
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
use std::fmt;
use std::mem;
use crate::codegen::{
MachineBasicBlock, MachineFunction, MachineInstr, MachineOperand, PhysReg, VirtReg,
};
use crate::x86::x86_instr_info::{X86InstrInfo, X86Opcode};
use crate::x86::x86_register_info::{RAX, RBP, RBX, RCX, RDI, RDX, RFLAGS, RIP, RSI, RSP};
pub const DEFAULT_FUNCTION_ALIGNMENT: u32 = 16;
pub const DEFAULT_BLOCK_ALIGNMENT: u32 = 4;
pub const DEFAULT_LOOP_ALIGNMENT: u32 = 16;
pub const MAX_PC_REL_DISP_64: i64 = 0x8000_0000;
pub const MAX_PC_REL_DISP_32: i64 = 0x8000_0000;
pub const MAX_SHORT_JUMP: i64 = 127;
pub const MIN_SHORT_JUMP: i64 = -128;
pub const MAX_NEAR_JUMP: i64 = 0x7FFF_FFFF;
pub const MIN_NEAR_JUMP: i64 = -0x8000_0000;
pub const MAX_CONSTANT_ISLAND_REACH: i64 = 0x7FFF_FFFF;
pub const CONSTANT_ISLAND_ALIGNMENT: u32 = 8;
pub const MAX_JUMP_TABLE_ENTRIES: usize = 4096;
pub const JUMP_TABLE_ENTRY_SIZE_64: u32 = 8;
pub const JUMP_TABLE_ENTRY_SIZE_32: u32 = 4;
pub const NOP1: &[u8] = &[0x90];
pub const NOP2_INTEL: &[u8] = &[0x66, 0x90];
pub const NOP3_INTEL: &[u8] = &[0x0F, 0x1F, 0x00];
pub const NOP4_INTEL: &[u8] = &[0x0F, 0x1F, 0x40, 0x00];
pub const NOP5_INTEL: &[u8] = &[0x0F, 0x1F, 0x44, 0x00, 0x00];
pub const NOP6_INTEL: &[u8] = &[0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00];
pub const NOP7_INTEL: &[u8] = &[0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00];
pub const NOP8_INTEL: &[u8] = &[0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00];
pub const NOP9_INTEL: &[u8] = &[0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00];
pub const NOP10_INTEL: &[u8] = &[0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00];
pub const NOP11_INTEL: &[u8] = &[
0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
];
pub const NOP12_INTEL: &[u8] = &[
0x66, 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
];
pub const NOP13_INTEL: &[u8] = &[
0x66, 0x66, 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
];
pub const NOP14_INTEL: &[u8] = &[
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
];
pub const NOP15_INTEL: &[u8] = &[
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NOPVariant {
Intel,
Amd,
SingleByte,
LongNop,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ElfSectionType {
Null,
Progbits,
Symtab,
Strtab,
Rela,
Hash,
Dynamic,
Note,
Nobits,
Rel,
Dynsym,
InitArray,
FiniArray,
PreinitArray,
Group,
SymtabShndx,
X8664Unwind,
}
impl ElfSectionType {
pub fn elf_value(&self) -> u32 {
match self {
ElfSectionType::Null => 0,
ElfSectionType::Progbits => 1,
ElfSectionType::Symtab => 2,
ElfSectionType::Strtab => 3,
ElfSectionType::Rela => 4,
ElfSectionType::Hash => 5,
ElfSectionType::Dynamic => 6,
ElfSectionType::Note => 7,
ElfSectionType::Nobits => 8,
ElfSectionType::Rel => 9,
ElfSectionType::Dynsym => 11,
ElfSectionType::InitArray => 14,
ElfSectionType::FiniArray => 15,
ElfSectionType::PreinitArray => 16,
ElfSectionType::Group => 17,
ElfSectionType::SymtabShndx => 18,
ElfSectionType::X8664Unwind => 0x7000_0001,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ElfSectionFlags {
pub write: bool,
pub alloc: bool,
pub execinstr: bool,
pub merge: bool,
pub strings: bool,
pub info_link: bool,
pub link_order: bool,
pub os_nonconforming: bool,
pub group: bool,
pub tls: bool,
}
impl ElfSectionFlags {
pub fn text() -> Self {
Self {
write: false,
alloc: true,
execinstr: true,
merge: false,
strings: false,
info_link: false,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
}
}
pub fn data() -> Self {
Self {
write: true,
alloc: true,
execinstr: false,
merge: false,
strings: false,
info_link: false,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
}
}
pub fn rodata() -> Self {
Self {
write: false,
alloc: true,
execinstr: false,
merge: false,
strings: false,
info_link: false,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
}
}
pub fn bss() -> Self {
Self {
write: true,
alloc: true,
execinstr: false,
merge: false,
strings: false,
info_link: false,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
}
}
pub fn elf_value(&self) -> u64 {
let mut flags: u64 = 0;
if self.write {
flags |= 0x1;
} if self.alloc {
flags |= 0x2;
} if self.execinstr {
flags |= 0x4;
} if self.merge {
flags |= 0x10;
} if self.strings {
flags |= 0x20;
} if self.info_link {
flags |= 0x40;
} if self.link_order {
flags |= 0x80;
} if self.os_nonconforming {
flags |= 0x100;
} if self.group {
flags |= 0x200;
} if self.tls {
flags |= 0x400;
} flags
}
}
#[derive(Debug, Clone)]
pub struct ElfSection {
pub name: String,
pub section_type: ElfSectionType,
pub flags: ElfSectionFlags,
pub addr: u64,
pub offset: u64,
pub size: u64,
pub alignment: u32,
pub link: u32,
pub info: u32,
pub entsize: u64,
pub data: Vec<u8>,
pub finalized: bool,
}
impl ElfSection {
pub fn new(name: &str, section_type: ElfSectionType, flags: ElfSectionFlags) -> Self {
Self {
name: name.to_string(),
section_type,
flags,
addr: 0,
offset: 0,
size: 0,
alignment: 1,
link: 0,
info: 0,
entsize: 0,
data: Vec::new(),
finalized: false,
}
}
pub fn append(&mut self, data: &[u8]) {
self.data.extend_from_slice(data);
self.size = self.data.len() as u64;
}
pub fn append_aligned(&mut self, data: &[u8], alignment: u32) {
let current_offset = self.data.len() as u64;
let misalignment = current_offset & (alignment as u64 - 1);
if misalignment != 0 {
let padding = alignment as u64 - misalignment;
let nops = generate_nop_padding(padding as usize, NOPVariant::Intel);
self.data.extend_from_slice(&nops);
}
self.data.extend_from_slice(data);
self.size = self.data.len() as u64;
}
pub fn append_u8(&mut self, value: u8) {
self.data.push(value);
self.size = self.data.len() as u64;
}
pub fn append_u16(&mut self, value: u16) {
self.data.extend_from_slice(&value.to_le_bytes());
self.size = self.data.len() as u64;
}
pub fn append_u32(&mut self, value: u32) {
self.data.extend_from_slice(&value.to_le_bytes());
self.size = self.data.len() as u64;
}
pub fn append_u64(&mut self, value: u64) {
self.data.extend_from_slice(&value.to_le_bytes());
self.size = self.data.len() as u64;
}
pub fn append_sleb128(&mut self, value: i64) {
let encoded = encode_sleb128(value);
self.data.extend_from_slice(&encoded);
self.size = self.data.len() as u64;
}
pub fn append_uleb128(&mut self, value: u64) {
let encoded = encode_uleb128(value);
self.data.extend_from_slice(&encoded);
self.size = self.data.len() as u64;
}
}
#[derive(Debug, Clone)]
pub struct ElfSymbol {
pub name: String,
pub value: u64,
pub size: u64,
pub symbol_type: SymbolType,
pub binding: SymbolBinding,
pub visibility: SymbolVisibility,
pub section_index: u32,
pub is_undefined: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolType {
Notype,
Object,
Func,
Section,
File,
Tls,
Common,
GnuIFunc,
}
impl SymbolType {
pub fn elf_value(&self) -> u8 {
match self {
SymbolType::Notype => 0,
SymbolType::Object => 1,
SymbolType::Func => 2,
SymbolType::Section => 3,
SymbolType::File => 4,
SymbolType::Tls => 6,
SymbolType::Common => 5,
SymbolType::GnuIFunc => 10,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolBinding {
Local,
Global,
Weak,
GnuUnique,
}
impl SymbolBinding {
pub fn elf_value(&self) -> u8 {
match self {
SymbolBinding::Local => 0,
SymbolBinding::Global => 1,
SymbolBinding::Weak => 2,
SymbolBinding::GnuUnique => 10,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolVisibility {
Default,
Internal,
Hidden,
Protected,
}
impl SymbolVisibility {
pub fn elf_value(&self) -> u8 {
match self {
SymbolVisibility::Default => 0,
SymbolVisibility::Internal => 1,
SymbolVisibility::Hidden => 2,
SymbolVisibility::Protected => 3,
}
}
}
impl ElfSymbol {
pub fn new_function(name: &str, value: u64, size: u64, section_index: u32) -> Self {
Self {
name: name.to_string(),
value,
size,
symbol_type: SymbolType::Func,
binding: SymbolBinding::Global,
visibility: SymbolVisibility::Default,
section_index,
is_undefined: false,
}
}
pub fn new_local(name: &str, value: u64, section_index: u32) -> Self {
Self {
name: name.to_string(),
value,
size: 0,
symbol_type: SymbolType::Notype,
binding: SymbolBinding::Local,
visibility: SymbolVisibility::Default,
section_index,
is_undefined: false,
}
}
pub fn as_undefined(mut self) -> Self {
self.is_undefined = true;
self.section_index = 0; self
}
}
#[derive(Debug, Clone)]
pub struct ElfRelocation {
pub offset: u64,
pub rel_type: X86RelocationType,
pub symbol_index: u32,
pub addend: i64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86RelocationType {
None,
Dir64,
PC32,
Got32,
Plt32,
Copy,
GlobDat,
JumpSlot,
Relative,
GotPcRel,
Dir32,
Dir32S,
Dir16,
PC16,
PC8,
PC64,
GotPC32,
TlsGd,
TlsLd,
TpOff32,
GotTpOff,
TlsDesc,
}
impl X86RelocationType {
pub fn elf_value_x86_64(&self) -> u32 {
match self {
X86RelocationType::None => 0,
X86RelocationType::Dir64 => 1,
X86RelocationType::PC32 => 2,
X86RelocationType::Got32 => 3,
X86RelocationType::Plt32 => 4,
X86RelocationType::Copy => 5,
X86RelocationType::GlobDat => 6,
X86RelocationType::JumpSlot => 7,
X86RelocationType::Relative => 8,
X86RelocationType::GotPcRel => 9,
X86RelocationType::Dir32 => 10,
X86RelocationType::Dir32S => 11,
X86RelocationType::Dir16 => 12,
X86RelocationType::PC16 => 13,
X86RelocationType::PC8 => 14,
X86RelocationType::PC64 => 24,
X86RelocationType::GotPC32 => 25,
X86RelocationType::TlsGd => 18,
X86RelocationType::TlsLd => 19,
X86RelocationType::TpOff32 => 23,
X86RelocationType::GotTpOff => 22,
X86RelocationType::TlsDesc => 36,
}
}
}
#[derive(Debug, Clone)]
pub struct ConstantIsland {
pub id: usize,
pub offset: u64,
pub values: Vec<ConstantIslandEntry>,
pub alignment: u32,
pub is_inline: bool,
pub function_name: String,
}
#[derive(Debug, Clone)]
pub struct ConstantIslandEntry {
pub value: u64,
pub size: u8,
pub is_pc_rel: bool,
pub label: Option<String>,
}
#[derive(Debug, Clone)]
pub struct JumpTable {
pub id: usize,
pub function_name: String,
pub entries: Vec<String>,
pub entry_size: u32,
pub min_case: i64,
pub max_case: i64,
pub alignment: u32,
pub pc_relative: bool,
pub offset: u64,
}
#[derive(Debug, Clone)]
pub enum CFIDirective {
DefCfa(u16, i64),
DefCfaRegister(u16),
DefCfaOffset(i64),
Offset(u16, i64),
Restore(u16),
Undefined(u16),
SameValue(u16),
Register(u16, u16),
RememberState,
RestoreState,
AdvanceLoc(i64),
AdvanceLoc1(u8),
AdvanceLoc2(u16),
AdvanceLoc4(u32),
EndFde,
Expression(u16, Vec<u8>),
ValOffset(u16, i64),
ValExpression(u16, Vec<u8>),
WindowSave,
ArgumentsSize(i64),
}
#[derive(Debug, Clone)]
pub struct FDE {
pub cie_index: u32,
pub initial_location: u64,
pub address_range: u64,
pub directives: Vec<CFIDirective>,
pub lsda: Option<u64>,
pub personality: Option<u64>,
pub has_lsda: bool,
}
#[derive(Debug, Clone)]
pub struct CIE {
pub version: u8,
pub augmentation: String,
pub code_alignment_factor: u64,
pub data_alignment_factor: i64,
pub return_address_register: u16,
pub initial_directives: Vec<CFIDirective>,
pub has_personality: bool,
pub personality_encoding: u8,
pub has_lsda_encoding: bool,
pub lsda_encoding: u8,
pub fde_encoding: u8,
}
pub fn encode_uleb128(value: u64) -> Vec<u8> {
let mut result: Vec<u8> = Vec::new();
let mut val = value;
loop {
let mut byte = (val & 0x7F) as u8;
val >>= 7;
if val != 0 {
byte |= 0x80;
}
result.push(byte);
if val == 0 {
break;
}
}
result
}
pub fn encode_sleb128(value: i64) -> Vec<u8> {
let mut result: Vec<u8> = Vec::new();
let mut val = value;
let mut more = true;
while more {
let mut byte = (val & 0x7F) as u8;
val >>= 7;
if (val == 0 && (byte & 0x40) == 0) || (val == -1 && (byte & 0x40) != 0) {
more = false;
} else {
byte |= 0x80;
}
result.push(byte);
}
result
}
pub fn generate_nop_padding(length: usize, variant: NOPVariant) -> Vec<u8> {
if length == 0 {
return Vec::new();
}
let mut result: Vec<u8> = Vec::with_capacity(length);
let mut remaining = length;
match variant {
NOPVariant::Intel => {
while remaining > 0 {
let nop = match remaining {
1 => NOP1,
2 => NOP2_INTEL,
3 => NOP3_INTEL,
4 => NOP4_INTEL,
5 => NOP5_INTEL,
6 => NOP6_INTEL,
7 => NOP7_INTEL,
8 => NOP8_INTEL,
9 => NOP9_INTEL,
10 => NOP10_INTEL,
11 => NOP11_INTEL,
12 => NOP12_INTEL,
13 => NOP13_INTEL,
14 => NOP14_INTEL,
_ => NOP15_INTEL,
};
let take = nop.len().min(remaining);
result.extend_from_slice(&nop[..take]);
remaining -= take;
}
}
NOPVariant::Amd => {
while remaining > 0 {
let nop = match remaining {
1 => NOP1,
2 => NOP2_INTEL,
3 => NOP3_INTEL,
4 => NOP4_INTEL,
5..=15 => {
NOP5_INTEL
}
_ => NOP15_INTEL,
};
let take = nop.len().min(remaining);
result.extend_from_slice(&nop[..take]);
remaining -= take;
}
}
NOPVariant::SingleByte => {
result.resize(length, 0x90);
}
NOPVariant::LongNop => {
while remaining > 1 {
result.push(0x66);
remaining -= 1;
}
if remaining == 1 {
result.push(0x90);
}
}
}
result
}
pub fn generate_alignment_padding(
current_offset: u64,
alignment: u32,
variant: NOPVariant,
) -> Vec<u8> {
let alignment = alignment as u64;
if alignment <= 1 {
return Vec::new();
}
let misalignment = current_offset & (alignment - 1);
if misalignment == 0 {
return Vec::new();
}
let padding = (alignment - misalignment) as usize;
generate_nop_padding(padding, variant)
}
#[derive(Debug, Clone)]
pub struct DwarfLineProgram {
pub bytecode: Vec<u8>,
pub standard_opcode_lengths: Vec<u8>,
pub minimum_instruction_length: u8,
pub maximum_operations_per_instruction: u8,
pub default_is_stmt: bool,
pub line_base: i8,
pub line_range: u8,
pub opcode_base: u8,
}
impl DwarfLineProgram {
pub fn new() -> Self {
Self {
bytecode: Vec::new(),
standard_opcode_lengths: vec![0; 12], minimum_instruction_length: 1,
maximum_operations_per_instruction: 1,
default_is_stmt: true,
line_base: -5,
line_range: 14,
opcode_base: 13, }
}
pub fn emit_special_opcode(&mut self, line_advance: i32, addr_advance: u32) {
let adjusted_line = line_advance - self.line_base as i32;
if adjusted_line >= 0 && adjusted_line < self.line_range as i32 && addr_advance < 256 {
let opcode = (adjusted_line as u8) + (self.line_range * addr_advance as u8);
let special = opcode + self.opcode_base;
self.bytecode.push(special);
}
}
pub fn emit_copy(&mut self) {
self.bytecode.push(0x01); }
pub fn emit_advance_pc(&mut self, advance: u64) {
self.bytecode.push(0x02); self.bytecode.extend_from_slice(&encode_uleb128(advance));
}
pub fn emit_advance_line(&mut self, advance: i64) {
self.bytecode.push(0x03); self.bytecode.extend_from_slice(&encode_sleb128(advance));
}
pub fn emit_set_file(&mut self, file_index: u64) {
self.bytecode.push(0x04); self.bytecode.extend_from_slice(&encode_uleb128(file_index));
}
pub fn emit_set_column(&mut self, column: u64) {
self.bytecode.push(0x05); self.bytecode.extend_from_slice(&encode_uleb128(column));
}
pub fn emit_end_sequence(&mut self) {
self.bytecode.push(0x00); self.bytecode.extend_from_slice(&encode_uleb128(1)); self.bytecode.push(0x01); }
pub fn emit_set_address(&mut self, addr: u64) {
self.bytecode.push(0x00); self.bytecode.extend_from_slice(&encode_uleb128(9)); self.bytecode.push(0x02); self.bytecode.extend_from_slice(&addr.to_le_bytes());
}
pub fn emit_define_file(&mut self, name: &str, dir_index: u64, mtime: u64, size: u64) {
let mut payload: Vec<u8> = Vec::new();
payload.extend_from_slice(name.as_bytes());
payload.push(0); payload.extend_from_slice(&encode_uleb128(dir_index));
payload.extend_from_slice(&encode_uleb128(mtime));
payload.extend_from_slice(&encode_uleb128(size));
self.bytecode.push(0x00); self.bytecode
.extend_from_slice(&encode_uleb128(1 + payload.len() as u64));
self.bytecode.push(0x03); self.bytecode.extend_from_slice(&payload);
}
pub fn len(&self) -> usize {
self.bytecode.len()
}
pub fn is_empty(&self) -> bool {
self.bytecode.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct LsdaCallSite {
pub start_offset: u32,
pub length: u32,
pub landing_pad: u32,
pub action_index: u32,
}
#[derive(Debug, Clone)]
pub struct LsdaAction {
pub type_filter: i32,
pub next_action_offset: i32,
}
#[derive(Debug, Clone)]
pub struct Lsda {
pub landing_pad_base: u32,
pub ttype_encoding: u8,
pub type_table: Vec<String>,
pub call_sites: Vec<LsdaCallSite>,
pub actions: Vec<LsdaAction>,
pub is_sjlj: bool,
}
impl Lsda {
pub fn new() -> Self {
Self {
landing_pad_base: 0,
ttype_encoding: 0x9B, type_table: Vec::new(),
call_sites: Vec::new(),
actions: Vec::new(),
is_sjlj: false,
}
}
pub fn encode(&self) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.push(self.landing_pad_base as u8);
data.push(self.ttype_encoding);
if !self.type_table.is_empty() {
data.extend_from_slice(&encode_uleb128(0)); } else {
data.push(0);
}
let cst_len = self.call_sites.len() as u64;
data.extend_from_slice(&encode_uleb128(cst_len));
for site in &self.call_sites {
data.extend_from_slice(&encode_uleb128(site.start_offset as u64));
data.extend_from_slice(&encode_uleb128(site.length as u64));
data.extend_from_slice(&encode_uleb128(site.landing_pad as u64));
data.extend_from_slice(&encode_uleb128(site.action_index as u64));
}
data
}
}
#[derive(Debug, Clone)]
pub struct X86CodeEmission {
pub target_triple: String,
pub is_64bit: bool,
pub config: CodeEmissionConfig,
pub sections: HashMap<String, ElfSection>,
pub section_order: Vec<String>,
pub symbols: Vec<ElfSymbol>,
pub relocations: Vec<ElfRelocation>,
pub constant_islands: Vec<ConstantIsland>,
pub jump_tables: Vec<JumpTable>,
pub text_section: String,
pub data_section: String,
pub rodata_section: String,
pub current_section: Option<String>,
pub text_offset: u64,
pub stats: CodeEmissionStats,
}
#[derive(Debug, Clone)]
pub struct CodeEmissionConfig {
pub function_alignment: u32,
pub loop_alignment: u32,
pub block_alignment: u32,
pub constant_island_alignment: u32,
pub jump_table_alignment: u32,
pub nop_variant: NOPVariant,
pub emit_debug_line: bool,
pub emit_cfi: bool,
pub emit_eh_tables: bool,
pub optimize_alignments: bool,
pub compact_unwind: bool,
pub target_os: TargetOs,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetOs {
Linux,
MacOS,
Windows,
FreeBSD,
Unknown,
}
impl Default for CodeEmissionConfig {
fn default() -> Self {
Self {
function_alignment: DEFAULT_FUNCTION_ALIGNMENT,
loop_alignment: DEFAULT_LOOP_ALIGNMENT,
block_alignment: DEFAULT_BLOCK_ALIGNMENT,
constant_island_alignment: CONSTANT_ISLAND_ALIGNMENT,
jump_table_alignment: 8,
nop_variant: NOPVariant::Intel,
emit_debug_line: false,
emit_cfi: false,
emit_eh_tables: false,
optimize_alignments: true,
compact_unwind: false,
target_os: TargetOs::Linux,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct CodeEmissionStats {
pub functions_emitted: usize,
pub blocks_emitted: usize,
pub code_bytes: u64,
pub data_bytes: u64,
pub nop_bytes: u64,
pub constant_islands: usize,
pub jump_tables: usize,
pub relocations_count: usize,
pub symbols_count: usize,
pub debug_bytes: u64,
}
impl X86CodeEmission {
pub fn new_x86_64(target: &str) -> Self {
let target_os = if target.contains("linux") {
TargetOs::Linux
} else if target.contains("darwin") || target.contains("macos") {
TargetOs::MacOS
} else if target.contains("windows") {
TargetOs::Windows
} else if target.contains("freebsd") {
TargetOs::FreeBSD
} else {
TargetOs::Unknown
};
Self {
target_triple: target.to_string(),
is_64bit: true,
config: CodeEmissionConfig::default(),
sections: HashMap::new(),
section_order: Vec::new(),
symbols: Vec::new(),
relocations: Vec::new(),
constant_islands: Vec::new(),
jump_tables: Vec::new(),
text_section: ".text".to_string(),
data_section: ".data".to_string(),
rodata_section: ".rodata".to_string(),
current_section: None,
text_offset: 0,
stats: CodeEmissionStats::default(),
}
}
pub fn new_x86_32(target: &str) -> Self {
let mut emitter = Self::new_x86_64(target);
emitter.is_64bit = false;
emitter
}
pub fn with_config(mut self, config: CodeEmissionConfig) -> Self {
self.config = config;
self
}
pub fn prepare_function(&mut self, mf: &MachineFunction) {
self.stats.functions_emitted += 1;
self.stats.blocks_emitted += mf.blocks.len();
self.switch_to_section(&self.text_section.clone());
self.emit_function_alignment(mf);
self.emit_symbol(&mf.name, self.text_offset, mf.blocks.len() as u64 * 16);
for block in &mf.blocks {
if self.config.optimize_alignments {
self.emit_block_alignment();
}
for _instr in &block.instructions {
self.stats.code_bytes += 4; }
}
self.emit_constant_islands_for_function(mf);
self.emit_jump_tables_for_function(mf);
if self.config.emit_cfi {
self.emit_cfi_for_function(mf);
}
if self.config.emit_debug_line {
self.emit_debug_line_for_function(mf);
}
if self.config.emit_eh_tables {
self.emit_eh_for_function(mf);
}
}
pub fn switch_to_section(&mut self, name: &str) {
if self.current_section.as_deref() == Some(name) {
return; }
if !self.sections.contains_key(name) {
self.create_section(name);
}
self.current_section = Some(name.to_string());
}
fn create_section(&mut self, name: &str) {
let (section_type, flags) = self.section_defaults(name);
let section = ElfSection::new(name, section_type, flags);
if !self.section_order.contains(&name.to_string()) {
self.section_order.push(name.to_string());
}
self.sections.insert(name.to_string(), section);
}
fn section_defaults(&self, name: &str) -> (ElfSectionType, ElfSectionFlags) {
match name {
".text" | ".text.hot" | ".text.startup" | ".text.unlikely" => {
(ElfSectionType::Progbits, ElfSectionFlags::text())
}
".data" | ".data.rel" | ".data.rel.ro" => {
(ElfSectionType::Progbits, ElfSectionFlags::data())
}
".rodata" | ".rodata.str1.1" | ".rodata.cst4" | ".rodata.cst8" | ".rodata.cst16" => {
(ElfSectionType::Progbits, ElfSectionFlags::rodata())
}
".bss" => (ElfSectionType::Nobits, ElfSectionFlags::bss()),
".eh_frame" => (
ElfSectionType::X8664Unwind,
ElfSectionFlags {
write: false,
alloc: true,
execinstr: false,
merge: false,
strings: false,
info_link: false,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
},
),
".debug_line" | ".debug_info" | ".debug_abbrev" | ".debug_str" | ".debug_loc"
| ".debug_ranges" => (
ElfSectionType::Progbits,
ElfSectionFlags {
write: false,
alloc: false,
execinstr: false,
merge: false,
strings: false,
info_link: false,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
},
),
".symtab" => (
ElfSectionType::Symtab,
ElfSectionFlags {
write: false,
alloc: false,
execinstr: false,
merge: false,
strings: false,
info_link: false,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
},
),
".strtab" => (
ElfSectionType::Strtab,
ElfSectionFlags {
write: false,
alloc: false,
execinstr: false,
merge: false,
strings: false,
info_link: false,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
},
),
".rela.text" | ".rela.data" | ".rela.eh_frame" => (
ElfSectionType::Rela,
ElfSectionFlags {
write: false,
alloc: false,
execinstr: false,
merge: false,
strings: false,
info_link: true,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
},
),
_ => (
ElfSectionType::Progbits,
ElfSectionFlags {
write: false,
alloc: false,
execinstr: false,
merge: false,
strings: false,
info_link: false,
link_order: false,
os_nonconforming: false,
group: false,
tls: false,
},
),
}
}
pub fn emit_function_alignment(&mut self, _mf: &MachineFunction) {
if self.config.function_alignment > 1 {
let padding = generate_alignment_padding(
self.text_offset,
self.config.function_alignment,
self.config.nop_variant,
);
if !padding.is_empty() {
self.stats.nop_bytes += padding.len() as u64;
self.append_to_current_section(&padding);
self.text_offset += padding.len() as u64;
}
}
}
fn emit_block_alignment(&mut self) {
if self.config.block_alignment > 1 {
let padding = generate_alignment_padding(
self.text_offset,
self.config.block_alignment,
self.config.nop_variant,
);
if !padding.is_empty() {
self.stats.nop_bytes += padding.len() as u64;
self.append_to_current_section(&padding);
self.text_offset += padding.len() as u64;
}
}
}
pub fn emit_loop_alignment(&mut self) {
if self.config.loop_alignment > 1 {
let padding = generate_alignment_padding(
self.text_offset,
self.config.loop_alignment,
self.config.nop_variant,
);
if !padding.is_empty() {
self.stats.nop_bytes += padding.len() as u64;
self.append_to_current_section(&padding);
self.text_offset += padding.len() as u64;
}
}
}
pub fn add_constant_island(&mut self, island: ConstantIsland) {
self.constant_islands.push(island);
self.stats.constant_islands += 1;
}
fn emit_constant_islands_for_function(&mut self, mf: &MachineFunction) {
let func_name = &mf.name;
let islands: Vec<usize> = self
.constant_islands
.iter()
.enumerate()
.filter(|(_, ci)| ci.function_name == *func_name)
.map(|(i, _)| i)
.collect();
if islands.is_empty() {
return;
}
for idx in islands {
let island = &self.constant_islands[idx];
if island.is_inline {
let alignment = island.alignment;
let padding = generate_alignment_padding(
self.text_offset,
alignment,
self.config.nop_variant,
);
self.append_to_current_section(&padding);
self.text_offset += padding.len() as u64;
for entry in &island.values {
match entry.size {
4 => {
self.append_u32_to_current_section(entry.value as u32);
self.text_offset += 4;
}
8 => {
self.append_u64_to_current_section(entry.value);
self.text_offset += 8;
}
_ => {
self.append_u64_to_current_section(entry.value);
self.text_offset += 8;
}
}
}
self.stats.data_bytes += island.values.len() as u64 * 8;
} else {
self.switch_to_section(&self.rodata_section);
let alignment = island.alignment;
let padding = generate_alignment_padding(
self.sections
.get(&self.rodata_section)
.map_or(0, |s| s.size),
alignment,
self.config.nop_variant,
);
self.append_to_current_section(&padding);
for entry in &island.values {
match entry.size {
4 => {
self.append_u32_to_current_section(entry.value as u32);
}
8 => {
self.append_u64_to_current_section(entry.value);
}
_ => {
self.append_u64_to_current_section(entry.value);
}
}
}
self.switch_to_section(&self.text_section);
self.stats.data_bytes += island.values.len() as u64 * 8;
}
}
}
pub fn add_jump_table(&mut self, table: JumpTable) {
self.jump_tables.push(table);
self.stats.jump_tables += 1;
}
fn emit_jump_tables_for_function(&mut self, mf: &MachineFunction) {
let func_name = &mf.name;
let tables: Vec<usize> = self
.jump_tables
.iter()
.enumerate()
.filter(|(_, jt)| jt.function_name == *func_name)
.map(|(i, _)| i)
.collect();
if tables.is_empty() {
return;
}
self.switch_to_section(&self.rodata_section);
for idx in tables {
let table = &self.jump_tables[idx];
let alignment = table.alignment;
let current_size = self
.sections
.get(&self.rodata_section)
.map_or(0, |s| s.size);
let padding =
generate_alignment_padding(current_size, alignment, self.config.nop_variant);
self.append_to_current_section(&padding);
for _entry in &table.entries {
if table.entry_size == 8 {
self.append_u64_to_current_section(0); } else {
self.append_u32_to_current_section(0); }
}
self.stats.data_bytes += table.entries.len() as u64 * table.entry_size as u64;
}
self.switch_to_section(&self.text_section);
}
pub fn emit_symbol(&mut self, name: &str, value: u64, size: u64) {
let section_index = self.get_current_section_index();
let symbol = ElfSymbol::new_function(name, value, size, section_index);
self.symbols.push(symbol);
self.stats.symbols_count += 1;
}
pub fn emit_relocation(
&mut self,
offset: u64,
rel_type: X86RelocationType,
symbol_name: &str,
addend: i64,
) {
let symbol_index = self.find_or_create_symbol(symbol_name);
self.relocations.push(ElfRelocation {
offset,
rel_type,
symbol_index,
addend,
});
self.stats.relocations_count += 1;
}
fn find_or_create_symbol(&mut self, name: &str) -> u32 {
if let Some(pos) = self.symbols.iter().position(|s| s.name == name) {
pos as u32
} else {
let idx = self.symbols.len() as u32;
let sym = ElfSymbol {
name: name.to_string(),
value: 0,
size: 0,
symbol_type: SymbolType::Notype,
binding: SymbolBinding::Global,
visibility: SymbolVisibility::Default,
section_index: 0,
is_undefined: true,
};
self.symbols.push(sym);
self.stats.symbols_count += 1;
idx
}
}
fn emit_cfi_for_function(&mut self, mf: &MachineFunction) {
let fde = self.build_fde_for_function(mf);
self.switch_to_section(".eh_frame");
let cie = CIE {
version: 1,
augmentation: "zR".to_string(),
code_alignment_factor: 1,
data_alignment_factor: if self.is_64bit { -8 } else { -4 },
return_address_register: if self.is_64bit { 16 } else { 8 }, initial_directives: Vec::new(),
has_personality: false,
personality_encoding: 0,
has_lsda_encoding: false,
lsda_encoding: 0,
fde_encoding: 0x1B, };
let encoded = self.encode_eh_frame(&cie, &fde);
self.append_to_current_section(&encoded);
self.stats.debug_bytes += encoded.len() as u64;
self.switch_to_section(&self.text_section);
}
fn build_fde_for_function(&self, mf: &MachineFunction) -> FDE {
let mut directives: Vec<CFIDirective> = Vec::new();
if self.is_64bit {
directives.push(CFIDirective::DefCfaOffset(16));
directives.push(CFIDirective::Offset(RBP, -16));
directives.push(CFIDirective::DefCfaRegister(RBP));
}
FDE {
cie_index: 0,
initial_location: 0, address_range: mf.blocks.len() as u64 * 16, directives,
lsda: None,
personality: None,
has_lsda: false,
}
}
fn encode_eh_frame(&self, cie: &CIE, fde: &FDE) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.push(cie.version);
data.extend_from_slice(cie.augmentation.as_bytes());
data.push(0);
data.extend_from_slice(&encode_uleb128(cie.code_alignment_factor));
data.extend_from_slice(&encode_sleb128(cie.data_alignment_factor));
data.push(cie.return_address_register as u8);
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0i32.to_le_bytes());
data.extend_from_slice(&0i32.to_le_bytes());
data.extend_from_slice(&(fde.address_range as u32).to_le_bytes());
for directive in &fde.directives {
match directive {
CFIDirective::DefCfaOffset(offset) => {
data.push(0x0F); data.extend_from_slice(&encode_uleb128(*offset as u64));
}
CFIDirective::DefCfaRegister(reg) => {
data.push(0x0D); data.extend_from_slice(&encode_uleb128(*reg as u64));
}
CFIDirective::Offset(reg, offset) => {
data.push(0x80 | (reg & 0x3F) as u8); data.extend_from_slice(&encode_uleb128(*offset as u64));
}
CFIDirective::DefCfa(reg, offset) => {
data.push(0x0C); data.extend_from_slice(&encode_uleb128(*reg as u64));
data.extend_from_slice(&encode_uleb128(*offset as u64));
}
_ => {
}
}
}
data
}
fn emit_debug_line_for_function(&mut self, _mf: &MachineFunction) {
if !self.config.emit_debug_line {
return;
}
let mut program = DwarfLineProgram::new();
if !program.is_empty() {
self.switch_to_section(".debug_line");
self.append_to_current_section(&program.bytecode);
self.stats.debug_bytes += program.bytecode.len() as u64;
self.switch_to_section(&self.text_section);
}
}
fn emit_eh_for_function(&mut self, mf: &MachineFunction) {
if !self.config.emit_eh_tables {
return;
}
let lsda = self.build_lsda_for_function(mf);
let encoded = lsda.encode();
if !encoded.is_empty() {
self.switch_to_section(".gcc_except_table");
self.append_to_current_section(&encoded);
self.stats.debug_bytes += encoded.len() as u64;
self.switch_to_section(&self.text_section);
}
}
fn build_lsda_for_function(&self, _mf: &MachineFunction) -> Lsda {
let mut lsda = Lsda::new();
lsda
}
fn append_to_current_section(&mut self, data: &[u8]) {
if let Some(ref name) = self.current_section.clone() {
if let Some(section) = self.sections.get_mut(name) {
section.append(data);
}
}
}
fn append_u32_to_current_section(&mut self, value: u32) {
if let Some(ref name) = self.current_section.clone() {
if let Some(section) = self.sections.get_mut(name) {
section.append_u32(value);
}
}
}
fn append_u64_to_current_section(&mut self, value: u64) {
if let Some(ref name) = self.current_section.clone() {
if let Some(section) = self.sections.get_mut(name) {
section.append_u64(value);
}
}
}
fn get_current_section_index(&self) -> u32 {
if let Some(ref name) = self.current_section {
self.section_order
.iter()
.position(|n| n == name)
.map_or(0, |i| (i + 1) as u32)
} else {
0
}
}
pub fn finalize_sections(&mut self) {
let mut offset: u64 = 0;
offset += 64;
let shdr_size: u64 = if self.is_64bit { 64 } else { 40 };
let num_sections = self.section_order.len() + 1; offset += shdr_size * num_sections as u64;
for name in &self.section_order.clone() {
if let Some(section) = self.sections.get_mut(name) {
let align = section.alignment as u64;
if align > 1 {
let misalign = offset & (align - 1);
if misalign != 0 {
offset += align - misalign;
}
}
section.offset = offset;
offset += section.size;
section.finalized = true;
}
}
}
pub fn report(&self) -> String {
format!(
"CodeEmission: funcs={} blocks={} code_bytes={} data_bytes={} nop_bytes={} \
const_islands={} jump_tables={} relocs={} symbols={} debug_bytes={} sections={}",
self.stats.functions_emitted,
self.stats.blocks_emitted,
self.stats.code_bytes,
self.stats.data_bytes,
self.stats.nop_bytes,
self.stats.constant_islands,
self.stats.jump_tables,
self.stats.relocations_count,
self.stats.symbols_count,
self.stats.debug_bytes,
self.sections.len(),
)
}
}
pub fn make_x86_64_code_emission(target: &str) -> X86CodeEmission {
X86CodeEmission::new_x86_64(target)
}
pub fn make_x86_32_code_emission(target: &str) -> X86CodeEmission {
X86CodeEmission::new_x86_32(target)
}
pub fn make_code_emission_full(target: &str) -> X86CodeEmission {
let config = CodeEmissionConfig {
emit_debug_line: true,
emit_cfi: true,
emit_eh_tables: true,
optimize_alignments: true,
..CodeEmissionConfig::default()
};
X86CodeEmission::new_x86_64(target).with_config(config)
}
pub fn make_code_emission_release(target: &str) -> X86CodeEmission {
let config = CodeEmissionConfig {
emit_debug_line: false,
emit_cfi: false,
emit_eh_tables: false,
optimize_alignments: true,
..CodeEmissionConfig::default()
};
X86CodeEmission::new_x86_64(target).with_config(config)
}
pub fn prepare_for_emission(mf: &MachineFunction, target: &str) -> X86CodeEmission {
let mut emitter = make_x86_64_code_emission(target);
emitter.prepare_function(mf);
emitter
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_uleb128_zero() {
let encoded = encode_uleb128(0);
assert_eq!(encoded, vec![0x00]);
}
#[test]
fn test_encode_uleb128_small() {
let encoded = encode_uleb128(127);
assert_eq!(encoded, vec![0x7F]);
}
#[test]
fn test_encode_uleb128_large() {
let encoded = encode_uleb128(128);
assert_eq!(encoded, vec![0x80, 0x01]);
}
#[test]
fn test_encode_uleb128_very_large() {
let encoded = encode_uleb128(624485);
assert_eq!(encoded, vec![0xE5, 0x8E, 0x26]);
}
#[test]
fn test_encode_sleb128_zero() {
let encoded = encode_sleb128(0);
assert_eq!(encoded, vec![0x00]);
}
#[test]
fn test_encode_sleb128_positive() {
let encoded = encode_sleb128(64);
assert_eq!(encoded, vec![0x40]);
}
#[test]
fn test_encode_sleb128_negative() {
let encoded = encode_sleb128(-1);
assert_eq!(encoded, vec![0x7F]);
}
#[test]
fn test_encode_sleb128_neg_64() {
let encoded = encode_sleb128(-64);
assert_eq!(encoded, vec![0x40]); }
#[test]
fn test_generate_nop_padding_lengths() {
for len in 1..=20 {
let nops = generate_nop_padding(len, NOPVariant::Intel);
assert_eq!(nops.len(), len, "NOP padding for len={}", len);
}
}
#[test]
fn test_generate_nop_padding_single_byte() {
let nops = generate_nop_padding(5, NOPVariant::SingleByte);
assert_eq!(nops, vec![0x90, 0x90, 0x90, 0x90, 0x90]);
}
#[test]
fn test_generate_alignment_padding() {
let padding = generate_alignment_padding(3, 4, NOPVariant::Intel);
assert_eq!(padding.len(), 1);
let padding = generate_alignment_padding(4, 4, NOPVariant::Intel);
assert_eq!(padding.len(), 0);
let padding = generate_alignment_padding(5, 16, NOPVariant::Intel);
assert_eq!(padding.len(), 11);
}
#[test]
fn test_elf_section_flags_text() {
let flags = ElfSectionFlags::text();
assert!(flags.alloc);
assert!(flags.execinstr);
assert!(!flags.write);
}
#[test]
fn test_elf_section_flags_data() {
let flags = ElfSectionFlags::data();
assert!(flags.alloc);
assert!(flags.write);
assert!(!flags.execinstr);
}
#[test]
fn test_elf_section_flags_rodata() {
let flags = ElfSectionFlags::rodata();
assert!(flags.alloc);
assert!(!flags.write);
assert!(!flags.execinstr);
}
#[test]
fn test_elf_section_append() {
let mut section =
ElfSection::new(".test", ElfSectionType::Progbits, ElfSectionFlags::rodata());
section.append_u32(0xDEADBEEF);
assert_eq!(section.size, 4);
assert_eq!(§ion.data, &[0xEF, 0xBE, 0xAD, 0xDE]);
}
#[test]
fn test_elf_section_append_u16() {
let mut section =
ElfSection::new(".test", ElfSectionType::Progbits, ElfSectionFlags::rodata());
section.append_u16(0x1234);
assert_eq!(section.data, vec![0x34, 0x12]);
}
#[test]
fn test_elf_symbol_new_function() {
let sym = ElfSymbol::new_function("my_func", 0x1000, 0x42, 1);
assert_eq!(sym.name, "my_func");
assert_eq!(sym.value, 0x1000);
assert_eq!(sym.size, 0x42);
assert_eq!(sym.symbol_type, SymbolType::Func);
assert_eq!(sym.binding, SymbolBinding::Global);
}
#[test]
fn test_elf_symbol_as_undefined() {
let sym = ElfSymbol::new_function("ext_func", 0, 0, 1).as_undefined();
assert!(sym.is_undefined);
assert_eq!(sym.section_index, 0);
}
#[test]
fn test_relocation_type_values() {
assert_eq!(X86RelocationType::Dir64.elf_value_x86_64(), 1);
assert_eq!(X86RelocationType::PC32.elf_value_x86_64(), 2);
assert_eq!(X86RelocationType::GotPcRel.elf_value_x86_64(), 9);
}
#[test]
fn test_dwarf_line_program_special_opcode() {
let mut program = DwarfLineProgram::new();
program.emit_special_opcode(0, 0);
assert_eq!(program.bytecode[0], 18);
}
#[test]
fn test_lsda_new() {
let lsda = Lsda::new();
assert_eq!(lsda.landing_pad_base, 0);
assert!(lsda.call_sites.is_empty());
assert!(lsda.actions.is_empty());
}
#[test]
fn test_lsda_encode_empty() {
let lsda = Lsda::new();
let encoded = lsda.encode();
assert!(encoded.len() >= 4);
}
#[test]
fn test_code_emission_config_default() {
let config = CodeEmissionConfig::default();
assert_eq!(config.function_alignment, 16);
assert!(config.optimize_alignments);
assert!(!config.emit_debug_line);
}
#[test]
fn test_code_emission_new() {
let emitter = X86CodeEmission::new_x86_64("x86_64-unknown-linux-gnu");
assert!(emitter.is_64bit);
assert_eq!(emitter.text_section, ".text");
assert!(emitter.sections.is_empty());
}
#[test]
fn test_section_switch_creates_section() {
let mut emitter = X86CodeEmission::new_x86_64("x86_64-unknown-linux-gnu");
emitter.switch_to_section(".text");
assert!(emitter.sections.contains_key(".text"));
assert_eq!(emitter.current_section, Some(".text".to_string()));
}
#[test]
fn test_section_switch_no_duplicate() {
let mut emitter = X86CodeEmission::new_x86_64("x86_64-unknown-linux-gnu");
emitter.switch_to_section(".text");
let count_before = emitter.sections.len();
emitter.switch_to_section(".text");
assert_eq!(emitter.sections.len(), count_before);
}
#[test]
fn test_symbol_emission() {
let mut emitter = X86CodeEmission::new_x86_64("x86_64-unknown-linux-gnu");
emitter.switch_to_section(".text");
emitter.emit_symbol("test_func", 0x4000, 64);
assert_eq!(emitter.symbols.len(), 1);
assert_eq!(emitter.symbols[0].name, "test_func");
}
#[test]
fn test_relocation_emission() {
let mut emitter = X86CodeEmission::new_x86_64("x86_64-unknown-linux-gnu");
emitter.emit_relocation(10, X86RelocationType::PC32, "puts", -4);
assert_eq!(emitter.relocations.len(), 1);
assert_eq!(emitter.relocations[0].offset, 10);
}
#[test]
fn test_constant_island_registration() {
let mut emitter = X86CodeEmission::new_x86_64("x86_64-unknown-linux-gnu");
let island = ConstantIsland {
id: 0,
offset: 0,
values: vec![ConstantIslandEntry {
value: 0xDEADBEEF,
size: 8,
is_pc_rel: true,
label: None,
}],
alignment: 8,
is_inline: true,
function_name: "test".to_string(),
};
emitter.add_constant_island(island);
assert_eq!(emitter.constant_islands.len(), 1);
assert_eq!(emitter.stats.constant_islands, 1);
}
#[test]
fn test_jump_table_registration() {
let mut emitter = X86CodeEmission::new_x86_64("x86_64-unknown-linux-gnu");
let table = JumpTable {
id: 0,
function_name: "test".to_string(),
entries: vec!["L0".to_string(), "L1".to_string()],
entry_size: 8,
min_case: 0,
max_case: 1,
alignment: 8,
pc_relative: true,
offset: 0,
};
emitter.add_jump_table(table);
assert_eq!(emitter.jump_tables.len(), 1);
}
#[test]
fn test_nop_variant_enum() {
assert_eq!(NOPVariant::Intel as u8, NOPVariant::Intel as u8);
assert_ne!(NOPVariant::Intel as u8, NOPVariant::Amd as u8);
}
#[test]
fn test_symbol_type_values() {
assert_eq!(SymbolType::Func.elf_value(), 2);
assert_eq!(SymbolType::Object.elf_value(), 1);
assert_eq!(SymbolType::Notype.elf_value(), 0);
}
#[test]
fn test_symbol_binding_values() {
assert_eq!(SymbolBinding::Global.elf_value(), 1);
assert_eq!(SymbolBinding::Local.elf_value(), 0);
assert_eq!(SymbolBinding::Weak.elf_value(), 2);
}
#[test]
fn test_section_type_values() {
assert_eq!(ElfSectionType::Progbits.elf_value(), 1);
assert_eq!(ElfSectionType::Nobits.elf_value(), 8);
}
#[test]
fn test_target_os_detection() {
let emitter = X86CodeEmission::new_x86_64("x86_64-unknown-linux-gnu");
assert_eq!(emitter.config.target_os, TargetOs::Linux);
let emitter = X86CodeEmission::new_x86_64("x86_64-apple-darwin");
assert_eq!(emitter.config.target_os, TargetOs::MacOS);
let emitter = X86CodeEmission::new_x86_64("x86_64-pc-windows-msvc");
assert_eq!(emitter.config.target_os, TargetOs::Windows);
}
#[test]
fn test_report_format() {
let emitter = X86CodeEmission::new_x86_64("x86_64-unknown-linux-gnu");
let report = emitter.report();
assert!(report.contains("CodeEmission:"));
assert!(report.contains("funcs=0"));
}
#[test]
fn test_leb128_roundtrip() {
let test_values: Vec<u64> = vec![0, 1, 127, 128, 255, 65535, 1_000_000];
for val in &test_values {
let encoded = encode_uleb128(*val);
let mut decoded: u64 = 0;
let mut shift: u32 = 0;
for &byte in &encoded {
decoded |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
assert_eq!(decoded, *val, "ULEB128 roundtrip for {}", val);
}
}
}