use std::collections::{HashMap, VecDeque};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetObjectFormat {
ELF,
COFF,
MachO,
Binary,
XCOFF,
GOFF,
Wasm,
}
impl TargetObjectFormat {
pub fn comment_char(&self) -> char {
match self {
TargetObjectFormat::ELF | TargetObjectFormat::Binary => '#',
TargetObjectFormat::COFF | TargetObjectFormat::XCOFF => '#',
TargetObjectFormat::MachO => '#',
TargetObjectFormat::GOFF => '#',
TargetObjectFormat::Wasm => ';',
}
}
pub fn label_prefix(&self) -> &str {
match self {
TargetObjectFormat::MachO => "L",
_ => ".L",
}
}
pub fn directive_prefix(&self) -> &str {
"."
}
pub fn supports_subsections_via_symbols(&self) -> bool {
matches!(self, TargetObjectFormat::MachO)
}
pub fn supports_lazy_reference(&self) -> bool {
matches!(self, TargetObjectFormat::MachO)
}
pub fn supports_plt_suffix(&self) -> bool {
matches!(self, TargetObjectFormat::ELF)
}
pub fn supports_gotpcrel_suffix(&self) -> bool {
matches!(self, TargetObjectFormat::ELF)
}
pub fn supports_seh(&self) -> bool {
matches!(self, TargetObjectFormat::COFF)
}
pub fn supports_codeview(&self) -> bool {
matches!(self, TargetObjectFormat::COFF)
}
}
impl fmt::Display for TargetObjectFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TargetObjectFormat::ELF => write!(f, "elf"),
TargetObjectFormat::COFF => write!(f, "coff"),
TargetObjectFormat::MachO => write!(f, "macho"),
TargetObjectFormat::Binary => write!(f, "binary"),
TargetObjectFormat::XCOFF => write!(f, "xcoff"),
TargetObjectFormat::GOFF => write!(f, "goff"),
TargetObjectFormat::Wasm => write!(f, "wasm"),
}
}
}
impl Default for TargetObjectFormat {
fn default() -> Self {
TargetObjectFormat::ELF
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SectionKind {
Text,
ReadOnly,
ReadOnlyStr,
ReadOnlyCst4,
ReadOnlyCst8,
ReadOnlyCst16,
ReadOnlyCst32,
Data,
Bss,
ThreadData,
ThreadBss,
ThreadRoData,
InitArray,
FiniArray,
PreinitArray,
EHFrame,
EHFrameHdr,
GCCExceptTable,
PData,
XData,
DebugInfo,
DebugLine,
DebugFrame,
DebugAbbrev,
DebugStr,
DebugAranges,
DebugLoc,
DebugRanges,
DebugMacro,
DebugAddr,
DebugStrOffsets,
DebugLineStr,
DebugRngLists,
DebugLocLists,
DebugNames,
DebugTypes,
CoffDebug,
LTO,
Note,
GnuAttributes,
Stabs,
StabsStr,
CTF,
Custom,
}
impl SectionKind {
pub fn is_text(&self) -> bool {
matches!(self, SectionKind::Text)
}
pub fn is_writable(&self) -> bool {
matches!(
self,
SectionKind::Data | SectionKind::Bss | SectionKind::ThreadData | SectionKind::ThreadBss
)
}
pub fn is_bss(&self) -> bool {
matches!(self, SectionKind::Bss | SectionKind::ThreadBss)
}
pub fn is_debug(&self) -> bool {
matches!(
self,
SectionKind::DebugInfo
| SectionKind::DebugLine
| SectionKind::DebugFrame
| SectionKind::DebugAbbrev
| SectionKind::DebugStr
| SectionKind::DebugAranges
| SectionKind::DebugLoc
| SectionKind::DebugRanges
| SectionKind::DebugMacro
| SectionKind::DebugAddr
| SectionKind::DebugStrOffsets
| SectionKind::DebugLineStr
| SectionKind::DebugRngLists
| SectionKind::DebugLocLists
| SectionKind::DebugNames
| SectionKind::DebugTypes
| SectionKind::CoffDebug
| SectionKind::Stabs
| SectionKind::StabsStr
| SectionKind::CTF
)
}
pub fn is_unwind(&self) -> bool {
matches!(
self,
SectionKind::EHFrame
| SectionKind::EHFrameHdr
| SectionKind::GCCExceptTable
| SectionKind::PData
| SectionKind::XData
)
}
pub fn default_elf_name(&self) -> &str {
match self {
SectionKind::Text => ".text",
SectionKind::ReadOnly => ".rodata",
SectionKind::ReadOnlyStr => ".rodata.str1.1",
SectionKind::ReadOnlyCst4 => ".rodata.cst4",
SectionKind::ReadOnlyCst8 => ".rodata.cst8",
SectionKind::ReadOnlyCst16 => ".rodata.cst16",
SectionKind::ReadOnlyCst32 => ".rodata.cst32",
SectionKind::Data => ".data",
SectionKind::Bss => ".bss",
SectionKind::ThreadData => ".tdata",
SectionKind::ThreadBss => ".tbss",
SectionKind::ThreadRoData => ".tdata",
SectionKind::InitArray => ".init_array",
SectionKind::FiniArray => ".fini_array",
SectionKind::PreinitArray => ".preinit_array",
SectionKind::EHFrame => ".eh_frame",
SectionKind::EHFrameHdr => ".eh_frame_hdr",
SectionKind::GCCExceptTable => ".gcc_except_table",
SectionKind::PData => ".pdata",
SectionKind::XData => ".xdata",
SectionKind::DebugInfo => ".debug_info",
SectionKind::DebugLine => ".debug_line",
SectionKind::DebugFrame => ".debug_frame",
SectionKind::DebugAbbrev => ".debug_abbrev",
SectionKind::DebugStr => ".debug_str",
SectionKind::DebugAranges => ".debug_aranges",
SectionKind::DebugLoc => ".debug_loc",
SectionKind::DebugRanges => ".debug_ranges",
SectionKind::DebugMacro => ".debug_macro",
SectionKind::DebugAddr => ".debug_addr",
SectionKind::DebugStrOffsets => ".debug_str_offsets",
SectionKind::DebugLineStr => ".debug_line_str",
SectionKind::DebugRngLists => ".debug_rnglists",
SectionKind::DebugLocLists => ".debug_loclists",
SectionKind::DebugNames => ".debug_names",
SectionKind::DebugTypes => ".debug_types",
SectionKind::CoffDebug => ".debug",
SectionKind::LTO => ".llvm.lto",
SectionKind::Note => ".note",
SectionKind::GnuAttributes => ".gnu.attributes",
SectionKind::Stabs => ".stab",
SectionKind::StabsStr => ".stabstr",
SectionKind::CTF => ".ctf",
SectionKind::Custom => ".custom",
}
}
pub fn default_coff_name(&self) -> &str {
match self {
SectionKind::Text => ".text",
SectionKind::ReadOnly => ".rdata",
SectionKind::ReadOnlyStr => ".rdata",
SectionKind::ReadOnlyCst4 => ".rdata",
SectionKind::ReadOnlyCst8 => ".rdata",
SectionKind::ReadOnlyCst16 => ".rdata",
SectionKind::ReadOnlyCst32 => ".rdata",
SectionKind::Data => ".data",
SectionKind::Bss => ".bss",
SectionKind::ThreadData => ".tls",
SectionKind::ThreadBss => ".tls$ZZZ",
SectionKind::ThreadRoData => ".tls",
SectionKind::InitArray => ".CRT$XCA",
SectionKind::FiniArray => ".CRT$XCZ",
SectionKind::PreinitArray => ".CRT$XCA",
SectionKind::EHFrame => ".eh_fram",
SectionKind::EHFrameHdr => ".eh_fram",
SectionKind::GCCExceptTable => ".xdata",
SectionKind::PData => ".pdata",
SectionKind::XData => ".xdata",
SectionKind::DebugInfo => ".debug$S",
SectionKind::DebugLine => ".debug$S",
SectionKind::DebugFrame => ".debug$S",
SectionKind::DebugAbbrev => ".debug$S",
SectionKind::DebugStr => ".debug$S",
SectionKind::DebugAranges => ".debug$S",
SectionKind::DebugLoc => ".debug$S",
SectionKind::DebugRanges => ".debug$S",
SectionKind::DebugMacro => ".debug$S",
SectionKind::DebugAddr => ".debug$S",
SectionKind::DebugStrOffsets => ".debug$S",
SectionKind::DebugLineStr => ".debug$S",
SectionKind::DebugRngLists => ".debug$S",
SectionKind::DebugLocLists => ".debug$S",
SectionKind::DebugNames => ".debug$S",
SectionKind::DebugTypes => ".debug$T",
SectionKind::CoffDebug => ".debug$S",
SectionKind::LTO => ".llvm.lto",
SectionKind::Note => ".note",
SectionKind::GnuAttributes => ".gnu.attributes",
SectionKind::Stabs => ".stab",
SectionKind::StabsStr => ".stabstr",
SectionKind::CTF => ".ctf",
SectionKind::Custom => ".custom",
}
}
pub fn default_macho_name(&self) -> &str {
match self {
SectionKind::Text => "__text",
SectionKind::ReadOnly => "__const",
SectionKind::ReadOnlyStr => "__cstring",
SectionKind::ReadOnlyCst4 => "__literal4",
SectionKind::ReadOnlyCst8 => "__literal8",
SectionKind::ReadOnlyCst16 => "__literal16",
SectionKind::ReadOnlyCst32 => "__literal32",
SectionKind::Data => "__data",
SectionKind::Bss => "__bss",
SectionKind::ThreadData => "__thread_data",
SectionKind::ThreadBss => "__thread_bss",
SectionKind::ThreadRoData => "__thread_data",
SectionKind::InitArray => "__mod_init_func",
SectionKind::FiniArray => "__mod_term_func",
SectionKind::PreinitArray => "__mod_init_func",
SectionKind::EHFrame => "__eh_frame",
SectionKind::EHFrameHdr => "__eh_frame",
SectionKind::GCCExceptTable => "__gcc_except_tab",
SectionKind::PData => "__pdata",
SectionKind::XData => "__xdata",
SectionKind::DebugInfo => "__debug_info",
SectionKind::DebugLine => "__debug_line",
SectionKind::DebugFrame => "__debug_frame",
SectionKind::DebugAbbrev => "__debug_abbrev",
SectionKind::DebugStr => "__debug_str",
SectionKind::DebugAranges => "__debug_aranges",
SectionKind::DebugLoc => "__debug_loc",
SectionKind::DebugRanges => "__debug_ranges",
SectionKind::DebugMacro => "__debug_macro",
SectionKind::DebugAddr => "__debug_addr",
SectionKind::DebugStrOffsets => "__debug_str_offsets",
SectionKind::DebugLineStr => "__debug_line_str",
SectionKind::DebugRngLists => "__debug_rnglists",
SectionKind::DebugLocLists => "__debug_loclists",
SectionKind::DebugNames => "__debug_names",
SectionKind::DebugTypes => "__debug_types",
SectionKind::CoffDebug => "__debug",
SectionKind::LTO => "__llvm_lto",
SectionKind::Note => "__note",
SectionKind::GnuAttributes => "__gnu_attributes",
SectionKind::Stabs => "__stab",
SectionKind::StabsStr => "__stabstr",
SectionKind::CTF => "__ctf",
SectionKind::Custom => "__custom",
}
}
pub fn default_name(&self, format: TargetObjectFormat) -> &str {
match format {
TargetObjectFormat::ELF | TargetObjectFormat::Binary => self.default_elf_name(),
TargetObjectFormat::COFF | TargetObjectFormat::XCOFF => self.default_coff_name(),
TargetObjectFormat::MachO => self.default_macho_name(),
TargetObjectFormat::GOFF => self.default_elf_name(),
TargetObjectFormat::Wasm => self.default_elf_name(),
}
}
}
pub struct X86MCTargetStreamer {
pub format: TargetObjectFormat,
pub current_section: String,
pub current_section_kind: SectionKind,
pub current_offset: u64,
pub section_alignment: u32,
pub is_64bit: bool,
pub pic: bool,
pub symbols: HashMap<String, SymbolInfo>,
pub equates: HashMap<String, String>,
pub macros: HashMap<String, Vec<String>>,
pub cond_stack: Vec<ConditionalState>,
pub cfi_state: CfiState,
pub cfi_active: bool,
pub cfi_saved_states: Vec<CfiState>,
pub seh_state: SehState,
pub seh_active: bool,
pub cv_files: HashMap<u32, String>,
pub cv_funcs: HashMap<u32, String>,
pub cv_inline_sites: HashMap<u32, CvInlineInfo>,
pub section_stack: Vec<(String, SectionKind, u64)>,
pub altmacro: bool,
pub bundle_locked: bool,
pub bundle_offset: u64,
pub tls_model: TlsModel,
pub gnu_attributes: Vec<(String, String)>,
pub output_buffer: String,
pub binary_buffer: Vec<u8>,
pub fixups: Vec<FixupRecord>,
pub labels: Vec<LabelRecord>,
pub file_info: FileInfo,
pub data_count: u64,
pub instruction_count: u64,
indent_level: u32,
}
#[derive(Debug, Clone)]
pub struct SymbolInfo {
pub name: String,
pub value: u64,
pub size: u64,
pub section: String,
pub is_global: bool,
pub is_weak: bool,
pub is_local: bool,
pub is_hidden: bool,
pub is_protected: bool,
pub is_internal: bool,
pub is_function: bool,
pub is_object: bool,
pub is_tls: bool,
pub typ: String,
}
impl Default for SymbolInfo {
fn default() -> Self {
SymbolInfo {
name: String::new(),
value: 0,
size: 0,
section: String::new(),
is_global: false,
is_weak: false,
is_local: false,
is_hidden: false,
is_protected: false,
is_internal: false,
is_function: false,
is_object: false,
is_tls: false,
typ: String::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ConditionalState {
pub condition_value: bool,
pub in_true_branch: bool,
pub has_seen_else: bool,
pub skipping: bool,
}
#[derive(Debug, Clone)]
pub struct CfiState {
pub cfa_register: u32,
pub cfa_offset: i64,
pub reg_offsets: HashMap<u32, i64>,
pub is_active: bool,
}
impl Default for CfiState {
fn default() -> Self {
CfiState {
cfa_register: 0,
cfa_offset: 0,
reg_offsets: HashMap::new(),
is_active: false,
}
}
}
#[derive(Debug, Clone)]
pub struct SehState {
pub handler: Option<String>,
pub handler_data: Option<String>,
pub prologue_end: bool,
pub push_regs: Vec<u32>,
pub saveregs: Vec<(u32, u32)>,
pub setframe_reg: u32,
pub setframe_offset: u32,
pub stackalloc_size: u32,
}
impl Default for SehState {
fn default() -> Self {
SehState {
handler: None,
handler_data: None,
prologue_end: false,
push_regs: Vec::new(),
saveregs: Vec::new(),
setframe_reg: 0,
setframe_offset: 0,
stackalloc_size: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct CvInlineInfo {
pub parent_func_id: u32,
pub file_id: u32,
pub line: u32,
pub col: u32,
}
#[derive(Debug, Clone)]
pub struct FixupRecord {
pub offset: u64,
pub kind: FixupKind,
pub symbol: String,
pub addend: i64,
pub section: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FixupKind {
Abs8,
Abs16,
Abs32,
Abs64,
Rel8,
Rel16,
Rel32,
RipRel32,
GotPcRel32,
Plt32,
GotOff,
TlsGd,
TlsLd,
GotTpOff,
TpOff,
DtpOff,
GotPc32TlsDesc,
}
impl FixupKind {
pub fn size_bytes(&self) -> u8 {
match self {
FixupKind::Abs8 | FixupKind::Rel8 => 1,
FixupKind::Abs16 | FixupKind::Rel16 => 2,
FixupKind::Abs32 | FixupKind::Rel32 | FixupKind::RipRel32 => 4,
FixupKind::GotPcRel32 | FixupKind::Plt32 => 4,
FixupKind::GotOff | FixupKind::TlsGd | FixupKind::TlsLd => 4,
FixupKind::GotTpOff | FixupKind::TpOff | FixupKind::DtpOff => 4,
FixupKind::GotPc32TlsDesc => 4,
FixupKind::Abs64 => 8,
}
}
pub fn is_pc_relative(&self) -> bool {
matches!(
self,
FixupKind::Rel8
| FixupKind::Rel16
| FixupKind::Rel32
| FixupKind::RipRel32
| FixupKind::GotPcRel32
| FixupKind::Plt32
| FixupKind::GotPc32TlsDesc
)
}
}
#[derive(Debug, Clone)]
pub struct LabelRecord {
pub name: String,
pub offset: u64,
pub section: String,
pub is_local: bool,
pub is_temporary: bool,
}
#[derive(Debug, Clone)]
pub struct FileInfo {
pub filename: String,
pub directory: String,
pub file_id: u32,
pub line: u32,
pub column: u32,
pub discriminator: u32,
pub is_stmt: bool,
pub basic_block: bool,
pub prologue_end: bool,
pub epilogue_begin: bool,
}
impl Default for FileInfo {
fn default() -> Self {
FileInfo {
filename: "<unknown>".to_string(),
directory: ".".to_string(),
file_id: 0,
line: 0,
column: 0,
discriminator: 0,
is_stmt: true,
basic_block: false,
prologue_end: false,
epilogue_begin: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TlsModel {
GeneralDynamic,
LocalDynamic,
InitialExec,
LocalExec,
}
impl Default for TlsModel {
fn default() -> Self {
TlsModel::GeneralDynamic
}
}
impl X86MCTargetStreamer {
pub fn new(format: TargetObjectFormat, is_64bit: bool) -> Self {
X86MCTargetStreamer {
format,
current_section: format_text_section(&format),
current_section_kind: SectionKind::Text,
current_offset: 0,
section_alignment: 16,
is_64bit,
pic: false,
symbols: HashMap::new(),
equates: HashMap::new(),
macros: HashMap::new(),
cond_stack: Vec::new(),
cfi_state: CfiState::default(),
cfi_active: false,
cfi_saved_states: Vec::new(),
seh_state: SehState::default(),
seh_active: false,
cv_files: HashMap::new(),
cv_funcs: HashMap::new(),
cv_inline_sites: HashMap::new(),
section_stack: Vec::new(),
altmacro: false,
bundle_locked: false,
bundle_offset: 0,
tls_model: TlsModel::default(),
gnu_attributes: Vec::new(),
output_buffer: String::new(),
binary_buffer: Vec::new(),
fixups: Vec::new(),
labels: Vec::new(),
file_info: FileInfo::default(),
data_count: 0,
instruction_count: 0,
indent_level: 0,
}
}
pub fn emit_text(&mut self) {
self.switch_section_kind(SectionKind::Text);
}
pub fn emit_data_section(&mut self) {
self.switch_section_kind(SectionKind::Data);
}
pub fn emit_bss(&mut self) {
self.switch_section_kind(SectionKind::Bss);
}
pub fn emit_rodata(&mut self) {
self.switch_section_kind(SectionKind::ReadOnly);
}
pub fn emit_section(&mut self, name: &str, flags: Option<&str>, section_type: Option<&str>) {
self.current_section = name.to_string();
self.current_section_kind = SectionKind::Custom;
self.current_offset = 0;
let name_lower = name.to_lowercase();
if name_lower == ".text" || name_lower.starts_with(".text.") {
self.current_section_kind = SectionKind::Text;
} else if name_lower == ".data" || name_lower.starts_with(".data.") {
self.current_section_kind = SectionKind::Data;
} else if name_lower == ".bss" || name_lower.starts_with(".bss.") {
self.current_section_kind = SectionKind::Bss;
} else if name_lower == ".rodata" || name_lower.starts_with(".rodata.") {
self.current_section_kind = SectionKind::ReadOnly;
} else if name_lower == ".tdata" || name_lower.starts_with(".tdata.") {
self.current_section_kind = SectionKind::ThreadData;
} else if name_lower == ".tbss" || name_lower.starts_with(".tbss.") {
self.current_section_kind = SectionKind::ThreadBss;
} else if name_lower == ".eh_frame" {
self.current_section_kind = SectionKind::EHFrame;
} else if name_lower.starts_with(".debug_") {
self.match_debug_section_kind(name);
} else if name_lower == ".note" || name_lower.starts_with(".note.") {
self.current_section_kind = SectionKind::Note;
} else if name_lower == ".stab" {
self.current_section_kind = SectionKind::Stabs;
} else if name_lower == ".stabstr" {
self.current_section_kind = SectionKind::StabsStr;
} else if name_lower == ".gnu.attributes" {
self.current_section_kind = SectionKind::GnuAttributes;
}
if let Some(f) = flags {
self.section_alignment = if f.contains('a') { 16 } else { 1 };
}
}
pub fn emit_pushsection(
&mut self,
name: &str,
flags: Option<&str>,
section_type: Option<&str>,
) {
self.section_stack.push((
self.current_section.clone(),
self.current_section_kind,
self.current_offset,
));
self.emit_section(name, flags, section_type);
}
pub fn emit_popsection(&mut self) {
if let Some((name, kind, offset)) = self.section_stack.pop() {
self.current_section = name;
self.current_section_kind = kind;
self.current_offset = offset;
}
}
pub fn emit_previous(&mut self) {
self.emit_popsection();
}
pub fn emit_subsections_via_symbols(&mut self) {
}
pub fn emit_no_dead_strip(&mut self) {
}
pub fn emit_lazy_reference(&mut self, _symbol: &str) {
}
pub fn emit_reference(&mut self, _symbol: &str) {
}
pub fn emit_globl(&mut self, name: &str) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.is_global = true;
entry.section = self.current_section.clone();
}
pub fn emit_global(&mut self, name: &str) {
self.emit_globl(name);
}
pub fn emit_local(&mut self, name: &str) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.is_local = true;
}
pub fn emit_weak(&mut self, name: &str) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.is_weak = true;
}
pub fn emit_hidden(&mut self, name: &str) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.is_hidden = true;
}
pub fn emit_protected(&mut self, name: &str) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.is_protected = true;
}
pub fn emit_internal(&mut self, name: &str) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.is_internal = true;
}
pub fn emit_type(&mut self, name: &str, typ: &str) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.typ = typ.to_string();
if typ.contains("@function") || typ == "STT_FUNC" {
entry.is_function = true;
} else if typ.contains("@object") || typ == "STT_OBJECT" {
entry.is_object = true;
} else if typ.contains("@tls_object") {
entry.is_object = true;
entry.is_tls = true;
}
}
pub fn emit_size(&mut self, name: &str, size: u64) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.size = size;
}
pub fn emit_label(&mut self, name: &str) {
self.labels.push(LabelRecord {
name: name.to_string(),
offset: self.current_offset,
section: self.current_section.clone(),
is_local: name.starts_with(".L"),
is_temporary: name.starts_with(".Ltmp"),
});
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.value = self.current_offset;
entry.section = self.current_section.clone();
}
pub fn emit_align(&mut self, alignment: u32, fill: Option<u8>, max: Option<u32>) {
let align_val = if alignment == 0 { 1 } else { alignment };
let fill_byte = fill.unwrap_or(0x90); let max_bytes = max.unwrap_or(0);
let mask = align_val as u64 - 1;
let padding = if self.current_offset & mask == 0 {
0
} else {
align_val as u64 - (self.current_offset & mask)
};
if max_bytes > 0 && padding > max_bytes as u64 {
return;
}
self.emit_fill_bytes(padding, fill_byte);
self.section_alignment = self.section_alignment.max(align_val);
}
pub fn emit_p2align(&mut self, alignment: u32, fill: Option<u8>, max: Option<u32>) {
let align_val = 1u32 << alignment;
self.emit_align(align_val, fill, max);
}
pub fn emit_balign(&mut self, alignment: u32, fill: Option<u8>) {
self.emit_align(alignment, fill, Some(alignment));
}
pub fn emit_org(&mut self, address: u64, fill: Option<u8>) {
if address < self.current_offset {
return;
}
let fill_byte = fill.unwrap_or(0);
let padding = address - self.current_offset;
if padding > 0 {
self.emit_fill_bytes(padding, fill_byte);
}
self.current_offset = address;
}
pub fn emit_fill(&mut self, count: u64, value: &[u8], size: u8) {
let total = count * size as u64;
for _ in 0..count {
self.emit_raw_bytes(value);
}
self.data_count += total;
}
pub fn emit_byte(&mut self, values: &[u8]) {
self.emit_raw_bytes(values);
self.data_count += values.len() as u64;
}
pub fn emit_short(&mut self, values: &[i16]) {
for &v in values {
let bytes = v.to_le_bytes();
self.emit_raw_bytes(&bytes);
}
self.data_count += (values.len() * 2) as u64;
}
pub fn emit_value(&mut self, values: &[i16]) {
self.emit_short(values);
}
pub fn emit_2byte(&mut self, values: &[i16]) {
self.emit_short(values);
}
pub fn emit_long(&mut self, values: &[i32]) {
for &v in values {
let bytes = v.to_le_bytes();
self.emit_raw_bytes(&bytes);
}
self.data_count += (values.len() * 4) as u64;
}
pub fn emit_4byte(&mut self, values: &[i32]) {
self.emit_long(values);
}
pub fn emit_quad(&mut self, values: &[i64]) {
for &v in values {
let bytes = v.to_le_bytes();
self.emit_raw_bytes(&bytes);
}
self.data_count += (values.len() * 8) as u64;
}
pub fn emit_8byte(&mut self, values: &[i64]) {
self.emit_quad(values);
}
pub fn emit_octa(&mut self, values: &[u128]) {
for &v in values {
let bytes = v.to_le_bytes();
self.emit_raw_bytes(&bytes);
}
self.data_count += (values.len() * 16) as u64;
}
pub fn emit_ascii(&mut self, s: &str) {
self.emit_raw_bytes(s.as_bytes());
self.data_count += s.len() as u64;
}
pub fn emit_asciz(&mut self, s: &str) {
self.emit_raw_bytes(s.as_bytes());
self.emit_raw_bytes(&[0]);
self.data_count += (s.len() + 1) as u64;
}
pub fn emit_string(&mut self, s: &str) {
self.emit_asciz(s);
}
pub fn emit_space(&mut self, size: u64, fill: Option<u8>) {
let fill_byte = fill.unwrap_or(0);
self.emit_fill_bytes(size, fill_byte);
}
pub fn emit_skip(&mut self, size: u64, fill: Option<u8>) {
self.emit_space(size, fill);
}
pub fn emit_zero(&mut self, size: u64) {
self.emit_space(size, Some(0));
}
pub fn emit_comm(&mut self, name: &str, size: u64, alignment: Option<u32>) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.size = size;
entry.section = "*COM*".to_string();
entry.is_global = true;
}
pub fn emit_lcomm(&mut self, name: &str, size: u64, alignment: Option<u32>) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.size = size;
entry.section = self.current_section.clone();
entry.is_local = true;
}
pub fn emit_common(&mut self, name: &str, size: u64, alignment: Option<u32>) {
self.emit_comm(name, size, alignment);
}
pub fn emit_equ(&mut self, name: &str, value: &str) {
self.equates.insert(name.to_string(), value.to_string());
if let Ok(v) = value.parse::<u64>() {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.value = v;
} else if value.starts_with("0x") || value.starts_with("0X") {
if let Ok(v) = u64::from_str_radix(&value[2..], 16) {
let entry = self.symbols.entry(name.to_string()).or_default();
entry.name = name.to_string();
entry.value = v;
}
}
}
pub fn emit_set(&mut self, name: &str, value: &str) {
self.emit_equ(name, value);
}
pub fn emit_file(&mut self, file_id: u32, filename: &str) {
self.file_info.file_id = file_id;
self.file_info.filename = filename.to_string();
}
pub fn emit_loc(&mut self, file_id: u32, line: u32, column: u32) {
self.file_info.file_id = file_id;
self.file_info.line = line;
self.file_info.column = column;
self.file_info.is_stmt = true;
}
pub fn emit_line(&mut self, line: u32) {
self.file_info.line = line;
}
pub fn emit_ident(&mut self, ident: &str) {
let old_section = self.current_section.clone();
let old_kind = self.current_section_kind;
let old_offset = self.current_offset;
self.current_section = ".comment".to_string();
self.current_section_kind = SectionKind::Custom;
self.current_offset = 0;
self.emit_asciz(ident);
self.current_section = old_section;
self.current_section_kind = old_kind;
self.current_offset = old_offset;
}
pub fn emit_cfi_startproc(&mut self) {
self.cfi_active = true;
self.cfi_state = CfiState::default();
self.cfi_state.is_active = true;
}
pub fn emit_cfi_endproc(&mut self) {
self.cfi_active = false;
self.cfi_state.is_active = false;
}
pub fn emit_cfi_def_cfa(&mut self, reg: u32, offset: i64) {
self.cfi_state.cfa_register = reg;
self.cfi_state.cfa_offset = offset;
}
pub fn emit_cfi_offset(&mut self, reg: u32, offset: i64) {
self.cfi_state.reg_offsets.insert(reg, offset);
}
pub fn emit_cfi_def_cfa_register(&mut self, reg: u32) {
self.cfi_state.cfa_register = reg;
}
pub fn emit_cfi_def_cfa_offset(&mut self, offset: i64) {
self.cfi_state.cfa_offset = offset;
}
pub fn emit_cfi_restore(&mut self, reg: u32) {
self.cfi_state.reg_offsets.remove(®);
}
pub fn emit_cfi_remember_state(&mut self) {
let saved = self.cfi_state.clone();
self.cfi_saved_states.push(saved);
}
pub fn emit_cfi_restore_state(&mut self) {
if let Some(saved) = self.cfi_saved_states.pop() {
self.cfi_state = saved;
}
}
pub fn emit_cfi_escape(&mut self, values: &[u8]) {
self.emit_raw_bytes(values);
}
pub fn emit_seh_proc(&mut self, _name: &str) {
if self.format.supports_seh() {
self.seh_active = true;
self.seh_state = SehState::default();
}
}
pub fn emit_seh_endproc(&mut self) {
if self.format.supports_seh() {
self.seh_active = false;
}
}
pub fn emit_seh_endprologue(&mut self) {
if self.format.supports_seh() {
self.seh_state.prologue_end = true;
}
}
pub fn emit_seh_pushreg(&mut self, reg: u32) {
if self.format.supports_seh() {
self.seh_state.push_regs.push(reg);
}
}
pub fn emit_seh_setframe(&mut self, reg: u32, offset: u32) {
if self.format.supports_seh() {
self.seh_state.setframe_reg = reg;
self.seh_state.setframe_offset = offset;
}
}
pub fn emit_seh_stackalloc(&mut self, size: u32) {
if self.format.supports_seh() {
self.seh_state.stackalloc_size = size;
}
}
pub fn emit_seh_savereg(&mut self, reg: u32, offset: u32) {
if self.format.supports_seh() {
self.seh_state.saveregs.push((reg, offset));
}
}
pub fn emit_seh_pushframe(&mut self, _code: u32) {
if self.format.supports_seh() {
}
}
pub fn emit_seh_handler(&mut self, handler: &str, data: Option<&str>) {
if self.format.supports_seh() {
self.seh_state.handler = Some(handler.to_string());
self.seh_state.handler_data = data.map(|s| s.to_string());
}
}
pub fn emit_cv_file(&mut self, file_id: u32, filename: &str) {
if self.format.supports_codeview() {
self.cv_files.insert(file_id, filename.to_string());
}
}
pub fn emit_cv_func_id(&mut self, func_id: u32, name: &str) {
if self.format.supports_codeview() {
self.cv_funcs.insert(func_id, name.to_string());
}
}
pub fn emit_cv_loc(&mut self, func_id: u32, file_id: u32, line: u32, col: u32) {
if self.format.supports_codeview() {
self.file_info.file_id = file_id;
self.file_info.line = line;
self.file_info.column = col;
}
}
pub fn emit_cv_inline_site_id(
&mut self,
site_id: u32,
parent_func_id: u32,
file_id: u32,
line: u32,
col: u32,
) {
if self.format.supports_codeview() {
self.cv_inline_sites.insert(
site_id,
CvInlineInfo {
parent_func_id,
file_id,
line,
col,
},
);
}
}
pub fn emit_macro_begin(&mut self, name: &str, _args: &[&str]) {
self.macros.insert(name.to_string(), Vec::new());
}
pub fn emit_macro_line(&mut self, name: &str, line: &str) {
if let Some(body) = self.macros.get_mut(name) {
body.push(line.to_string());
}
}
pub fn emit_endm(&mut self) {
}
pub fn emit_rept(&mut self, _count: u32) {
}
pub fn emit_endr(&mut self) {
}
pub fn emit_irp(&mut self, _param: &str, _values: &[&str]) {
}
pub fn emit_irpc(&mut self, _param: &str, _chars: &str) {
}
fn push_conditional(&mut self, condition_value: bool) {
let skipping = if let Some(parent) = self.cond_stack.last() {
parent.skipping || !condition_value
} else {
!condition_value
};
self.cond_stack.push(ConditionalState {
condition_value,
in_true_branch: condition_value && !skipping,
has_seen_else: false,
skipping,
});
}
fn is_skipping(&self) -> bool {
self.cond_stack.last().map(|c| c.skipping).unwrap_or(false)
}
pub fn emit_if(&mut self, condition: &str) {
let value = self.evaluate_numeric_condition(condition);
self.push_conditional(value);
}
pub fn emit_ifdef(&mut self, name: &str) {
let defined = self.symbols.contains_key(name) || self.equates.contains_key(name);
self.push_conditional(defined);
}
pub fn emit_ifndef(&mut self, name: &str) {
let defined = self.symbols.contains_key(name) || self.equates.contains_key(name);
self.push_conditional(!defined);
}
pub fn emit_ifeq(&mut self, a: &str, b: &str) {
self.push_conditional(a.trim() == b.trim());
}
pub fn emit_ifne(&mut self, a: &str, b: &str) {
self.push_conditional(a.trim() != b.trim());
}
pub fn emit_iflt(&mut self, a: &str, b: &str) {
let result = self.compare_expressions(a, b);
self.push_conditional(result == std::cmp::Ordering::Less);
}
pub fn emit_ifle(&mut self, a: &str, b: &str) {
let result = self.compare_expressions(a, b);
self.push_conditional(
result == std::cmp::Ordering::Less || result == std::cmp::Ordering::Equal,
);
}
pub fn emit_ifgt(&mut self, a: &str, b: &str) {
let result = self.compare_expressions(a, b);
self.push_conditional(result == std::cmp::Ordering::Greater);
}
pub fn emit_ifge(&mut self, a: &str, b: &str) {
let result = self.compare_expressions(a, b);
self.push_conditional(
result == std::cmp::Ordering::Greater || result == std::cmp::Ordering::Equal,
);
}
pub fn emit_else(&mut self) {
if let Some(state) = self.cond_stack.last_mut() {
if state.has_seen_else {
return;
}
state.has_seen_else = true;
state.skipping = !state.skipping;
}
}
pub fn emit_endif(&mut self) {
self.cond_stack.pop();
}
pub fn emit_include(&mut self, _filename: &str) {
}
pub fn emit_incbin(&mut self, _filename: &str, _skip: Option<u64>, _count: Option<u64>) {
}
pub fn emit_error(&mut self, message: &str) {
}
pub fn emit_warning(&mut self, message: &str) {
}
pub fn emit_print(&mut self, message: &str) {
}
pub fn emit_bundle_lock(&mut self) {
self.bundle_locked = true;
self.bundle_offset = self.current_offset;
}
pub fn emit_bundle_unlock(&mut self) {
self.bundle_locked = false;
}
pub fn emit_tls_model(&mut self, model: &str) {
self.tls_model = match model {
"general-dynamic" | "global-dynamic" => TlsModel::GeneralDynamic,
"local-dynamic" => TlsModel::LocalDynamic,
"initial-exec" => TlsModel::InitialExec,
"local-exec" => TlsModel::LocalExec,
_ => TlsModel::GeneralDynamic,
};
}
pub fn emit_gnu_attribute(&mut self, tag: &str, value: &str) {
self.gnu_attributes
.push((tag.to_string(), value.to_string()));
}
pub fn emit_note(&mut self, _name: &str, _note_type: u32, _desc: &[u8]) {
}
pub fn emit_stab(&mut self, _type_: u8, _other: u8, _desc: u16, _value: u32) {
}
pub fn emit_stabs(&mut self, _s: &str) {
}
pub fn emit_stabn(&mut self, _type_: u8, _other: u8, _desc: u16, _value: u32) {
}
pub fn emit_altmacro(&mut self) {
self.altmacro = true;
}
pub fn emit_noaltmacro(&mut self) {
self.altmacro = false;
}
fn switch_section_kind(&mut self, kind: SectionKind) {
self.current_section_kind = kind;
self.current_section = kind.default_name(self.format).to_string();
self.current_offset = 0;
}
fn match_debug_section_kind(&mut self, name: &str) {
let name_lower = name.to_lowercase();
self.current_section_kind = match name_lower.as_str() {
".debug_info" => SectionKind::DebugInfo,
".debug_line" => SectionKind::DebugLine,
".debug_frame" => SectionKind::DebugFrame,
".debug_abbrev" => SectionKind::DebugAbbrev,
".debug_str" => SectionKind::DebugStr,
".debug_aranges" => SectionKind::DebugAranges,
".debug_loc" => SectionKind::DebugLoc,
".debug_ranges" => SectionKind::DebugRanges,
".debug_macro" => SectionKind::DebugMacro,
".debug_addr" => SectionKind::DebugAddr,
".debug_str_offsets" => SectionKind::DebugStrOffsets,
".debug_line_str" => SectionKind::DebugLineStr,
".debug_rnglists" => SectionKind::DebugRngLists,
".debug_loclists" => SectionKind::DebugLocLists,
".debug_names" => SectionKind::DebugNames,
".debug_types" => SectionKind::DebugTypes,
_ => SectionKind::DebugInfo,
};
}
fn emit_raw_bytes(&mut self, bytes: &[u8]) {
self.binary_buffer.extend_from_slice(bytes);
self.current_offset += bytes.len() as u64;
}
fn emit_fill_bytes(&mut self, count: u64, value: u8) {
for _ in 0..count {
self.binary_buffer.push(value);
}
self.current_offset += count;
}
fn evaluate_numeric_condition(&self, expr: &str) -> bool {
let expr = expr.trim();
let mut result = expr.to_string();
for (name, value) in &self.equates {
result = result.replace(name.as_str(), value.as_str());
}
if let Ok(v) = result.parse::<i64>() {
return v != 0;
}
if result.contains("==") {
let parts: Vec<&str> = result.split("==").collect();
if parts.len() == 2 {
return parts[0].trim() == parts[1].trim();
}
}
if result.contains("!=") {
let parts: Vec<&str> = result.split("!=").collect();
if parts.len() == 2 {
return parts[0].trim() != parts[1].trim();
}
}
if result.contains(">=") {
let parts: Vec<&str> = result.split(">=").collect();
if parts.len() == 2 {
if let (Ok(a), Ok(b)) = (
parts[0].trim().parse::<i64>(),
parts[1].trim().parse::<i64>(),
) {
return a >= b;
}
}
}
if result.contains("<=") {
let parts: Vec<&str> = result.split("<=").collect();
if parts.len() == 2 {
if let (Ok(a), Ok(b)) = (
parts[0].trim().parse::<i64>(),
parts[1].trim().parse::<i64>(),
) {
return a <= b;
}
}
}
if result.contains('>') {
let parts: Vec<&str> = result.split('>').collect();
if parts.len() == 2 {
if let (Ok(a), Ok(b)) = (
parts[0].trim().parse::<i64>(),
parts[1].trim().parse::<i64>(),
) {
return a > b;
}
}
}
if result.contains('<') {
let parts: Vec<&str> = result.split('<').collect();
if parts.len() == 2 {
if let (Ok(a), Ok(b)) = (
parts[0].trim().parse::<i64>(),
parts[1].trim().parse::<i64>(),
) {
return a < b;
}
}
}
false
}
fn compare_expressions(&self, a: &str, b: &str) -> std::cmp::Ordering {
let resolve = |s: &str| -> Option<i64> {
if let Some(val) = self.equates.get(s) {
val.parse::<i64>().ok()
} else {
s.trim().parse::<i64>().ok()
}
};
match (resolve(a), resolve(b)) {
(Some(va), Some(vb)) => va.cmp(&vb),
_ => a.trim().cmp(b.trim()),
}
}
pub fn add_fixup(&mut self, offset: u64, kind: FixupKind, symbol: &str, addend: i64) {
self.fixups.push(FixupRecord {
offset,
kind,
symbol: symbol.to_string(),
addend,
section: self.current_section.clone(),
});
}
pub fn reset(&mut self) {
self.current_section = format_text_section(&self.format);
self.current_section_kind = SectionKind::Text;
self.current_offset = 0;
self.section_alignment = 16;
self.symbols.clear();
self.equates.clear();
self.macros.clear();
self.cond_stack.clear();
self.cfi_state = CfiState::default();
self.cfi_active = false;
self.cfi_saved_states.clear();
self.seh_state = SehState::default();
self.seh_active = false;
self.cv_files.clear();
self.cv_funcs.clear();
self.cv_inline_sites.clear();
self.section_stack.clear();
self.altmacro = false;
self.bundle_locked = false;
self.bundle_offset = 0;
self.tls_model = TlsModel::default();
self.gnu_attributes.clear();
self.output_buffer.clear();
self.binary_buffer.clear();
self.fixups.clear();
self.labels.clear();
self.file_info = FileInfo::default();
self.data_count = 0;
self.instruction_count = 0;
self.indent_level = 0;
}
pub fn text_section_name(&self) -> &str {
SectionKind::Text.default_name(self.format)
}
pub fn data_section_name(&self) -> &str {
SectionKind::Data.default_name(self.format)
}
pub fn rodata_section_name(&self) -> &str {
SectionKind::ReadOnly.default_name(self.format)
}
pub fn bss_section_name(&self) -> &str {
SectionKind::Bss.default_name(self.format)
}
pub fn gotpcrel_fixup_kind(&self) -> FixupKind {
FixupKind::GotPcRel32
}
}
pub struct X86TargetAsmStreamer {
pub streamer: X86MCTargetStreamer,
pub output: String,
indent: String,
pub verbose: bool,
pub show_encoding: bool,
line_count: u32,
directive_count: u64,
}
impl X86TargetAsmStreamer {
pub fn new(format: TargetObjectFormat, is_64bit: bool) -> Self {
X86TargetAsmStreamer {
streamer: X86MCTargetStreamer::new(format, is_64bit),
output: String::new(),
indent: "\t".to_string(),
verbose: false,
show_encoding: false,
line_count: 0,
directive_count: 0,
}
}
pub fn from_triple(triple: &str) -> Self {
let is_64bit = triple.contains("x86_64") || triple.contains("amd64");
let format = if triple.contains("windows")
|| triple.contains("mingw")
|| triple.contains("msvc")
{
TargetObjectFormat::COFF
} else if triple.contains("apple") || triple.contains("darwin") || triple.contains("macos")
{
TargetObjectFormat::MachO
} else {
TargetObjectFormat::ELF
};
Self::new(format, is_64bit)
}
fn comment_char(&self) -> char {
self.streamer.format.comment_char()
}
fn directive(&self) -> &str {
self.streamer.format.directive_prefix()
}
fn emit_line(&mut self, line: &str) {
self.output.push_str(line);
self.output.push('\n');
self.line_count += 1;
}
fn emit_indented(&mut self, line: &str) {
self.output.push_str(&self.indent);
self.output.push_str(line);
self.output.push('\n');
self.line_count += 1;
}
fn emit_comment_line(&mut self, comment: &str) {
self.output
.push_str(&format!("{} {}", self.comment_char(), comment));
self.output.push('\n');
self.line_count += 1;
}
fn emit_directive(&mut self, dir: &str, args: &str) {
self.directive_count += 1;
if args.is_empty() {
self.emit_line(&format!("{}{}", self.directive(), dir));
} else {
self.emit_line(&format!("{}{} {}", self.directive(), dir, args));
}
}
fn emit_label_line(&mut self, label: &str) {
self.emit_line(&format!("{}:", label));
}
pub fn emit_asm_text(&mut self) {
self.emit_directive("text", "");
self.streamer.emit_text();
}
pub fn emit_asm_data(&mut self) {
self.emit_directive("data", "");
self.streamer.emit_data_section();
}
pub fn emit_asm_bss(&mut self) {
self.emit_directive("bss", "");
self.streamer.emit_bss();
}
pub fn emit_asm_rodata(&mut self) {
if self.streamer.format == TargetObjectFormat::MachO {
self.emit_directive("section", "__TEXT,__const");
} else if self.streamer.format == TargetObjectFormat::COFF {
self.emit_directive("section", ".rdata");
} else {
self.emit_directive("section", ".rodata");
}
self.streamer.emit_rodata();
}
pub fn emit_asm_section(&mut self, name: &str, flags: Option<&str>, typ: Option<&str>) {
let mut args = name.to_string();
if let Some(f) = flags {
args.push_str(&format!(",\"{}\"", f));
}
if let Some(t) = typ {
args.push_str(&format!(",@{}", t));
}
self.emit_directive("section", &args);
self.streamer.emit_section(name, flags, typ);
}
pub fn emit_asm_pushsection(&mut self, name: &str, flags: Option<&str>, typ: Option<&str>) {
let mut args = name.to_string();
if let Some(f) = flags {
args.push_str(&format!(",\"{}\"", f));
}
if let Some(t) = typ {
args.push_str(&format!(",@{}", t));
}
self.emit_directive("pushsection", &args);
self.streamer.emit_pushsection(name, flags, typ);
}
pub fn emit_asm_popsection(&mut self) {
self.emit_directive("popsection", "");
self.streamer.emit_popsection();
}
pub fn emit_asm_previous(&mut self) {
self.emit_directive("previous", "");
self.streamer.emit_previous();
}
pub fn emit_asm_globl(&mut self, name: &str) {
self.emit_directive("globl", name);
self.streamer.emit_globl(name);
}
pub fn emit_asm_global(&mut self, name: &str) {
self.emit_directive("global", name);
self.streamer.emit_global(name);
}
pub fn emit_asm_local(&mut self, name: &str) {
self.emit_directive("local", name);
self.streamer.emit_local(name);
}
pub fn emit_asm_weak(&mut self, name: &str) {
self.emit_directive("weak", name);
self.streamer.emit_weak(name);
}
pub fn emit_asm_hidden(&mut self, name: &str) {
self.emit_directive("hidden", name);
self.streamer.emit_hidden(name);
}
pub fn emit_asm_protected(&mut self, name: &str) {
self.emit_directive("protected", name);
self.streamer.emit_protected(name);
}
pub fn emit_asm_internal(&mut self, name: &str) {
self.emit_directive("internal", name);
self.streamer.emit_internal(name);
}
pub fn emit_asm_type(&mut self, name: &str, typ: &str) {
self.emit_directive("type", &format!("{}, {}", name, typ));
self.streamer.emit_type(name, typ);
}
pub fn emit_asm_size(&mut self, name: &str, size: u64) {
self.emit_directive("size", &format!("{}, {}", name, size));
self.streamer.emit_size(name, size);
}
pub fn emit_asm_comm(&mut self, name: &str, size: u64, alignment: Option<u32>) {
match alignment {
Some(a) => self.emit_directive("comm", &format!("{},{},{}", name, size, a)),
None => self.emit_directive("comm", &format!("{},{}", name, size)),
}
self.streamer.emit_comm(name, size, alignment);
}
pub fn emit_asm_lcomm(&mut self, name: &str, size: u64, alignment: Option<u32>) {
match alignment {
Some(a) => self.emit_directive("lcomm", &format!("{},{},{}", name, size, a)),
None => self.emit_directive("lcomm", &format!("{},{}", name, size)),
}
self.streamer.emit_lcomm(name, size, alignment);
}
pub fn emit_asm_equ(&mut self, name: &str, value: &str) {
self.emit_directive("equ", &format!("{}, {}", name, value));
self.streamer.emit_equ(name, value);
}
pub fn emit_asm_set(&mut self, name: &str, value: &str) {
self.emit_directive("set", &format!("{}, {}", name, value));
self.streamer.emit_set(name, value);
}
pub fn emit_asm_byte(&mut self, values: &[u8]) {
let args: Vec<String> = values.iter().map(|v| format!("{}", v)).collect();
self.emit_directive("byte", &args.join(", "));
self.streamer.emit_byte(values);
}
pub fn emit_asm_short(&mut self, values: &[i16]) {
let args: Vec<String> = values.iter().map(|v| format!("{}", v)).collect();
self.emit_directive("short", &args.join(", "));
self.streamer.emit_short(values);
}
pub fn emit_asm_long(&mut self, values: &[i32]) {
let args: Vec<String> = values.iter().map(|v| format!("{}", v)).collect();
self.emit_directive("long", &args.join(", "));
self.streamer.emit_long(values);
}
pub fn emit_asm_quad(&mut self, values: &[i64]) {
let args: Vec<String> = values.iter().map(|v| format!("{}", v)).collect();
self.emit_directive("quad", &args.join(", "));
self.streamer.emit_quad(values);
}
pub fn emit_asm_octa(&mut self, values: &[u128]) {
let args: Vec<String> = values.iter().map(|v| format!("0x{:032x}", v)).collect();
self.emit_directive("octa", &args.join(", "));
self.streamer.emit_octa(values);
}
pub fn emit_asm_value(&mut self, values: &[i16]) {
self.emit_asm_short(values);
}
pub fn emit_asm_2byte(&mut self, values: &[i16]) {
self.emit_asm_short(values);
}
pub fn emit_asm_4byte(&mut self, values: &[i32]) {
self.emit_asm_long(values);
}
pub fn emit_asm_8byte(&mut self, values: &[i64]) {
self.emit_asm_quad(values);
}
pub fn emit_asm_ascii(&mut self, s: &str) {
let escaped = s
.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\t', "\\t");
self.emit_directive("ascii", &format!("\"{}\"", escaped));
self.streamer.emit_ascii(s);
}
pub fn emit_asm_asciz(&mut self, s: &str) {
let escaped = s
.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\t', "\\t");
self.emit_directive("asciz", &format!("\"{}\"", escaped));
self.streamer.emit_asciz(s);
}
pub fn emit_asm_string(&mut self, s: &str) {
let escaped = s
.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\t', "\\t");
self.emit_directive("string", &format!("\"{}\"", escaped));
self.streamer.emit_string(s);
}
pub fn emit_asm_space(&mut self, size: u64, fill: Option<u8>) {
match fill {
Some(f) => self.emit_directive("space", &format!("{}, {}", size, f)),
None => self.emit_directive("space", &format!("{}", size)),
}
self.streamer.emit_space(size, fill);
}
pub fn emit_asm_skip(&mut self, size: u64, fill: Option<u8>) {
match fill {
Some(f) => self.emit_directive("skip", &format!("{}, {}", size, f)),
None => self.emit_directive("skip", &format!("{}", size)),
}
self.streamer.emit_skip(size, fill);
}
pub fn emit_asm_zero(&mut self, size: u64) {
self.emit_directive("zero", &format!("{}", size));
self.streamer.emit_zero(size);
}
pub fn emit_asm_align(&mut self, alignment: u32, fill: Option<u8>, max: Option<u32>) {
let mut args = format!("{}", alignment);
if let Some(f) = fill {
args.push_str(&format!(", {}", f));
}
if let Some(m) = max {
args.push_str(&format!(", {}", m));
}
self.emit_directive("align", &args);
self.streamer.emit_align(alignment, fill, max);
}
pub fn emit_asm_p2align(&mut self, alignment: u32, fill: Option<u8>, max: Option<u32>) {
let mut args = format!("{}", alignment);
if let Some(f) = fill {
args.push_str(&format!(", {}", f));
}
if let Some(m) = max {
args.push_str(&format!(", {}", m));
}
self.emit_directive("p2align", &args);
self.streamer.emit_p2align(alignment, fill, max);
}
pub fn emit_asm_balign(&mut self, alignment: u32, fill: Option<u8>) {
match fill {
Some(f) => self.emit_directive("balign", &format!("{}, {}", alignment, f)),
None => self.emit_directive("balign", &format!("{}", alignment)),
}
self.streamer.emit_balign(alignment, fill);
}
pub fn emit_asm_org(&mut self, address: u64, fill: Option<u8>) {
match fill {
Some(f) => self.emit_directive("org", &format!("{}, {}", address, f)),
None => self.emit_directive("org", &format!("{}", address)),
}
self.streamer.emit_org(address, fill);
}
pub fn emit_asm_fill(&mut self, count: u64, value: &[u8], size: u8) {
let val_str = value
.iter()
.map(|b| format!("{}", b))
.collect::<Vec<_>>()
.join(",");
self.emit_directive("fill", &format!("{}, {}, {}", count, val_str, size));
self.streamer.emit_fill(count, value, size);
}
pub fn emit_asm_cfi_startproc(&mut self) {
self.emit_directive("cfi_startproc", "");
self.streamer.emit_cfi_startproc();
}
pub fn emit_asm_cfi_endproc(&mut self) {
self.emit_directive("cfi_endproc", "");
self.streamer.emit_cfi_endproc();
}
pub fn emit_asm_cfi_def_cfa(&mut self, reg: u32, offset: i64) {
self.emit_directive("cfi_def_cfa", &format!("{}, {}", reg, offset));
self.streamer.emit_cfi_def_cfa(reg, offset);
}
pub fn emit_asm_cfi_offset(&mut self, reg: u32, offset: i64) {
self.emit_directive("cfi_offset", &format!("{}, {}", reg, offset));
self.streamer.emit_cfi_offset(reg, offset);
}
pub fn emit_asm_cfi_def_cfa_register(&mut self, reg: u32) {
self.emit_directive("cfi_def_cfa_register", &format!("{}", reg));
self.streamer.emit_cfi_def_cfa_register(reg);
}
pub fn emit_asm_cfi_def_cfa_offset(&mut self, offset: i64) {
self.emit_directive("cfi_def_cfa_offset", &format!("{}", offset));
self.streamer.emit_cfi_def_cfa_offset(offset);
}
pub fn emit_asm_cfi_restore(&mut self, reg: u32) {
self.emit_directive("cfi_restore", &format!("{}", reg));
self.streamer.emit_cfi_restore(reg);
}
pub fn emit_asm_cfi_remember_state(&mut self) {
self.emit_directive("cfi_remember_state", "");
self.streamer.emit_cfi_remember_state();
}
pub fn emit_asm_cfi_restore_state(&mut self) {
self.emit_directive("cfi_restore_state", "");
self.streamer.emit_cfi_restore_state();
}
pub fn emit_asm_cfi_escape(&mut self, values: &[u8]) {
let hex: Vec<String> = values.iter().map(|b| format!("0x{:02x}", b)).collect();
self.emit_directive("cfi_escape", &hex.join(", "));
self.streamer.emit_cfi_escape(values);
}
pub fn emit_asm_seh_proc(&mut self, name: &str) {
self.emit_directive("seh_proc", name);
self.streamer.emit_seh_proc(name);
}
pub fn emit_asm_seh_endproc(&mut self) {
self.emit_directive("seh_endproc", "");
self.streamer.emit_seh_endproc();
}
pub fn emit_asm_seh_endprologue(&mut self) {
self.emit_directive("seh_endprologue", "");
self.streamer.emit_seh_endprologue();
}
pub fn emit_asm_seh_pushreg(&mut self, reg: u32) {
self.emit_directive("seh_pushreg", &format!("{}", reg));
self.streamer.emit_seh_pushreg(reg);
}
pub fn emit_asm_seh_setframe(&mut self, reg: u32, offset: u32) {
self.emit_directive("seh_setframe", &format!("{}, {}", reg, offset));
self.streamer.emit_seh_setframe(reg, offset);
}
pub fn emit_asm_seh_stackalloc(&mut self, size: u32) {
self.emit_directive("seh_stackalloc", &format!("{}", size));
self.streamer.emit_seh_stackalloc(size);
}
pub fn emit_asm_seh_savereg(&mut self, reg: u32, offset: u32) {
self.emit_directive("seh_savereg", &format!("{}, {}", reg, offset));
self.streamer.emit_seh_savereg(reg, offset);
}
pub fn emit_asm_seh_pushframe(&mut self, code: u32) {
self.emit_directive("seh_pushframe", &format!("{}", code));
self.streamer.emit_seh_pushframe(code);
}
pub fn emit_asm_seh_handler(&mut self, handler: &str, data: Option<&str>) {
match data {
Some(d) => self.emit_directive("seh_handler", &format!("{}, {}", handler, d)),
None => self.emit_directive("seh_handler", handler),
}
self.streamer.emit_seh_handler(handler, data);
}
pub fn emit_asm_cv_file(&mut self, file_id: u32, filename: &str) {
self.emit_directive("cv_file", &format!("{}, \"{}\"", file_id, filename));
self.streamer.emit_cv_file(file_id, filename);
}
pub fn emit_asm_cv_func_id(&mut self, func_id: u32, name: &str) {
self.emit_directive("cv_func_id", &format!("{}, {}", func_id, name));
self.streamer.emit_cv_func_id(func_id, name);
}
pub fn emit_asm_cv_loc(&mut self, func_id: u32, file_id: u32, line: u32, col: u32) {
self.emit_directive(
"cv_loc",
&format!("{}, {}, {}, {}", func_id, file_id, line, col),
);
self.streamer.emit_cv_loc(func_id, file_id, line, col);
}
pub fn emit_asm_cv_inline_site_id(
&mut self,
site_id: u32,
parent_func_id: u32,
file_id: u32,
line: u32,
col: u32,
) {
self.emit_directive(
"cv_inline_site_id",
&format!(
"{}, {}, {}, {}, {}",
site_id, parent_func_id, file_id, line, col
),
);
self.streamer
.emit_cv_inline_site_id(site_id, parent_func_id, file_id, line, col);
}
pub fn emit_asm_file(&mut self, file_id: u32, filename: &str) {
self.emit_directive("file", &format!("{} \"{}\"", file_id, filename));
self.streamer.emit_file(file_id, filename);
}
pub fn emit_asm_loc(&mut self, file_id: u32, line: u32, column: u32) {
let mut args = format!("{} {} {}", file_id, line, column);
if !self.streamer.file_info.is_stmt {
args.push_str(" is_stmt 0");
}
if self.streamer.file_info.prologue_end {
args.push_str(" prologue_end");
}
if self.streamer.file_info.epilogue_begin {
args.push_str(" epilogue_begin");
}
self.emit_directive("loc", &args);
self.streamer.emit_loc(file_id, line, column);
}
pub fn emit_asm_line(&mut self, line: u32) {
self.emit_directive("line", &format!("{}", line));
self.streamer.emit_line(line);
}
pub fn emit_asm_ident(&mut self, ident: &str) {
self.emit_directive("ident", &format!("\"{}\"", ident));
self.streamer.emit_ident(ident);
}
pub fn emit_asm_macro_begin(&mut self, name: &str, args: &[&str]) {
self.emit_directive("macro", &format!("{} {}", name, args.join(", ")));
self.streamer.emit_macro_begin(name, args);
}
pub fn emit_asm_endm(&mut self) {
self.emit_directive("endm", "");
self.streamer.emit_endm();
}
pub fn emit_asm_rept(&mut self, count: u32) {
self.emit_directive("rept", &format!("{}", count));
self.streamer.emit_rept(count);
}
pub fn emit_asm_endr(&mut self) {
self.emit_directive("endr", "");
self.streamer.emit_endr();
}
pub fn emit_asm_irp(&mut self, param: &str, values: &[&str]) {
self.emit_directive("irp", &format!("{}, {}", param, values.join(",")));
self.streamer.emit_irp(param, values);
}
pub fn emit_asm_irpc(&mut self, param: &str, chars: &str) {
self.emit_directive("irpc", &format!("{}, {}", param, chars));
self.streamer.emit_irpc(param, chars);
}
pub fn emit_asm_if(&mut self, condition: &str) {
self.emit_directive("if", condition);
self.streamer.emit_if(condition);
}
pub fn emit_asm_ifdef(&mut self, name: &str) {
self.emit_directive("ifdef", name);
self.streamer.emit_ifdef(name);
}
pub fn emit_asm_ifndef(&mut self, name: &str) {
self.emit_directive("ifndef", name);
self.streamer.emit_ifndef(name);
}
pub fn emit_asm_ifeq(&mut self, a: &str, b: &str) {
self.emit_directive("ifeq", &format!("{} {}", a, b));
self.streamer.emit_ifeq(a, b);
}
pub fn emit_asm_ifne(&mut self, a: &str, b: &str) {
self.emit_directive("ifne", &format!("{} {}", a, b));
self.streamer.emit_ifne(a, b);
}
pub fn emit_asm_iflt(&mut self, a: &str, b: &str) {
self.emit_directive("iflt", &format!("{} {}", a, b));
self.streamer.emit_iflt(a, b);
}
pub fn emit_asm_ifle(&mut self, a: &str, b: &str) {
self.emit_directive("ifle", &format!("{} {}", a, b));
self.streamer.emit_ifle(a, b);
}
pub fn emit_asm_ifgt(&mut self, a: &str, b: &str) {
self.emit_directive("ifgt", &format!("{} {}", a, b));
self.streamer.emit_ifgt(a, b);
}
pub fn emit_asm_ifge(&mut self, a: &str, b: &str) {
self.emit_directive("ifge", &format!("{} {}", a, b));
self.streamer.emit_ifge(a, b);
}
pub fn emit_asm_else(&mut self) {
self.emit_directive("else", "");
self.streamer.emit_else();
}
pub fn emit_asm_endif(&mut self) {
self.emit_directive("endif", "");
self.streamer.emit_endif();
}
pub fn emit_asm_include(&mut self, filename: &str) {
self.emit_directive("include", &format!("\"{}\"", filename));
self.streamer.emit_include(filename);
}
pub fn emit_asm_incbin(&mut self, filename: &str, skip: Option<u64>, count: Option<u64>) {
let mut args = format!("\"{}\"", filename);
if let Some(s) = skip {
args.push_str(&format!(", {}", s));
}
if let Some(c) = count {
args.push_str(&format!(", {}", c));
}
self.emit_directive("incbin", &args);
self.streamer.emit_incbin(filename, skip, count);
}
pub fn emit_asm_error(&mut self, message: &str) {
self.emit_directive("error", &format!("\"{}\"", message));
self.streamer.emit_error(message);
}
pub fn emit_asm_warning(&mut self, message: &str) {
self.emit_directive("warning", &format!("\"{}\"", message));
self.streamer.emit_warning(message);
}
pub fn emit_asm_print(&mut self, message: &str) {
self.emit_directive("print", &format!("\"{}\"", message));
self.streamer.emit_print(message);
}
pub fn emit_asm_bundle_lock(&mut self) {
self.emit_directive("bundle_lock", "");
self.streamer.emit_bundle_lock();
}
pub fn emit_asm_bundle_unlock(&mut self) {
self.emit_directive("bundle_unlock", "");
self.streamer.emit_bundle_unlock();
}
pub fn emit_asm_no_dead_strip(&mut self) {
self.emit_directive("no_dead_strip", "");
self.streamer.emit_no_dead_strip();
}
pub fn emit_asm_subsections_via_symbols(&mut self) {
self.emit_directive("subsections_via_symbols", "");
self.streamer.emit_subsections_via_symbols();
}
pub fn emit_asm_lazy_reference(&mut self, symbol: &str) {
self.emit_directive("lazy_reference", symbol);
self.streamer.emit_lazy_reference(symbol);
}
pub fn emit_asm_reference(&mut self, symbol: &str) {
self.emit_directive("reference", symbol);
self.streamer.emit_reference(symbol);
}
pub fn emit_asm_tls_model(&mut self, model: &str) {
self.emit_directive("tls_model", model);
self.streamer.emit_tls_model(model);
}
pub fn emit_asm_gnu_attribute(&mut self, tag: &str, value: &str) {
self.emit_directive("gnu_attribute", &format!("{}, {}", tag, value));
self.streamer.emit_gnu_attribute(tag, value);
}
pub fn emit_asm_note(&mut self, name: &str, note_type: u32, desc: &[u8]) {
let desc_str = desc
.iter()
.map(|b| format!("{}", b))
.collect::<Vec<_>>()
.join(",");
self.emit_directive("note", &format!("{},{},{}", name, note_type, desc_str));
self.streamer.emit_note(name, note_type, desc);
}
pub fn emit_asm_stab(&mut self, type_: u8, other: u8, desc: u16, value: u32) {
self.emit_directive("stab", &format!("{},{},{},{}", type_, other, desc, value));
self.streamer.emit_stab(type_, other, desc, value);
}
pub fn emit_asm_stabs(&mut self, s: &str) {
let escaped = s.replace('\\', "\\\\").replace('"', "\\\"");
self.emit_directive("stabs", &format!("\"{}\"", escaped));
self.streamer.emit_stabs(s);
}
pub fn emit_asm_stabn(&mut self, type_: u8, other: u8, desc: u16, value: u32) {
self.emit_directive("stabn", &format!("{},{},{},{}", type_, other, desc, value));
self.streamer.emit_stabn(type_, other, desc, value);
}
pub fn emit_asm_altmacro(&mut self) {
self.emit_directive("altmacro", "");
self.streamer.emit_altmacro();
}
pub fn emit_asm_noaltmacro(&mut self) {
self.emit_directive("noaltmacro", "");
self.streamer.emit_noaltmacro();
}
pub fn emit_local_label(&mut self, name: &str) {
let prefix = self.streamer.format.label_prefix().to_string();
self.emit_label_line(&format!("{}{}", prefix, name));
self.streamer.emit_label(&format!("{}{}", prefix, name));
}
pub fn emit_global_label(&mut self, name: &str) {
self.emit_asm_globl(name);
self.emit_label_line(name);
self.streamer.emit_label(name);
}
pub fn emit_instruction(&mut self, mnemonic: &str, operands: &str) {
if operands.is_empty() {
self.emit_indented(mnemonic);
} else {
self.emit_indented(&format!("{}\t{}", mnemonic, operands));
}
self.streamer.instruction_count += 1;
}
pub fn emit_instruction_with_encoding(
&mut self,
mnemonic: &str,
operands: &str,
_encoding: &[u8],
) {
if self.show_encoding {
let hex: Vec<String> = _encoding.iter().map(|b| format!("{:02x}", b)).collect();
self.emit_comment_line(&format!("encoding: [{}]", hex.join(" ")));
}
self.emit_instruction(mnemonic, operands);
}
pub fn emit_blank_line(&mut self) {
self.emit_line("");
}
pub fn emit_comment(&mut self, comment: &str) {
self.emit_comment_line(comment);
}
pub fn finalize(&self) -> &str {
&self.output
}
pub fn as_str(&self) -> &str {
&self.output
}
pub fn into_string(self) -> String {
self.output
}
pub fn clear(&mut self) {
self.output.clear();
self.line_count = 0;
self.directive_count = 0;
self.streamer.reset();
}
}
impl Default for X86TargetAsmStreamer {
fn default() -> Self {
Self::new(TargetObjectFormat::ELF, true)
}
}
pub struct X86TargetObjectStreamer {
pub streamer: X86MCTargetStreamer,
pub section_data: HashMap<String, Vec<u8>>,
pub section_alignments: HashMap<String, u32>,
pub section_fixups: HashMap<String, Vec<FixupRecord>>,
pub symbols: Vec<SymbolRecord>,
pub labels: Vec<LabelRecord>,
pub gen_dwarf: bool,
pub gen_cfi: bool,
temp_label_counter: u64,
section_counter: u32,
}
#[derive(Debug, Clone)]
pub struct SymbolRecord {
pub name: String,
pub value: u64,
pub size: u64,
pub section: String,
pub binding: SymbolBinding,
pub sym_type: SymbolType,
pub visibility: SymbolVisibility,
pub is_absolute: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolBinding {
Local,
Global,
Weak,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolType {
NoType,
Object,
Func,
Section,
File,
Common,
Tls,
GnuIfunc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolVisibility {
Default,
Internal,
Hidden,
Protected,
}
impl X86TargetObjectStreamer {
pub fn new(format: TargetObjectFormat, is_64bit: bool) -> Self {
let mut obj = X86TargetObjectStreamer {
streamer: X86MCTargetStreamer::new(format, is_64bit),
section_data: HashMap::new(),
section_alignments: HashMap::new(),
section_fixups: HashMap::new(),
symbols: Vec::new(),
labels: Vec::new(),
gen_dwarf: false,
gen_cfi: false,
temp_label_counter: 0,
section_counter: 0,
};
let sec_name = SectionKind::Text.default_name(format).to_string();
obj.section_data.insert(sec_name.clone(), Vec::new());
obj.section_alignments.insert(sec_name, 16);
obj.section_fixups
.insert(obj.streamer.current_section.clone(), Vec::new());
obj
}
pub fn from_triple(triple: &str) -> Self {
let is_64bit = triple.contains("x86_64") || triple.contains("amd64");
let format = if triple.contains("windows")
|| triple.contains("mingw")
|| triple.contains("msvc")
{
TargetObjectFormat::COFF
} else if triple.contains("apple") || triple.contains("darwin") || triple.contains("macos")
{
TargetObjectFormat::MachO
} else {
TargetObjectFormat::ELF
};
Self::new(format, is_64bit)
}
fn current_data_mut(&mut self) -> &mut Vec<u8> {
let sec = self.streamer.current_section.clone();
self.section_data.entry(sec).or_insert_with(Vec::new)
}
fn current_fixups_mut(&mut self) -> &mut Vec<FixupRecord> {
let sec = self.streamer.current_section.clone();
self.section_fixups.entry(sec).or_insert_with(Vec::new)
}
pub fn switch_section(&mut self, name: &str, flags: Option<&str>, typ: Option<&str>) {
self.finalize_current_section();
self.streamer.emit_section(name, flags, typ);
self.section_data
.entry(name.to_string())
.or_insert_with(Vec::new);
self.section_alignments
.entry(name.to_string())
.or_insert(self.streamer.section_alignment);
self.section_fixups
.entry(name.to_string())
.or_insert_with(Vec::new);
self.section_counter += 1;
}
pub fn push_section(&mut self, name: &str, flags: Option<&str>, typ: Option<&str>) {
self.finalize_current_section();
self.streamer.emit_pushsection(name, flags, typ);
self.section_data
.entry(name.to_string())
.or_insert_with(Vec::new);
self.section_alignments
.entry(name.to_string())
.or_insert(self.streamer.section_alignment);
self.section_fixups
.entry(name.to_string())
.or_insert_with(Vec::new);
}
pub fn pop_section(&mut self) {
self.finalize_current_section();
self.streamer.emit_popsection();
}
fn finalize_current_section(&mut self) {
let sec = self.streamer.current_section.clone();
let data = std::mem::take(&mut self.streamer.binary_buffer);
let fixups = std::mem::take(&mut self.streamer.fixups);
if let Some(buf) = self.section_data.get_mut(&sec) {
buf.extend_from_slice(&data);
}
if let Some(fx) = self.section_fixups.get_mut(&sec) {
fx.extend(fixups);
}
self.streamer.current_offset = 0;
}
pub fn emit_bytes(&mut self, bytes: &[u8]) {
self.streamer.emit_raw_bytes(bytes);
}
pub fn emit_obj_byte(&mut self, values: &[u8]) {
self.streamer.emit_byte(values);
}
pub fn emit_obj_short(&mut self, values: &[i16]) {
self.streamer.emit_short(values);
}
pub fn emit_obj_long(&mut self, values: &[i32]) {
self.streamer.emit_long(values);
}
pub fn emit_obj_quad(&mut self, values: &[i64]) {
self.streamer.emit_quad(values);
}
pub fn emit_obj_asciz(&mut self, s: &str) {
self.streamer.emit_asciz(s);
}
pub fn emit_obj_align(&mut self, alignment: u32, fill: Option<u8>) {
self.streamer.emit_align(alignment, fill, None);
}
pub fn emit_obj_fill(&mut self, count: u64, value: &[u8], size: u8) {
self.streamer.emit_fill(count, value, size);
}
pub fn emit_obj_org(&mut self, address: u64, fill: Option<u8>) {
self.streamer.emit_org(address, fill);
}
pub fn emit_obj_zero(&mut self, size: u64) {
self.streamer.emit_zero(size);
}
pub fn define_symbol(
&mut self,
name: &str,
binding: SymbolBinding,
sym_type: SymbolType,
visibility: SymbolVisibility,
) {
let offset = self.current_offset();
self.symbols.push(SymbolRecord {
name: name.to_string(),
value: offset,
size: 0,
section: self.streamer.current_section.clone(),
binding,
sym_type,
visibility,
is_absolute: false,
});
}
pub fn define_label(&mut self, name: &str, is_local: bool) {
let offset = self.current_offset();
self.labels.push(LabelRecord {
name: name.to_string(),
offset,
section: self.streamer.current_section.clone(),
is_local,
is_temporary: name.starts_with(".Ltmp"),
});
self.streamer.emit_label(name);
}
pub fn new_temp_label(&mut self) -> String {
self.temp_label_counter += 1;
format!(".Ltmp{}", self.temp_label_counter)
}
pub fn current_offset(&self) -> u64 {
self.streamer.current_offset
}
pub fn add_fixup(&mut self, kind: FixupKind, symbol: &str, addend: i64) {
let offset = self.streamer.current_offset;
self.streamer.add_fixup(offset, kind, symbol, addend);
let sec = self.streamer.current_section.clone();
if let Some(fx) = self.section_fixups.get_mut(&sec) {
fx.push(FixupRecord {
offset,
kind,
symbol: symbol.to_string(),
addend,
section: sec,
});
}
}
pub fn emit_gotpcrel32_value(&mut self, symbol: &str, addend: i64) {
let offset = self.streamer.current_offset;
let mut data = self.current_data_mut();
let placeholder: [u8; 4] = [0u8; 4];
data.extend_from_slice(&placeholder);
self.streamer.current_offset += 4;
self.add_fixup(FixupKind::GotPcRel32, symbol, addend);
}
pub fn emit_instruction_bytes(&mut self, bytes: &[u8]) {
self.streamer.emit_raw_bytes(bytes);
self.streamer.instruction_count += 1;
}
pub fn finalize(&mut self) {
self.finalize_current_section();
}
pub fn section_names(&self) -> Vec<String> {
let mut names: Vec<String> = self.section_data.keys().cloned().collect();
names.sort();
names
}
pub fn get_section_data(&self, name: &str) -> Option<&[u8]> {
self.section_data.get(name).map(|v| v.as_slice())
}
pub fn get_section_fixups(&self, name: &str) -> Option<&[FixupRecord]> {
self.section_fixups.get(name).map(|v| v.as_slice())
}
pub fn get_section_alignment(&self, name: &str) -> Option<u32> {
self.section_alignments.get(name).copied()
}
pub fn reset(&mut self) {
self.streamer.reset();
self.section_data.clear();
self.section_alignments.clear();
self.section_fixups.clear();
self.symbols.clear();
self.labels.clear();
self.temp_label_counter = 0;
self.section_counter = 0;
let sec_name = SectionKind::Text
.default_name(self.streamer.format)
.to_string();
self.section_data.insert(sec_name.clone(), Vec::new());
self.section_alignments.insert(sec_name, 16);
self.section_fixups
.insert(self.streamer.current_section.clone(), Vec::new());
}
}
impl Default for X86TargetObjectStreamer {
fn default() -> Self {
Self::new(TargetObjectFormat::ELF, true)
}
}
pub struct X86EmitGPRel32Value {
pub streamer: X86MCTargetStreamer,
pub add_suffix: bool,
pub no_plt: bool,
pub relocation_count: u64,
}
impl X86EmitGPRel32Value {
pub fn new(format: TargetObjectFormat, is_64bit: bool) -> Self {
X86EmitGPRel32Value {
streamer: X86MCTargetStreamer::new(format, is_64bit),
add_suffix: true,
no_plt: false,
relocation_count: 0,
}
}
pub fn emit_gotpcrel32(&mut self, symbol: &str, addend: i64) {
self.streamer.emit_byte(&[0, 0, 0, 0]);
let offset = self.streamer.current_offset - 4;
self.streamer
.add_fixup(offset, FixupKind::GotPcRel32, symbol, addend);
self.relocation_count += 1;
}
pub fn emit_gotpcrel32_x32(&mut self, symbol: &str) {
self.emit_gotpcrel32(symbol, 0);
}
pub fn emit_gotpcrelx(&mut self, symbol: &str) {
self.emit_gotpcrel32(symbol, 0);
}
pub fn emit_rex_gotpcrelx(&mut self, symbol: &str) {
self.emit_gotpcrel32(symbol, 0);
}
pub fn emit_gotpcrel32_tls(&mut self, symbol: &str) {
self.streamer.emit_byte(&[0, 0, 0, 0]);
let offset = self.streamer.current_offset - 4;
self.streamer.add_fixup(offset, FixupKind::TlsGd, symbol, 0);
self.relocation_count += 1;
}
pub fn emit_gotpcrel32_tlsdesc(&mut self, symbol: &str) {
self.streamer.emit_byte(&[0, 0, 0, 0]);
let offset = self.streamer.current_offset - 4;
self.streamer
.add_fixup(offset, FixupKind::GotPc32TlsDesc, symbol, 0);
self.relocation_count += 1;
}
pub fn emit_plt32(&mut self, symbol: &str, addend: i64) {
self.streamer.emit_byte(&[0, 0, 0, 0]);
let offset = self.streamer.current_offset - 4;
self.streamer
.add_fixup(offset, FixupKind::Plt32, symbol, addend);
self.relocation_count += 1;
}
pub fn emit_got_off(&mut self, symbol: &str) {
self.streamer.emit_byte(&[0, 0, 0, 0]);
let offset = self.streamer.current_offset - 4;
self.streamer
.add_fixup(offset, FixupKind::GotOff, symbol, 0);
self.relocation_count += 1;
}
pub fn format_gotpcrel(symbol: &str) -> String {
format!("{}@GOTPCREL", symbol)
}
pub fn format_plt(symbol: &str) -> String {
format!("{}@PLT", symbol)
}
pub fn format_gotoff(symbol: &str) -> String {
format!("{}@GOTOFF", symbol)
}
pub fn format_gottpoff(symbol: &str) -> String {
format!("{}@GOTTPOFF", symbol)
}
pub fn format_tpoff(symbol: &str) -> String {
format!("{}@TPOFF", symbol)
}
pub fn format_dtpoff(symbol: &str) -> String {
format!("{}@DTPOFF", symbol)
}
pub fn count(&self) -> u64 {
self.relocation_count
}
pub fn reset(&mut self) {
self.streamer.reset();
self.relocation_count = 0;
}
pub fn is_supported(&self) -> bool {
self.streamer.format.supports_gotpcrel_suffix()
}
}
impl Default for X86EmitGPRel32Value {
fn default() -> Self {
Self::new(TargetObjectFormat::ELF, true)
}
}
fn format_text_section(format: &TargetObjectFormat) -> String {
SectionKind::Text.default_name(*format).to_string()
}
pub struct X86TargetNullStreamer {
pub streamer: X86MCTargetStreamer,
pub instruction_count: u64,
pub directive_count: u64,
pub byte_count: u64,
pub label_count: u64,
pub fixup_count: u64,
}
impl X86TargetNullStreamer {
pub fn new(format: TargetObjectFormat, is_64bit: bool) -> Self {
X86TargetNullStreamer {
streamer: X86MCTargetStreamer::new(format, is_64bit),
instruction_count: 0,
directive_count: 0,
byte_count: 0,
label_count: 0,
fixup_count: 0,
}
}
pub fn update_counts(&mut self) {
self.instruction_count = self.streamer.instruction_count;
self.byte_count = self.streamer.data_count;
self.label_count = self.streamer.labels.len() as u64;
self.fixup_count = self.streamer.fixups.len() as u64;
self.directive_count = self.streamer.data_count;
}
pub fn reset(&mut self) {
self.streamer.reset();
self.instruction_count = 0;
self.directive_count = 0;
self.byte_count = 0;
self.label_count = 0;
self.fixup_count = 0;
}
}
impl Default for X86TargetNullStreamer {
fn default() -> Self {
Self::new(TargetObjectFormat::ELF, true)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DirectiveAction {
EmitText,
EmitData,
EmitBss,
EmitRodata,
EmitSection(String, Option<String>, Option<String>),
EmitPushsection(String, Option<String>, Option<String>),
EmitPopsection,
EmitPrevious,
EmitGlobl(String),
EmitGlobal(String),
EmitLocal(String),
EmitWeak(String),
EmitHidden(String),
EmitProtected(String),
EmitInternal(String),
EmitType(String, String),
EmitSize(String, u64),
EmitLabel(String),
EmitByte(Vec<u8>),
EmitShort(Vec<i16>),
EmitLong(Vec<i32>),
EmitQuad(Vec<i64>),
EmitOcta(Vec<u128>),
EmitAscii(String),
EmitAsciz(String),
EmitAlign(u32, Option<u8>, Option<u32>),
EmitP2Align(u32, Option<u8>, Option<u32>),
EmitBAlign(u32, Option<u8>),
EmitOrg(u64, Option<u8>),
EmitFill(u64, Vec<u8>, u8),
EmitSpace(u64, Option<u8>),
EmitZero(u64),
EmitComm(String, u64, Option<u32>),
EmitLComm(String, u64, Option<u32>),
EmitEqu(String, String),
EmitSet(String, String),
EmitFile(u32, String),
EmitLoc(u32, u32, u32),
EmitLine(u32),
EmitIdent(String),
EmitCfiStartProc,
EmitCfiEndProc,
EmitCfiDefCfa(u32, i64),
EmitCfiOffset(u32, i64),
EmitCfiDefCfaRegister(u32),
EmitCfiDefCfaOffset(i64),
EmitCfiRestore(u32),
EmitCfiRememberState,
EmitCfiRestoreState,
EmitCfiEscape(Vec<u8>),
EmitSehProc(String),
EmitSehEndProc,
EmitSehEndPrologue,
EmitSehPushReg(u32),
EmitSehSetFrame(u32, u32),
EmitSehStackAlloc(u32),
EmitSehSaveReg(u32, u32),
EmitCvFile(u32, String),
EmitCvFuncId(u32, String),
EmitCvLoc(u32, u32, u32, u32),
EmitIf(String),
EmitIfDef(String),
EmitIfNDef(String),
EmitElse,
EmitEndIf,
EmitMacro(String, Vec<String>),
EmitEndM,
EmitBundleLock,
EmitBundleUnlock,
EmitTlsModel(String),
EmitGnuAttribute(String, String),
EmitAltMacro,
EmitNoAltMacro,
}
pub struct X86TargetRecordStreamer {
pub streamer: X86MCTargetStreamer,
pub actions: Vec<DirectiveAction>,
pub paused: bool,
}
impl X86TargetRecordStreamer {
pub fn new(format: TargetObjectFormat, is_64bit: bool) -> Self {
X86TargetRecordStreamer {
streamer: X86MCTargetStreamer::new(format, is_64bit),
actions: Vec::new(),
paused: false,
}
}
pub fn emit_directive_action(&mut self, action: DirectiveAction) {
if !self.paused {
self.actions.push(action);
}
}
pub fn pause(&mut self) {
self.paused = true;
}
pub fn resume(&mut self) {
self.paused = false;
}
pub fn clear(&mut self) {
self.actions.clear();
self.paused = false;
}
pub fn replay_to_asm(&self, asm: &mut X86TargetAsmStreamer) {
for action in &self.actions {
match action {
DirectiveAction::EmitText => asm.emit_asm_text(),
DirectiveAction::EmitData => asm.emit_asm_data(),
DirectiveAction::EmitBss => asm.emit_asm_bss(),
DirectiveAction::EmitRodata => asm.emit_asm_rodata(),
DirectiveAction::EmitSection(name, flags, typ) => {
asm.emit_asm_section(name, flags.as_deref(), typ.as_deref());
}
DirectiveAction::EmitGlobl(name) => asm.emit_asm_globl(name),
DirectiveAction::EmitGlobal(name) => asm.emit_asm_global(name),
DirectiveAction::EmitByte(vals) => asm.emit_asm_byte(vals),
DirectiveAction::EmitLabel(name) => {
asm.streamer.emit_label(name);
}
DirectiveAction::EmitCfiStartProc => asm.emit_asm_cfi_startproc(),
DirectiveAction::EmitCfiEndProc => asm.emit_asm_cfi_endproc(),
_ => {} }
}
}
pub fn replay_to_object(&self, obj: &mut X86TargetObjectStreamer) {
for action in &self.actions {
match action {
DirectiveAction::EmitByte(vals) => obj.emit_obj_byte(vals),
DirectiveAction::EmitLabel(name) => {
obj.streamer.emit_label(name);
}
_ => {} }
}
}
pub fn len(&self) -> usize {
self.actions.len()
}
pub fn is_empty(&self) -> bool {
self.actions.is_empty()
}
}
impl Default for X86TargetRecordStreamer {
fn default() -> Self {
Self::new(TargetObjectFormat::ELF, true)
}
}
#[derive(Debug, Clone)]
pub struct RelaxableInstruction {
pub bytes: Vec<u8>,
pub mnemonic: String,
pub min_size: u8,
pub current_size: u8,
pub max_size: u8,
pub target_label: String,
pub fixup_kind: FixupKind,
pub can_relax_to_near: bool,
}
#[derive(Debug, Clone)]
pub struct RelaxationManager {
pub relaxable: Vec<RelaxableInstruction>,
pub label_offsets: HashMap<String, u64>,
pub total_size: u64,
pub iteration: u32,
pub converged: bool,
}
impl RelaxationManager {
pub fn relax_pass(&mut self) -> bool {
let mut changed = false;
let mut offset = 0u64;
for inst in &mut self.relaxable {
let inst_offset = offset;
if let Some(&target_offset) = self.label_offsets.get(&inst.target_label) {
let disp = target_offset as i64 - (inst_offset as i64 + inst.current_size as i64);
let in_short_range = disp >= -128 && disp <= 127;
if !in_short_range && inst.can_relax_to_near && inst.current_size < inst.max_size {
inst.current_size = inst.max_size;
inst.fixup_kind = FixupKind::Rel32;
if inst.bytes[0] == 0xEB {
inst.bytes = vec![0xE9, 0x00, 0x00, 0x00, 0x00];
} else if inst.bytes[0] >= 0x70 && inst.bytes[0] <= 0x7F {
let cc = inst.bytes[0] - 0x70;
inst.bytes = vec![0x0F, 0x80 + cc, 0x00, 0x00, 0x00, 0x00];
}
changed = true;
}
}
offset += inst.current_size as u64;
}
self.total_size = offset;
self.iteration += 1;
self.converged = !changed;
changed
}
}
pub struct InstructionEmitter<'a> {
pub streamer: &'a mut X86MCTargetStreamer,
}
impl<'a> InstructionEmitter<'a> {
pub fn new(streamer: &'a mut X86MCTargetStreamer) -> Self {
InstructionEmitter { streamer }
}
pub fn emit_nop(&mut self) {
self.streamer.emit_raw_bytes(&[0x90]);
self.streamer.instruction_count += 1;
}
pub fn emit_nop_n(&mut self, n: u64) {
let nops = generate_optimal_nop_bytes(n, self.streamer.is_64bit);
self.streamer.emit_raw_bytes(&nops);
self.streamer.instruction_count += 1;
}
pub fn emit_push_reg(&mut self, reg: u8) {
self.streamer.emit_raw_bytes(&[0x50 + reg]);
self.streamer.instruction_count += 1;
}
pub fn emit_pop_reg(&mut self, reg: u8) {
self.streamer.emit_raw_bytes(&[0x58 + reg]);
self.streamer.instruction_count += 1;
}
pub fn emit_ret(&mut self) {
self.streamer.emit_raw_bytes(&[0xC3]);
self.streamer.instruction_count += 1;
}
pub fn emit_mov_imm32(&mut self, reg: u8, imm: i32) {
let mut bytes = vec![0xB8 + reg];
bytes.extend_from_slice(&imm.to_le_bytes());
self.streamer.emit_raw_bytes(&bytes);
self.streamer.instruction_count += 1;
}
pub fn emit_lea_rip_relative(&mut self, reg: u8) {
self.streamer
.emit_raw_bytes(&[0x48, 0x8D, 0x05 | ((reg & 0x07) << 3)]);
self.streamer.emit_raw_bytes(&[0x00, 0x00, 0x00, 0x00]);
self.streamer.instruction_count += 1;
}
pub fn emit_call_rel32_placeholder(&mut self) {
self.streamer
.emit_raw_bytes(&[0xE8, 0x00, 0x00, 0x00, 0x00]);
self.streamer.instruction_count += 1;
}
pub fn emit_int3(&mut self) {
self.streamer.emit_raw_bytes(&[0xCC]);
self.streamer.instruction_count += 1;
}
pub fn emit_xor_rr(&mut self, reg1: u8, reg2: u8) {
let rex = if reg1 >= 8 || reg2 >= 8 { 0x48 } else { 0x00 };
let modrm = 0xC0 | ((reg2 & 0x07) << 3) | (reg1 & 0x07);
if rex != 0 {
self.streamer.emit_raw_bytes(&[rex, 0x31, modrm]);
} else {
self.streamer.emit_raw_bytes(&[0x31, modrm]);
}
self.streamer.instruction_count += 1;
}
pub fn emit_leave(&mut self) {
self.streamer.emit_raw_bytes(&[0xC9]);
self.streamer.instruction_count += 1;
}
}
pub fn generate_optimal_nop_bytes(size: u64, is_64bit: bool) -> Vec<u8> {
if size == 0 {
return Vec::new();
}
let mut result = Vec::with_capacity(size as usize);
let mut remaining = size;
while remaining > 0 {
if remaining == 1 {
result.push(0x90);
remaining -= 1;
} else if remaining == 2 {
result.extend_from_slice(&[0x66, 0x90]);
remaining -= 2;
} else if remaining == 3 {
result.extend_from_slice(&[0x0F, 0x1F, 0x00]);
remaining -= 3;
} else if remaining == 4 {
if is_64bit {
result.extend_from_slice(&[0x0F, 0x1F, 0x40, 0x00]);
} else {
result.extend_from_slice(&[0x0F, 0x1F, 0x00, 0x00]);
}
remaining -= 4;
} else if remaining == 5 {
result.extend_from_slice(&[0x0F, 0x1F, 0x44, 0x00, 0x00]);
remaining -= 5;
} else if remaining == 6 {
result.extend_from_slice(&[0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00]);
remaining -= 6;
} else if remaining == 7 {
result.extend_from_slice(&[0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00]);
remaining -= 7;
} else if remaining == 8 {
result.extend_from_slice(&[0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]);
remaining -= 8;
} else if remaining == 9 {
result.extend_from_slice(&[0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]);
remaining -= 9;
} else if remaining >= 10 {
if remaining % 9 == 0 || remaining >= 18 {
result.extend_from_slice(&[0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]);
remaining -= 9;
} else {
result.extend_from_slice(&[0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]);
remaining -= 8;
}
}
}
result
}
pub fn map_relocation_to_elf(kind: FixupKind, is_64bit: bool) -> &'static str {
match (kind, is_64bit) {
(FixupKind::Abs64, true) => "R_X86_64_64",
(FixupKind::Abs64, false) => "R_386_32",
(FixupKind::Abs32, true) => "R_X86_64_32",
(FixupKind::Abs32, false) => "R_386_32",
(FixupKind::Abs16, _) => "R_X86_64_16",
(FixupKind::Abs8, _) => "R_X86_64_8",
(FixupKind::Rel32, true) => "R_X86_64_PC32",
(FixupKind::Rel32, false) => "R_386_PC32",
(FixupKind::Rel8, _) => "R_X86_64_PC8",
(FixupKind::Rel16, _) => "R_X86_64_PC16",
(FixupKind::RipRel32, true) => "R_X86_64_PC32",
(FixupKind::RipRel32, false) => "R_386_PC32",
(FixupKind::GotPcRel32, true) => "R_X86_64_GOTPCREL",
(FixupKind::GotPcRel32, false) => "R_386_GOTPC",
(FixupKind::Plt32, true) => "R_X86_64_PLT32",
(FixupKind::Plt32, false) => "R_386_PLT32",
(FixupKind::GotOff, true) => "R_X86_64_GOTOFF64",
(FixupKind::GotOff, false) => "R_386_GOTOFF",
(FixupKind::TlsGd, true) => "R_X86_64_TLSGD",
(FixupKind::TlsGd, false) => "R_386_TLS_GD",
(FixupKind::TlsLd, _) => "R_X86_64_TLSLD",
(FixupKind::GotTpOff, true) => "R_X86_64_GOTTPOFF",
(FixupKind::GotTpOff, false) => "R_386_TLS_GOTIE",
(FixupKind::TpOff, true) => "R_X86_64_TPOFF32",
(FixupKind::TpOff, false) => "R_386_TLS_LE",
(FixupKind::DtpOff, _) => "R_X86_64_DTPOFF32",
(FixupKind::GotPc32TlsDesc, true) => "R_X86_64_GOTPC32_TLSDESC",
(FixupKind::GotPc32TlsDesc, false) => "R_386_TLS_DESC",
}
}
pub fn map_relocation_to_coff(kind: FixupKind) -> &'static str {
match kind {
FixupKind::Abs64 => "IMAGE_REL_AMD64_ADDR64",
FixupKind::Abs32 => "IMAGE_REL_AMD64_ADDR32",
FixupKind::Rel32 => "IMAGE_REL_AMD64_REL32",
FixupKind::Rel8 => "IMAGE_REL_AMD64_REL32",
FixupKind::RipRel32 => "IMAGE_REL_AMD64_REL32",
FixupKind::GotPcRel32 => "IMAGE_REL_AMD64_REL32",
FixupKind::Abs16 => "IMAGE_REL_AMD64_ADDR32",
_ => "IMAGE_REL_AMD64_ABSOLUTE",
}
}
pub fn map_relocation_to_macho(kind: FixupKind) -> &'static str {
match kind {
FixupKind::Abs64 => "X86_64_RELOC_UNSIGNED",
FixupKind::Abs32 => "X86_64_RELOC_UNSIGNED",
FixupKind::Rel32 => "X86_64_RELOC_BRANCH",
FixupKind::GotPcRel32 => "X86_64_RELOC_GOT_LOAD",
FixupKind::GotOff => "X86_64_RELOC_GOT",
FixupKind::Rel8 => "X86_64_RELOC_BRANCH",
_ => "X86_64_RELOC_UNSIGNED",
}
}
pub fn get_elf_relocation_number(kind: FixupKind, is_64bit: bool) -> u32 {
match (kind, is_64bit) {
(FixupKind::Abs64, true) => 1,
(FixupKind::Abs32, true) => 10,
(FixupKind::Abs32, false) => 1,
(FixupKind::Rel32, true) => 2,
(FixupKind::Rel32, false) => 2,
(FixupKind::GotPcRel32, true) => 9,
(FixupKind::GotPcRel32, false) => 10,
(FixupKind::Plt32, true) => 4,
(FixupKind::Plt32, false) => 4,
_ => 0,
}
}
#[derive(Debug, Clone)]
pub struct LabelResolver {
pub label_offsets: HashMap<String, u64>,
pub label_sections: HashMap<String, String>,
pub pending_fixups: Vec<(FixupKind, String, u64, String)>,
}
impl LabelResolver {
pub fn new() -> Self {
LabelResolver {
label_offsets: HashMap::new(),
label_sections: HashMap::new(),
pending_fixups: Vec::new(),
}
}
pub fn define_label(&mut self, name: &str, offset: u64, section: &str) {
self.label_offsets.insert(name.to_string(), offset);
self.label_sections
.insert(name.to_string(), section.to_string());
}
pub fn add_fixup(&mut self, kind: FixupKind, symbol: &str, offset: u64, section: &str) {
self.pending_fixups
.push((kind, symbol.to_string(), offset, section.to_string()));
}
pub fn calculate_displacement(&self, from: &str, to: &str) -> Option<i64> {
match (self.label_offsets.get(from), self.label_offsets.get(to)) {
(Some(&from_off), Some(&to_off)) => Some(to_off as i64 - from_off as i64),
_ => None,
}
}
pub fn is_defined(&self, name: &str) -> bool {
self.label_offsets.contains_key(name)
}
}
impl Default for LabelResolver {
fn default() -> Self {
Self::new()
}
}
pub fn get_all_section_names_for_format(format: TargetObjectFormat) -> Vec<String> {
let kinds = [
SectionKind::Text,
SectionKind::Data,
SectionKind::ReadOnly,
SectionKind::Bss,
SectionKind::ReadOnlyStr,
SectionKind::EHFrame,
SectionKind::DebugInfo,
SectionKind::DebugLine,
SectionKind::DebugStr,
SectionKind::DebugAbbrev,
SectionKind::DebugAranges,
SectionKind::DebugFrame,
SectionKind::InitArray,
SectionKind::FiniArray,
SectionKind::ThreadData,
SectionKind::ThreadBss,
SectionKind::Note,
];
kinds
.iter()
.map(|k| k.default_name(format).to_string())
.collect()
}
#[derive(Debug, Clone)]
pub struct DisassemblyHint {
pub is_prologue: bool,
pub is_epilogue: bool,
pub is_branch_target: bool,
pub is_call_target: bool,
pub alignment: u32,
pub comment: Option<String>,
}
impl Default for DisassemblyHint {
fn default() -> Self {
DisassemblyHint {
is_prologue: false,
is_epilogue: false,
is_branch_target: false,
is_call_target: false,
alignment: 1,
comment: None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct BundleAlignment {
pub size: u32,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_comment_char() {
assert_eq!(TargetObjectFormat::ELF.comment_char(), '#');
assert_eq!(TargetObjectFormat::COFF.comment_char(), '#');
assert_eq!(TargetObjectFormat::MachO.comment_char(), '#');
assert_eq!(TargetObjectFormat::Wasm.comment_char(), ';');
}
#[test]
fn test_format_label_prefix() {
assert_eq!(TargetObjectFormat::ELF.label_prefix(), ".L");
assert_eq!(TargetObjectFormat::COFF.label_prefix(), ".L");
assert_eq!(TargetObjectFormat::MachO.label_prefix(), "L");
}
#[test]
fn test_format_supports_subsections_via_symbols() {
assert!(!TargetObjectFormat::ELF.supports_subsections_via_symbols());
assert!(TargetObjectFormat::MachO.supports_subsections_via_symbols());
}
#[test]
fn test_format_supports_gotpcrel_suffix() {
assert!(TargetObjectFormat::ELF.supports_gotpcrel_suffix());
assert!(!TargetObjectFormat::COFF.supports_gotpcrel_suffix());
assert!(!TargetObjectFormat::MachO.supports_gotpcrel_suffix());
}
#[test]
fn test_format_supports_seh() {
assert!(TargetObjectFormat::COFF.supports_seh());
assert!(!TargetObjectFormat::ELF.supports_seh());
assert!(!TargetObjectFormat::MachO.supports_seh());
}
#[test]
fn test_format_supports_codeview() {
assert!(TargetObjectFormat::COFF.supports_codeview());
assert!(!TargetObjectFormat::ELF.supports_codeview());
assert!(!TargetObjectFormat::MachO.supports_codeview());
}
#[test]
fn test_format_display() {
assert_eq!(format!("{}", TargetObjectFormat::ELF), "elf");
assert_eq!(format!("{}", TargetObjectFormat::COFF), "coff");
assert_eq!(format!("{}", TargetObjectFormat::MachO), "macho");
assert_eq!(format!("{}", TargetObjectFormat::Binary), "binary");
}
#[test]
fn test_format_default() {
assert_eq!(TargetObjectFormat::default(), TargetObjectFormat::ELF);
}
#[test]
fn test_section_kind_is_text() {
assert!(SectionKind::Text.is_text());
assert!(!SectionKind::Data.is_text());
assert!(!SectionKind::Bss.is_text());
}
#[test]
fn test_section_kind_is_writable() {
assert!(SectionKind::Data.is_writable());
assert!(SectionKind::Bss.is_writable());
assert!(!SectionKind::Text.is_writable());
assert!(!SectionKind::ReadOnly.is_writable());
}
#[test]
fn test_section_kind_is_bss() {
assert!(SectionKind::Bss.is_bss());
assert!(SectionKind::ThreadBss.is_bss());
assert!(!SectionKind::Data.is_bss());
assert!(!SectionKind::Text.is_bss());
}
#[test]
fn test_section_kind_is_debug() {
assert!(SectionKind::DebugInfo.is_debug());
assert!(SectionKind::DebugLine.is_debug());
assert!(SectionKind::DebugStr.is_debug());
assert!(!SectionKind::Text.is_debug());
assert!(!SectionKind::Data.is_debug());
}
#[test]
fn test_section_kind_is_unwind() {
assert!(SectionKind::EHFrame.is_unwind());
assert!(SectionKind::PData.is_unwind());
assert!(SectionKind::XData.is_unwind());
assert!(!SectionKind::Text.is_unwind());
}
#[test]
fn test_section_kind_default_elf_name() {
assert_eq!(SectionKind::Text.default_elf_name(), ".text");
assert_eq!(SectionKind::Data.default_elf_name(), ".data");
assert_eq!(SectionKind::Bss.default_elf_name(), ".bss");
assert_eq!(SectionKind::ReadOnly.default_elf_name(), ".rodata");
assert_eq!(SectionKind::EHFrame.default_elf_name(), ".eh_frame");
assert_eq!(SectionKind::DebugInfo.default_elf_name(), ".debug_info");
assert_eq!(SectionKind::Note.default_elf_name(), ".note");
assert_eq!(SectionKind::Stabs.default_elf_name(), ".stab");
}
#[test]
fn test_section_kind_default_coff_name() {
assert_eq!(SectionKind::Text.default_coff_name(), ".text");
assert_eq!(SectionKind::Data.default_coff_name(), ".data");
assert_eq!(SectionKind::Bss.default_coff_name(), ".bss");
assert_eq!(SectionKind::ReadOnly.default_coff_name(), ".rdata");
assert_eq!(SectionKind::PData.default_coff_name(), ".pdata");
assert_eq!(SectionKind::XData.default_coff_name(), ".xdata");
}
#[test]
fn test_section_kind_default_macho_name() {
assert_eq!(SectionKind::Text.default_macho_name(), "__text");
assert_eq!(SectionKind::Data.default_macho_name(), "__data");
assert_eq!(SectionKind::Bss.default_macho_name(), "__bss");
assert_eq!(SectionKind::ReadOnly.default_macho_name(), "__const");
}
#[test]
fn test_fixup_kind_size_bytes() {
assert_eq!(FixupKind::Abs8.size_bytes(), 1);
assert_eq!(FixupKind::Abs16.size_bytes(), 2);
assert_eq!(FixupKind::Abs32.size_bytes(), 4);
assert_eq!(FixupKind::Abs64.size_bytes(), 8);
assert_eq!(FixupKind::Rel8.size_bytes(), 1);
assert_eq!(FixupKind::Rel32.size_bytes(), 4);
assert_eq!(FixupKind::RipRel32.size_bytes(), 4);
assert_eq!(FixupKind::GotPcRel32.size_bytes(), 4);
assert_eq!(FixupKind::Plt32.size_bytes(), 4);
}
#[test]
fn test_fixup_kind_is_pc_relative() {
assert!(FixupKind::Rel8.is_pc_relative());
assert!(FixupKind::Rel16.is_pc_relative());
assert!(FixupKind::Rel32.is_pc_relative());
assert!(FixupKind::RipRel32.is_pc_relative());
assert!(FixupKind::GotPcRel32.is_pc_relative());
assert!(FixupKind::Plt32.is_pc_relative());
assert!(!FixupKind::Abs8.is_pc_relative());
assert!(!FixupKind::Abs32.is_pc_relative());
assert!(!FixupKind::Abs64.is_pc_relative());
}
#[test]
fn test_streamer_creation_elf64() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(s.format, TargetObjectFormat::ELF);
assert!(s.is_64bit);
assert_eq!(s.current_section, ".text");
assert_eq!(s.current_section_kind, SectionKind::Text);
}
#[test]
fn test_streamer_creation_elf32() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, false);
assert_eq!(s.format, TargetObjectFormat::ELF);
assert!(!s.is_64bit);
}
#[test]
fn test_streamer_creation_coff() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
assert_eq!(s.format, TargetObjectFormat::COFF);
assert_eq!(s.current_section, ".text");
}
#[test]
fn test_streamer_creation_macho() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::MachO, true);
assert_eq!(s.format, TargetObjectFormat::MachO);
assert_eq!(s.current_section, "__text");
assert_eq!(s.current_section_kind, SectionKind::Text);
}
#[test]
fn test_streamer_text_section_name() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(s.text_section_name(), ".text");
let s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
assert_eq!(s.text_section_name(), ".text");
let s = X86MCTargetStreamer::new(TargetObjectFormat::MachO, true);
assert_eq!(s.text_section_name(), "__text");
}
#[test]
fn test_emit_text() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_section = ".data".to_string();
s.emit_text();
assert_eq!(s.current_section, ".text");
assert_eq!(s.current_section_kind, SectionKind::Text);
}
#[test]
fn test_emit_data_section() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_data_section();
assert_eq!(s.current_section, ".data");
assert_eq!(s.current_section_kind, SectionKind::Data);
}
#[test]
fn test_emit_bss() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_bss();
assert_eq!(s.current_section, ".bss");
assert_eq!(s.current_section_kind, SectionKind::Bss);
}
#[test]
fn test_emit_rodata() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_rodata();
assert_eq!(s.current_section, ".rodata");
assert_eq!(s.current_section_kind, SectionKind::ReadOnly);
}
#[test]
fn test_emit_section_custom() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_section(".mycustom", Some("aw"), Some("progbits"));
assert_eq!(s.current_section, ".mycustom");
assert_eq!(s.current_section_kind, SectionKind::Custom);
}
#[test]
fn test_emit_section_recognizes_text() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_section(".text.hot", None, None);
assert_eq!(s.current_section_kind, SectionKind::Text);
}
#[test]
fn test_emit_section_recognizes_data() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_section(".data.rel.ro", None, None);
assert_eq!(s.current_section_kind, SectionKind::Data);
}
#[test]
fn test_emit_section_recognizes_eh_frame() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_section(".eh_frame", None, None);
assert_eq!(s.current_section_kind, SectionKind::EHFrame);
}
#[test]
fn test_emit_pushsection_popsection() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(s.section_stack.len(), 0);
s.emit_pushsection(".data", None, None);
assert_eq!(s.current_section, ".data");
assert_eq!(s.section_stack.len(), 1);
s.emit_popsection();
assert_eq!(s.current_section, ".text");
assert_eq!(s.section_stack.len(), 0);
}
#[test]
fn test_emit_previous() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_pushsection(".data", None, None);
assert_eq!(s.current_section, ".data");
s.emit_previous();
assert_eq!(s.current_section, ".text");
}
#[test]
fn test_emit_subsections_via_symbols() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::MachO, true);
s.emit_subsections_via_symbols();
}
#[test]
fn test_emit_no_dead_strip() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::MachO, true);
s.emit_no_dead_strip();
}
#[test]
fn test_emit_lazy_reference() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::MachO, true);
s.emit_lazy_reference("_foo");
}
#[test]
fn test_emit_reference() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::MachO, true);
s.emit_reference("_bar");
}
#[test]
fn test_emit_globl() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_globl("main");
let sym = s.symbols.get("main").unwrap();
assert!(sym.is_global);
}
#[test]
fn test_emit_global() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_global("_start");
let sym = s.symbols.get("_start").unwrap();
assert!(sym.is_global);
}
#[test]
fn test_emit_local() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_local("helper");
let sym = s.symbols.get("helper").unwrap();
assert!(sym.is_local);
}
#[test]
fn test_emit_weak() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_weak("weak_sym");
let sym = s.symbols.get("weak_sym").unwrap();
assert!(sym.is_weak);
}
#[test]
fn test_emit_hidden() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_hidden("hidden_sym");
let sym = s.symbols.get("hidden_sym").unwrap();
assert!(sym.is_hidden);
}
#[test]
fn test_emit_protected() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_protected("protected_sym");
let sym = s.symbols.get("protected_sym").unwrap();
assert!(sym.is_protected);
}
#[test]
fn test_emit_internal() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_internal("internal_sym");
let sym = s.symbols.get("internal_sym").unwrap();
assert!(sym.is_internal);
}
#[test]
fn test_emit_type_function() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_type("myfunc", "@function");
let sym = s.symbols.get("myfunc").unwrap();
assert!(sym.is_function);
assert_eq!(sym.typ, "@function");
}
#[test]
fn test_emit_type_object() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_type("myobj", "@object");
let sym = s.symbols.get("myobj").unwrap();
assert!(sym.is_object);
}
#[test]
fn test_emit_size() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_size("myfunc", 42);
let sym = s.symbols.get("myfunc").unwrap();
assert_eq!(sym.size, 42);
}
#[test]
fn test_emit_label() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 100;
s.emit_label("mylabel");
assert_eq!(s.labels.len(), 1);
assert_eq!(s.labels[0].name, "mylabel");
assert_eq!(s.labels[0].offset, 100);
}
#[test]
fn test_emit_local_label() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_label(".L1");
assert!(s.labels[0].is_local);
}
#[test]
fn test_multiple_symbol_attributes() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_globl("multi");
s.emit_hidden("multi");
s.emit_type("multi", "@function");
let sym = s.symbols.get("multi").unwrap();
assert!(sym.is_global);
assert!(sym.is_hidden);
assert!(sym.is_function);
}
#[test]
fn test_emit_align_zero() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_align(0, None, None);
assert_eq!(s.current_offset, 0);
}
#[test]
fn test_emit_align_already_aligned() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 16;
s.emit_align(16, None, None);
assert_eq!(s.current_offset, 16);
}
#[test]
fn test_emit_align_needs_padding() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 1;
s.emit_align(16, None, None);
assert_eq!(s.current_offset, 16);
}
#[test]
fn test_emit_align_with_max_bytes() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 1;
s.emit_align(256, None, Some(5));
assert_eq!(s.current_offset, 1);
}
#[test]
fn test_emit_p2align() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 3;
s.emit_p2align(4, None, None);
assert_eq!(s.current_offset, 16);
}
#[test]
fn test_emit_balign() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 5;
s.emit_balign(8, Some(0xCC));
assert_eq!(s.current_offset, 8);
assert_eq!(s.binary_buffer[5], 0xCC);
}
#[test]
fn test_emit_org_forward() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_org(100, Some(0x90));
assert_eq!(s.current_offset, 100);
assert_eq!(s.binary_buffer.len(), 100);
assert_eq!(s.binary_buffer[0], 0x90);
}
#[test]
fn test_emit_org_backward() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 50;
s.emit_org(10, Some(0));
assert_eq!(s.current_offset, 50);
}
#[test]
fn test_emit_fill() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_fill(4, &[0xDE, 0xAD], 2);
assert_eq!(s.current_offset, 8);
assert_eq!(s.binary_buffer.len(), 8);
assert_eq!(s.binary_buffer[0], 0xDE);
assert_eq!(s.binary_buffer[1], 0xAD);
}
#[test]
fn test_emit_byte() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_byte(&[0x01, 0x02, 0x03]);
assert_eq!(s.current_offset, 3);
assert_eq!(s.binary_buffer, vec![0x01, 0x02, 0x03]);
assert_eq!(s.data_count, 3);
}
#[test]
fn test_emit_short() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_short(&[0x0102, 0x0304]);
assert_eq!(s.current_offset, 4);
assert_eq!(s.binary_buffer[0], 0x02);
assert_eq!(s.binary_buffer[1], 0x01);
assert_eq!(s.binary_buffer[2], 0x04);
assert_eq!(s.binary_buffer[3], 0x03);
}
#[test]
fn test_emit_long() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_long(&[0x01020304]);
assert_eq!(s.current_offset, 4);
assert_eq!(s.binary_buffer.len(), 4);
assert_eq!(s.binary_buffer[0], 0x04);
assert_eq!(s.binary_buffer[3], 0x01);
}
#[test]
fn test_emit_quad() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_quad(&[0x0102030405060708]);
assert_eq!(s.current_offset, 8);
assert_eq!(s.binary_buffer.len(), 8);
assert_eq!(s.binary_buffer[0], 0x08);
assert_eq!(s.binary_buffer[7], 0x01);
}
#[test]
fn test_emit_octa() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_octa(&[0]);
assert_eq!(s.current_offset, 16);
assert_eq!(s.binary_buffer.len(), 16);
}
#[test]
fn test_emit_ascii() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ascii("hello");
assert_eq!(s.current_offset, 5);
assert_eq!(s.binary_buffer, b"hello");
assert_eq!(s.binary_buffer.len(), 5);
}
#[test]
fn test_emit_asciz() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_asciz("hello");
assert_eq!(s.current_offset, 6);
assert_eq!(&s.binary_buffer[..5], b"hello");
assert_eq!(s.binary_buffer[5], 0);
}
#[test]
fn test_emit_string() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_string("test");
assert_eq!(s.current_offset, 5);
assert_eq!(s.binary_buffer[4], 0);
}
#[test]
fn test_emit_space() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_space(10, Some(0x00));
assert_eq!(s.current_offset, 10);
assert_eq!(s.binary_buffer.len(), 10);
assert!(s.binary_buffer.iter().all(|&b| b == 0));
}
#[test]
fn test_emit_skip() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_skip(5, Some(0xFF));
assert_eq!(s.current_offset, 5);
assert!(s.binary_buffer.iter().all(|&b| b == 0xFF));
}
#[test]
fn test_emit_zero() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_zero(8);
assert_eq!(s.current_offset, 8);
assert!(s.binary_buffer.iter().all(|&b| b == 0));
}
#[test]
fn test_emit_value() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_value(&[42]);
assert_eq!(s.current_offset, 2);
}
#[test]
fn test_emit_2byte() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_2byte(&[100]);
assert_eq!(s.current_offset, 2);
}
#[test]
fn test_emit_4byte() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_4byte(&[1000]);
assert_eq!(s.current_offset, 4);
}
#[test]
fn test_emit_8byte() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_8byte(&[100000]);
assert_eq!(s.current_offset, 8);
}
#[test]
fn test_emit_comm() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_comm("common_var", 1024, Some(8));
let sym = s.symbols.get("common_var").unwrap();
assert_eq!(sym.size, 1024);
assert_eq!(sym.section, "*COM*");
assert!(sym.is_global);
}
#[test]
fn test_emit_lcomm() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_lcomm("local_var", 512, Some(4));
let sym = s.symbols.get("local_var").unwrap();
assert_eq!(sym.size, 512);
assert!(sym.is_local);
}
#[test]
fn test_emit_common() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_common("com_var", 256, None);
let sym = s.symbols.get("com_var").unwrap();
assert_eq!(sym.size, 256);
}
#[test]
fn test_emit_equ_numeric() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_equ("SIZE", "64");
let sym = s.symbols.get("SIZE").unwrap();
assert_eq!(sym.value, 64);
}
#[test]
fn test_emit_equ_hex() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_equ("FLAG", "0xFF");
let sym = s.symbols.get("FLAG").unwrap();
assert_eq!(sym.value, 255);
}
#[test]
fn test_emit_set() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_set("RECONF", "42");
let sym = s.symbols.get("RECONF").unwrap();
assert_eq!(sym.value, 42);
}
#[test]
fn test_emit_file() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_file(1, "main.c");
assert_eq!(s.file_info.file_id, 1);
assert_eq!(s.file_info.filename, "main.c");
}
#[test]
fn test_emit_loc() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_loc(1, 42, 10);
assert_eq!(s.file_info.file_id, 1);
assert_eq!(s.file_info.line, 42);
assert_eq!(s.file_info.column, 10);
assert!(s.file_info.is_stmt);
}
#[test]
fn test_emit_line() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_line(100);
assert_eq!(s.file_info.line, 100);
}
#[test]
fn test_emit_ident() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ident("GCC: (test) 1.0");
assert_eq!(s.current_section, ".text");
}
#[test]
fn test_cfi_startproc_endproc() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert!(!s.cfi_active);
s.emit_cfi_startproc();
assert!(s.cfi_active);
assert!(s.cfi_state.is_active);
s.emit_cfi_endproc();
assert!(!s.cfi_active);
assert!(!s.cfi_state.is_active);
}
#[test]
fn test_cfi_def_cfa() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_def_cfa(7, 8); assert_eq!(s.cfi_state.cfa_register, 7);
assert_eq!(s.cfi_state.cfa_offset, 8);
}
#[test]
fn test_cfi_offset() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_offset(16, -8); assert_eq!(s.cfi_state.reg_offsets.get(&16), Some(&-8));
}
#[test]
fn test_cfi_def_cfa_register() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_def_cfa_register(6); assert_eq!(s.cfi_state.cfa_register, 6);
}
#[test]
fn test_cfi_def_cfa_offset() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_def_cfa_offset(16);
assert_eq!(s.cfi_state.cfa_offset, 16);
}
#[test]
fn test_cfi_restore() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_offset(16, -8);
assert!(s.cfi_state.reg_offsets.contains_key(&16));
s.emit_cfi_restore(16);
assert!(!s.cfi_state.reg_offsets.contains_key(&16));
}
#[test]
fn test_cfi_remember_restore_state() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_def_cfa(7, 8);
s.emit_cfi_remember_state();
s.emit_cfi_def_cfa(6, 16);
assert_eq!(s.cfi_state.cfa_register, 6);
assert_eq!(s.cfi_state.cfa_offset, 16);
s.emit_cfi_restore_state();
assert_eq!(s.cfi_state.cfa_register, 7);
assert_eq!(s.cfi_state.cfa_offset, 8);
}
#[test]
fn test_cfi_remember_restore_state_nested() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_def_cfa(7, 8); s.emit_cfi_remember_state();
s.emit_cfi_def_cfa(6, 16); s.emit_cfi_remember_state();
s.emit_cfi_def_cfa(5, 24); s.emit_cfi_restore_state();
assert_eq!(s.cfi_state.cfa_register, 6);
assert_eq!(s.cfi_state.cfa_offset, 16);
s.emit_cfi_restore_state(); assert_eq!(s.cfi_state.cfa_register, 7);
assert_eq!(s.cfi_state.cfa_offset, 8);
}
#[test]
fn test_cfi_escape() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_escape(&[0x0A, 0x0B, 0x0C]);
assert_eq!(s.binary_buffer, vec![0x0A, 0x0B, 0x0C]);
}
#[test]
fn test_seh_proc_endproc() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_seh_proc("myfunc");
assert!(s.seh_active);
s.emit_seh_endproc();
assert!(!s.seh_active);
}
#[test]
fn test_seh_on_unsupported_format() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_seh_proc("myfunc");
assert!(!s.seh_active);
}
#[test]
fn test_seh_endprologue() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_seh_proc("test");
s.emit_seh_endprologue();
assert!(s.seh_state.prologue_end);
}
#[test]
fn test_seh_pushreg() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_seh_proc("test");
s.emit_seh_pushreg(5); assert_eq!(s.seh_state.push_regs, vec![5]);
}
#[test]
fn test_seh_setframe() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_seh_proc("test");
s.emit_seh_setframe(5, 0);
assert_eq!(s.seh_state.setframe_reg, 5);
assert_eq!(s.seh_state.setframe_offset, 0);
}
#[test]
fn test_seh_stackalloc() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_seh_proc("test");
s.emit_seh_stackalloc(0x20);
assert_eq!(s.seh_state.stackalloc_size, 0x20);
}
#[test]
fn test_seh_savereg() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_seh_proc("test");
s.emit_seh_savereg(13, 0x10);
assert_eq!(s.seh_state.saveregs, vec![(13, 0x10)]);
}
#[test]
fn test_seh_pushframe() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_seh_proc("test");
s.emit_seh_pushframe(0);
}
#[test]
fn test_seh_handler() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_seh_proc("test");
s.emit_seh_handler("__C_specific_handler", Some("$handler_data$0"));
assert_eq!(s.seh_state.handler.as_deref(), Some("__C_specific_handler"));
assert_eq!(s.seh_state.handler_data.as_deref(), Some("$handler_data$0"));
}
#[test]
fn test_cv_file() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_cv_file(1, "test.cpp");
assert_eq!(s.cv_files.get(&1), Some(&"test.cpp".to_string()));
}
#[test]
fn test_cv_file_unsupported_format() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cv_file(1, "test.cpp");
assert!(s.cv_files.is_empty());
}
#[test]
fn test_cv_func_id() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_cv_func_id(42, "main");
assert_eq!(s.cv_funcs.get(&42), Some(&"main".to_string()));
}
#[test]
fn test_cv_loc() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_cv_loc(1, 2, 10, 5);
assert_eq!(s.file_info.file_id, 2);
assert_eq!(s.file_info.line, 10);
}
#[test]
fn test_cv_inline_site_id() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
s.emit_cv_inline_site_id(1, 42, 3, 15, 2);
let site = s.cv_inline_sites.get(&1).unwrap();
assert_eq!(site.parent_func_id, 42);
assert_eq!(site.file_id, 3);
assert_eq!(site.line, 15);
assert_eq!(site.col, 2);
}
#[test]
fn test_emit_macro_begin_and_lines() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_macro_begin("MY_MACRO", &["arg1", "arg2"]);
assert!(s.macros.contains_key("MY_MACRO"));
s.emit_macro_line("MY_MACRO", "mov %arg1, %arg2");
let body = s.macros.get("MY_MACRO").unwrap();
assert_eq!(body.len(), 1);
assert_eq!(body[0], "mov %arg1, %arg2");
}
#[test]
fn test_emit_endm() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_macro_begin("TMP", &[]);
s.emit_endm();
}
#[test]
fn test_emit_rept_and_endr() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_rept(10);
s.emit_endr();
}
#[test]
fn test_emit_irp() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_irp("reg", &["eax", "ebx", "ecx"]);
}
#[test]
fn test_emit_irpc() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_irpc("c", "abc");
}
#[test]
fn test_emit_if_true() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_if("1");
assert!(!s.is_skipping());
}
#[test]
fn test_emit_if_false() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_if("0");
assert!(s.is_skipping());
}
#[test]
fn test_emit_ifdef_defined() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_equ("DEFINED", "1");
s.emit_ifdef("DEFINED");
assert!(!s.is_skipping());
}
#[test]
fn test_emit_ifdef_undefined() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ifdef("UNDEFINED");
assert!(s.is_skipping());
}
#[test]
fn test_emit_ifndef() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ifndef("UNDEFINED");
assert!(!s.is_skipping());
}
#[test]
fn test_emit_ifeq_match() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ifeq("abc", "abc");
assert!(!s.is_skipping());
}
#[test]
fn test_emit_ifeq_no_match() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ifeq("abc", "def");
assert!(s.is_skipping());
}
#[test]
fn test_emit_ifne() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ifne("abc", "def");
assert!(!s.is_skipping());
}
#[test]
fn test_emit_iflt() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_iflt("1", "2");
assert!(!s.is_skipping());
}
#[test]
fn test_emit_ifle() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ifle("1", "1");
assert!(!s.is_skipping());
}
#[test]
fn test_emit_ifgt() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ifgt("2", "1");
assert!(!s.is_skipping());
}
#[test]
fn test_emit_ifge() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ifge("2", "2");
assert!(!s.is_skipping());
}
#[test]
fn test_emit_if_else_endif() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_if("0");
assert!(s.is_skipping());
s.emit_else();
assert!(!s.is_skipping());
s.emit_endif();
assert!(s.cond_stack.is_empty());
}
#[test]
fn test_emit_if_else_endif_true_branch() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_if("1");
assert!(!s.is_skipping());
s.emit_else();
assert!(s.is_skipping());
s.emit_endif();
}
#[test]
fn test_conditional_nesting() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_if("1");
assert!(!s.is_skipping());
s.emit_if("0");
assert!(s.is_skipping());
s.emit_endif();
assert!(!s.is_skipping());
s.emit_endif();
}
#[test]
fn test_emit_include() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_include("header.inc");
}
#[test]
fn test_emit_incbin() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_incbin("data.bin", Some(0), Some(1024));
}
#[test]
fn test_emit_error() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_error("something went wrong");
}
#[test]
fn test_emit_warning() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_warning("deprecated feature");
}
#[test]
fn test_emit_print() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_print("hello world");
}
#[test]
fn test_bundle_lock_unlock() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert!(!s.bundle_locked);
s.emit_bundle_lock();
assert!(s.bundle_locked);
s.emit_bundle_unlock();
assert!(!s.bundle_locked);
}
#[test]
fn test_tls_model_general_dynamic() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_tls_model("global-dynamic");
assert_eq!(s.tls_model, TlsModel::GeneralDynamic);
}
#[test]
fn test_tls_model_local_exec() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_tls_model("local-exec");
assert_eq!(s.tls_model, TlsModel::LocalExec);
}
#[test]
fn test_gnu_attribute() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_gnu_attribute("Tag_CPU_raw_name", "corei7");
assert_eq!(s.gnu_attributes.len(), 1);
assert_eq!(s.gnu_attributes[0].0, "Tag_CPU_raw_name");
assert_eq!(s.gnu_attributes[0].1, "corei7");
}
#[test]
fn test_emit_altmacro_noaltmacro() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert!(!s.altmacro);
s.emit_altmacro();
assert!(s.altmacro);
s.emit_noaltmacro();
assert!(!s.altmacro);
}
#[test]
fn test_emit_note() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_note("GNU", 1, &[0x00, 0x00, 0x00, 0x00]);
}
#[test]
fn test_emit_stab() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_stab(0x64, 0x00, 0x0000, 0x00000000);
}
#[test]
fn test_emit_stabs() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_stabs("main:F(0,1)");
}
#[test]
fn test_emit_stabn() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_stabn(0x24, 0x00, 0x0003, 0x00000000);
}
#[test]
fn test_add_fixup() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.add_fixup(0, FixupKind::GotPcRel32, "foo", -4);
assert_eq!(s.fixups.len(), 1);
assert_eq!(s.fixups[0].kind, FixupKind::GotPcRel32);
assert_eq!(s.fixups[0].symbol, "foo");
assert_eq!(s.fixups[0].addend, -4);
}
#[test]
fn test_add_multiple_fixups() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.add_fixup(0, FixupKind::GotPcRel32, "a", 0);
s.add_fixup(4, FixupKind::Plt32, "b", -4);
s.add_fixup(8, FixupKind::Rel32, "c", 0);
assert_eq!(s.fixups.len(), 3);
}
#[test]
fn test_streamer_reset() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_byte(&[1, 2, 3]);
s.emit_globl("x");
s.emit_equ("y", "10");
s.emit_cfi_startproc();
s.add_fixup(0, FixupKind::Rel32, "z", 0);
s.reset();
assert_eq!(s.current_section, ".text");
assert_eq!(s.current_offset, 0);
assert!(s.symbols.is_empty());
assert!(s.equates.is_empty());
assert!(s.fixups.is_empty());
assert!(!s.cfi_active);
}
#[test]
fn test_asm_streamer_creation() {
let a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
assert!(a.output.is_empty());
assert_eq!(a.line_count, 0);
}
#[test]
fn test_asm_streamer_from_triple() {
let a = X86TargetAsmStreamer::from_triple("x86_64-unknown-linux-gnu");
assert_eq!(a.streamer.format, TargetObjectFormat::ELF);
assert!(a.streamer.is_64bit);
}
#[test]
fn test_asm_streamer_from_triple_windows() {
let a = X86TargetAsmStreamer::from_triple("x86_64-pc-windows-msvc");
assert_eq!(a.streamer.format, TargetObjectFormat::COFF);
}
#[test]
fn test_asm_streamer_from_triple_macos() {
let a = X86TargetAsmStreamer::from_triple("x86_64-apple-darwin");
assert_eq!(a.streamer.format, TargetObjectFormat::MachO);
}
#[test]
fn test_asm_streamer_from_triple_i386() {
let a = X86TargetAsmStreamer::from_triple("i386-unknown-linux-gnu");
assert!(!a.streamer.is_64bit);
}
#[test]
fn test_asm_emit_text() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_text();
assert!(a.output.contains(".text"));
}
#[test]
fn test_asm_emit_data() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_data();
assert!(a.output.contains(".data"));
}
#[test]
fn test_asm_emit_section() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_section(".mytext", Some("ax"), Some("progbits"));
assert!(a.output.contains(".section"));
assert!(a.output.contains(".mytext"));
}
#[test]
fn test_asm_emit_globl() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_globl("main");
assert!(a.output.contains(".globl main"));
}
#[test]
fn test_asm_emit_type_function() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_type("myfunc", "@function");
assert!(a.output.contains(".type myfunc, @function"));
}
#[test]
fn test_asm_emit_size() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_size("myfunc", 64);
assert!(a.output.contains(".size myfunc, 64"));
}
#[test]
fn test_asm_emit_byte() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_byte(&[0x90, 0x90, 0x90]);
assert!(a.output.contains(".byte"));
assert!(a.output.contains("144"));
}
#[test]
fn test_asm_emit_short() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_short(&[42]);
assert!(a.output.contains(".short"));
assert!(a.output.contains("42"));
}
#[test]
fn test_asm_emit_long() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_long(&[1000]);
assert!(a.output.contains(".long"));
assert!(a.output.contains("1000"));
}
#[test]
fn test_asm_emit_quad() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_quad(&[100000]);
assert!(a.output.contains(".quad"));
}
#[test]
fn test_asm_emit_ascii() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_ascii("hello");
assert!(a.output.contains(".ascii"));
assert!(a.output.contains("hello"));
}
#[test]
fn test_asm_emit_asciz() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_asciz("test");
assert!(a.output.contains(".asciz"));
}
#[test]
fn test_asm_emit_string() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_string("str");
assert!(a.output.contains(".string"));
}
#[test]
fn test_asm_emit_space() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_space(16, Some(0));
assert!(a.output.contains(".space 16, 0"));
}
#[test]
fn test_asm_emit_align() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_align(4, Some(0x90), None);
assert!(a.output.contains(".align 4, 144"));
}
#[test]
fn test_asm_emit_p2align() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_p2align(4, None, None);
assert!(a.output.contains(".p2align 4"));
}
#[test]
fn test_asm_emit_balign() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_balign(16, Some(0xCC));
assert!(a.output.contains(".balign 16, 204"));
}
#[test]
fn test_asm_emit_org() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_org(256, None);
assert!(a.output.contains(".org 256"));
}
#[test]
fn test_asm_emit_fill() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_fill(4, &[0x90], 1);
assert!(a.output.contains(".fill"));
}
#[test]
fn test_asm_emit_comm() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_comm("global_var", 256, Some(8));
assert!(a.output.contains(".comm global_var,256,8"));
}
#[test]
fn test_asm_emit_lcomm() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_lcomm("local_var", 128, Some(4));
assert!(a.output.contains(".lcomm local_var,128,4"));
}
#[test]
fn test_asm_emit_equ() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_equ("MAX", "100");
assert!(a.output.contains(".equ MAX, 100"));
}
#[test]
fn test_asm_emit_cfi_directives() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_cfi_startproc();
a.emit_asm_cfi_def_cfa(7, 8);
a.emit_asm_cfi_offset(16, -8);
a.emit_asm_cfi_endproc();
assert!(a.output.contains(".cfi_startproc"));
assert!(a.output.contains(".cfi_def_cfa 7, 8"));
assert!(a.output.contains(".cfi_offset 16, -8"));
assert!(a.output.contains(".cfi_endproc"));
}
#[test]
fn test_asm_emit_cfi_remember_restore() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_cfi_remember_state();
a.emit_asm_cfi_restore_state();
assert!(a.output.contains(".cfi_remember_state"));
assert!(a.output.contains(".cfi_restore_state"));
}
#[test]
fn test_asm_emit_cfi_escape() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_cfi_escape(&[0x0A, 0x0B]);
assert!(a.output.contains(".cfi_escape 0x0a, 0x0b"));
}
#[test]
fn test_asm_emit_seh_directives() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::COFF, true);
a.emit_asm_seh_proc("myfunc");
a.emit_asm_seh_pushreg(4);
a.emit_asm_seh_endprologue();
a.emit_asm_seh_endproc();
assert!(a.output.contains(".seh_proc myfunc"));
assert!(a.output.contains(".seh_pushreg 4"));
assert!(a.output.contains(".seh_endprologue"));
assert!(a.output.contains(".seh_endproc"));
}
#[test]
fn test_asm_emit_seh_handler() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::COFF, true);
a.emit_asm_seh_handler("__C_specific_handler", Some("$data$0"));
assert!(a
.output
.contains(".seh_handler __C_specific_handler, $data$0"));
}
#[test]
fn test_asm_emit_cv_directives() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::COFF, true);
a.emit_asm_cv_file(1, "test.cpp");
a.emit_asm_cv_func_id(1, "main");
a.emit_asm_cv_loc(1, 1, 10, 5);
assert!(a.output.contains(".cv_file 1, \"test.cpp\""));
assert!(a.output.contains(".cv_func_id 1, main"));
assert!(a.output.contains(".cv_loc 1, 1, 10, 5"));
}
#[test]
fn test_asm_emit_debug_directives() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_file(1, "main.c");
a.emit_asm_loc(1, 5, 3);
a.emit_asm_line(10);
a.emit_asm_ident("GCC: 1.0");
assert!(a.output.contains(".file 1 \"main.c\""));
assert!(a.output.contains(".loc 1 5 3"));
assert!(a.output.contains(".line 10"));
assert!(a.output.contains(".ident \"GCC: 1.0\""));
}
#[test]
fn test_asm_emit_conditional_directives() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_if("1");
a.emit_asm_else();
a.emit_asm_endif();
assert!(a.output.contains(".if 1"));
assert!(a.output.contains(".else"));
assert!(a.output.contains(".endif"));
}
#[test]
fn test_asm_emit_ifdef_ifndef() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_ifdef("DEBUG");
a.emit_asm_endif();
a.emit_asm_ifndef("NDEBUG");
a.emit_asm_endif();
assert!(a.output.contains(".ifdef DEBUG"));
assert!(a.output.contains(".ifndef NDEBUG"));
}
#[test]
fn test_asm_emit_comparison_directives() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_ifeq("A", "B");
a.emit_asm_endif();
a.emit_asm_ifne("C", "D");
a.emit_asm_endif();
a.emit_asm_iflt("1", "2");
a.emit_asm_endif();
a.emit_asm_ifle("3", "4");
a.emit_asm_endif();
a.emit_asm_ifgt("5", "6");
a.emit_asm_endif();
a.emit_asm_ifge("7", "8");
a.emit_asm_endif();
assert!(a.output.contains(".ifeq A B"));
assert!(a.output.contains(".ifne C D"));
assert!(a.output.contains(".iflt 1 2"));
assert!(a.output.contains(".ifle 3 4"));
assert!(a.output.contains(".ifgt 5 6"));
assert!(a.output.contains(".ifge 7 8"));
}
#[test]
fn test_asm_emit_macro_repeat_directives() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_macro_begin("PUSH_ALL", &[]);
a.emit_asm_endm();
a.emit_asm_rept(5);
a.emit_asm_endr();
a.emit_asm_irp("r", &["eax", "ebx"]);
a.emit_asm_irpc("c", "xyz");
assert!(a.output.contains(".macro PUSH_ALL"));
assert!(a.output.contains(".endm"));
assert!(a.output.contains(".rept 5"));
assert!(a.output.contains(".endr"));
assert!(a.output.contains(".irp r, eax,ebx"));
assert!(a.output.contains(".irpc c, xyz"));
}
#[test]
fn test_asm_emit_include_incbin() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_include("header.h");
a.emit_asm_incbin("data.bin", None, None);
assert!(a.output.contains(".include \"header.h\""));
assert!(a.output.contains(".incbin \"data.bin\""));
}
#[test]
fn test_asm_emit_error_warning_print() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_error("stop here");
a.emit_asm_warning("check this");
a.emit_asm_print("hello");
assert!(a.output.contains(".error \"stop here\""));
assert!(a.output.contains(".warning \"check this\""));
assert!(a.output.contains(".print \"hello\""));
}
#[test]
fn test_asm_emit_bundle() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_bundle_lock();
a.emit_asm_bundle_unlock();
assert!(a.output.contains(".bundle_lock"));
assert!(a.output.contains(".bundle_unlock"));
}
#[test]
fn test_asm_emit_misc() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::MachO, true);
a.emit_asm_no_dead_strip();
a.emit_asm_subsections_via_symbols();
a.emit_asm_lazy_reference("_foo");
a.emit_asm_reference("_bar");
assert!(a.output.contains(".no_dead_strip"));
assert!(a.output.contains(".subsections_via_symbols"));
assert!(a.output.contains(".lazy_reference _foo"));
assert!(a.output.contains(".reference _bar"));
}
#[test]
fn test_asm_emit_tls_model() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_tls_model("initial-exec");
assert!(a.output.contains(".tls_model initial-exec"));
}
#[test]
fn test_asm_emit_gnu_attribute() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_gnu_attribute("Tag_CPU_name", "x86-64");
assert!(a.output.contains(".gnu_attribute Tag_CPU_name, x86-64"));
}
#[test]
fn test_asm_emit_stab_directives() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_stab(0x64, 0x00, 0x0000, 0x00000000);
a.emit_asm_stabs("test.s");
a.emit_asm_stabn(0x24, 0x00, 0x0003, 0x00000000);
assert!(a.output.contains(".stab"));
assert!(a.output.contains(".stabs"));
assert!(a.output.contains(".stabn"));
}
#[test]
fn test_asm_emit_altmacro() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_altmacro();
a.emit_asm_noaltmacro();
assert!(a.output.contains(".altmacro"));
assert!(a.output.contains(".noaltmacro"));
}
#[test]
fn test_asm_emit_instruction() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_instruction("nop", "");
a.emit_instruction("mov", "%eax, %ebx");
assert!(a.output.contains("nop"));
assert!(a.output.contains("mov"));
assert!(a.output.contains("%eax, %ebx"));
}
#[test]
fn test_asm_emit_global_label() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_global_label("main");
assert!(a.output.contains(".globl main"));
assert!(a.output.contains("main:"));
}
#[test]
fn test_asm_emit_local_label() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_local_label("L123");
assert!(a.output.contains(".LL123:"));
}
#[test]
fn test_asm_macho_label_prefix() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::MachO, true);
a.emit_local_label("foo");
assert!(a.output.contains("Lfoo:"));
}
#[test]
fn test_asm_emit_comment() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_comment("This is a comment");
assert!(a.output.contains("# This is a comment"));
}
#[test]
fn test_asm_emit_blank_line() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_blank_line();
assert!(a.output.ends_with("\n\n"));
}
#[test]
fn test_asm_finalize() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_text();
let output = a.finalize();
assert!(!output.is_empty());
}
#[test]
fn test_asm_into_string() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_data();
let s = a.into_string();
assert!(s.contains(".data"));
}
#[test]
fn test_asm_clear() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_text();
assert!(!a.output.is_empty());
a.clear();
assert!(a.output.is_empty());
assert_eq!(a.line_count, 0);
}
#[test]
fn test_asm_default() {
let a = X86TargetAsmStreamer::default();
assert_eq!(a.streamer.format, TargetObjectFormat::ELF);
assert!(a.streamer.is_64bit);
}
#[test]
fn test_asm_emit_pushsection_popsection() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_pushsection(".data", None, None);
a.emit_asm_popsection();
assert!(a.output.contains(".pushsection .data"));
assert!(a.output.contains(".popsection"));
}
#[test]
fn test_asm_emit_previous() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_previous();
assert!(a.output.contains(".previous"));
}
#[test]
fn test_asm_emit_value_2byte_4byte_8byte() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_value(&[42]);
a.emit_asm_2byte(&[100]);
a.emit_asm_4byte(&[1000]);
a.emit_asm_8byte(&[100000]);
assert!(a.output.contains(".short"));
assert!(a.output.contains(".long"));
assert!(a.output.contains(".quad"));
}
#[test]
fn test_asm_full_function_assembly() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_text();
a.emit_asm_file(1, "test.c");
a.emit_asm_globl("myfunc");
a.emit_asm_type("myfunc", "@function");
a.emit_global_label("myfunc");
a.emit_asm_cfi_startproc();
a.emit_asm_cfi_def_cfa(7, 8);
a.emit_asm_cfi_offset(16, -8);
a.emit_instruction("push", "%rbp");
a.emit_instruction("mov", "%rsp, %rbp");
a.emit_asm_loc(1, 2, 1);
a.emit_instruction("mov", "$42, %eax");
a.emit_instruction("pop", "%rbp");
a.emit_instruction("ret", "");
a.emit_asm_cfi_endproc();
a.emit_asm_size("myfunc", 16);
let output = a.as_str();
assert!(output.contains(".text"));
assert!(output.contains(".globl myfunc"));
assert!(output.contains(".type myfunc, @function"));
assert!(output.contains("myfunc:"));
assert!(output.contains(".cfi_startproc"));
assert!(output.contains(".cfi_def_cfa 7, 8"));
assert!(output.contains("push"));
assert!(output.contains("ret"));
assert!(output.contains(".size myfunc, 16"));
}
#[test]
fn test_object_streamer_creation() {
let o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(o.streamer.format, TargetObjectFormat::ELF);
assert!(o.section_data.contains_key(".text"));
}
#[test]
fn test_object_streamer_from_triple() {
let o = X86TargetObjectStreamer::from_triple("x86_64-pc-windows-msvc");
assert_eq!(o.streamer.format, TargetObjectFormat::COFF);
}
#[test]
fn test_object_switch_section() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.switch_section(".data", None, None);
assert_eq!(o.streamer.current_section, ".data");
}
#[test]
fn test_object_emit_bytes() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_bytes(&[1, 2, 3]);
assert_eq!(o.streamer.current_offset, 3);
}
#[test]
fn test_object_emit_byte() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_obj_byte(&[0x90, 0x90]);
assert_eq!(o.streamer.current_offset, 2);
}
#[test]
fn test_object_emit_short() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_obj_short(&[0x1234]);
assert_eq!(o.streamer.current_offset, 2);
}
#[test]
fn test_object_emit_long() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_obj_long(&[0x12345678]);
assert_eq!(o.streamer.current_offset, 4);
}
#[test]
fn test_object_emit_quad() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_obj_quad(&[0x1234567890ABCDEF]);
assert_eq!(o.streamer.current_offset, 8);
}
#[test]
fn test_object_emit_align() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_bytes(&[0]); o.emit_obj_align(16, None);
assert_eq!(o.streamer.current_offset, 16);
}
#[test]
fn test_object_emit_fill() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_obj_fill(4, &[0xCC], 1);
assert_eq!(o.streamer.current_offset, 4);
}
#[test]
fn test_object_emit_zero() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_obj_zero(100);
assert_eq!(o.streamer.current_offset, 100);
}
#[test]
fn test_object_define_symbol() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.define_symbol(
"main",
SymbolBinding::Global,
SymbolType::Func,
SymbolVisibility::Default,
);
assert_eq!(o.symbols.len(), 1);
assert_eq!(o.symbols[0].name, "main");
}
#[test]
fn test_object_define_label() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.define_label("my_label", false);
assert_eq!(o.labels.len(), 1);
assert_eq!(o.labels[0].name, "my_label");
}
#[test]
fn test_object_new_temp_label() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
let label1 = o.new_temp_label();
let label2 = o.new_temp_label();
assert!(label1.starts_with(".Ltmp"));
assert!(label2.starts_with(".Ltmp"));
assert_ne!(label1, label2);
}
#[test]
fn test_object_add_fixup() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.add_fixup(FixupKind::GotPcRel32, "printf", -4);
assert_eq!(o.streamer.fixups.len(), 1);
assert_eq!(o.streamer.fixups[0].symbol, "printf");
}
#[test]
fn test_object_emit_gotpcrel32_value() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_gotpcrel32_value("foo", 0);
assert_eq!(o.streamer.current_offset, 4);
let fixups = o.section_fixups.get(".text").unwrap();
assert_eq!(fixups.len(), 1);
assert_eq!(fixups[0].kind, FixupKind::GotPcRel32);
assert_eq!(fixups[0].symbol, "foo");
}
#[test]
fn test_object_emit_instruction_bytes() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_instruction_bytes(&[0x90, 0x90, 0x90]); assert_eq!(o.streamer.current_offset, 3);
assert_eq!(o.streamer.instruction_count, 1);
}
#[test]
fn test_object_section_data_accumulation() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_bytes(&[1, 2, 3]);
o.emit_bytes(&[4, 5, 6]);
o.finalize();
let data = o.get_section_data(".text").unwrap();
assert_eq!(data, &[1, 2, 3, 4, 5, 6]);
}
#[test]
fn test_object_multi_section() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_bytes(&[0x90]); o.switch_section(".data", None, None);
o.emit_bytes(&[0x41, 0x42]);
o.finalize();
assert_eq!(o.get_section_data(".text").unwrap(), &[0x90]);
assert_eq!(o.get_section_data(".data").unwrap(), &[0x41, 0x42]);
}
#[test]
fn test_object_section_names() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.switch_section(".rodata", None, None);
o.switch_section(".data", None, None);
o.switch_section(".bss", None, None);
let names = o.section_names();
assert!(names.contains(&".text".to_string()));
assert!(names.contains(&".data".to_string()));
assert!(names.contains(&".bss".to_string()));
assert!(names.contains(&".rodata".to_string()));
}
#[test]
fn test_object_section_alignment() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
let align = o.get_section_alignment(".text");
assert_eq!(align, Some(16));
}
#[test]
fn test_object_push_pop_section() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_bytes(&[0x90]);
o.push_section(".data", None, None);
o.emit_bytes(&[0x41]);
o.pop_section();
o.emit_bytes(&[0xC3]);
o.finalize();
assert_eq!(o.get_section_data(".text").unwrap(), &[0x90, 0xC3]);
assert_eq!(o.get_section_data(".data").unwrap(), &[0x41]);
}
#[test]
fn test_object_reset() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_bytes(&[0x90]);
o.define_symbol(
"x",
SymbolBinding::Global,
SymbolType::Func,
SymbolVisibility::Default,
);
o.reset();
assert_eq!(o.streamer.current_offset, 0);
assert!(o.symbols.is_empty());
assert_eq!(o.section_data.len(), 1); assert!(o.get_section_data(".text").unwrap().is_empty());
}
#[test]
fn test_object_default() {
let o = X86TargetObjectStreamer::default();
assert_eq!(o.streamer.format, TargetObjectFormat::ELF);
assert!(o.streamer.is_64bit);
}
#[test]
fn test_object_full_function_emission() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.define_label(".Lfunc_begin0", true);
o.define_symbol(
"myfunc",
SymbolBinding::Global,
SymbolType::Func,
SymbolVisibility::Default,
);
o.emit_instruction_bytes(&[0x55]);
o.emit_instruction_bytes(&[0x48, 0x89, 0xE5]);
o.emit_instruction_bytes(&[0xB8, 0x2A, 0x00, 0x00, 0x00]);
o.emit_instruction_bytes(&[0x5D]);
o.emit_instruction_bytes(&[0xC3]);
o.define_label(".Lfunc_end0", true);
o.finalize();
let text_data = o.get_section_data(".text").unwrap();
assert_eq!(
text_data,
&[0x55, 0x48, 0x89, 0xE5, 0xB8, 0x2A, 0x00, 0x00, 0x00, 0x5D, 0xC3]
);
assert_eq!(o.symbols.len(), 1);
assert_eq!(o.labels.len(), 2);
}
#[test]
fn test_gotpcrel_creation() {
let g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
assert!(g.is_supported());
assert_eq!(g.relocation_count, 0);
}
#[test]
fn test_gotpcrel_emit_gotpcrel32() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32("foo", 0);
assert_eq!(g.relocation_count, 1);
assert_eq!(g.streamer.current_offset, 4);
assert_eq!(g.streamer.fixups.len(), 1);
assert_eq!(g.streamer.fixups[0].kind, FixupKind::GotPcRel32);
assert_eq!(g.streamer.fixups[0].symbol, "foo");
}
#[test]
fn test_gotpcrel_emit_gotpcrel32_with_addend() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32("bar", -4);
assert_eq!(g.streamer.fixups[0].addend, -4);
}
#[test]
fn test_gotpcrel_emit_plt32() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_plt32("puts", -4);
assert_eq!(g.streamer.fixups[0].kind, FixupKind::Plt32);
assert_eq!(g.streamer.fixups[0].symbol, "puts");
}
#[test]
fn test_gotpcrel_emit_gotpcrel32_x32() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32_x32("x32sym");
assert_eq!(g.relocation_count, 1);
}
#[test]
fn test_gotpcrel_emit_gotpcrelx() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrelx("relaxable_sym");
assert_eq!(g.relocation_count, 1);
assert_eq!(g.streamer.fixups[0].kind, FixupKind::GotPcRel32);
}
#[test]
fn test_gotpcrel_emit_rex_gotpcrelx() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_rex_gotpcrelx("rex_sym");
assert_eq!(g.relocation_count, 1);
}
#[test]
fn test_gotpcrel_emit_tls() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32_tls("tls_var");
assert_eq!(g.streamer.fixups[0].kind, FixupKind::TlsGd);
}
#[test]
fn test_gotpcrel_emit_tlsdesc() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32_tlsdesc("tlsdesc_var");
assert_eq!(g.streamer.fixups[0].kind, FixupKind::GotPc32TlsDesc);
}
#[test]
fn test_gotpcrel_emit_got_off() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_got_off("got_var");
assert_eq!(g.streamer.fixups[0].kind, FixupKind::GotOff);
}
#[test]
fn test_gotpcrel_multiple_emissions() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32("a", 0);
g.emit_gotpcrel32("b", 0);
g.emit_plt32("c", -4);
g.emit_gotpcrel32_tls("d");
assert_eq!(g.relocation_count, 4);
assert_eq!(g.count(), 4);
assert_eq!(g.streamer.fixups.len(), 4);
assert_eq!(g.streamer.current_offset, 16);
}
#[test]
fn test_gotpcrel_format_gotpcrel() {
assert_eq!(X86EmitGPRel32Value::format_gotpcrel("foo"), "foo@GOTPCREL");
}
#[test]
fn test_gotpcrel_format_plt() {
assert_eq!(X86EmitGPRel32Value::format_plt("printf"), "printf@PLT");
}
#[test]
fn test_gotpcrel_format_gotoff() {
assert_eq!(X86EmitGPRel32Value::format_gotoff("var"), "var@GOTOFF");
}
#[test]
fn test_gotpcrel_format_tls() {
assert_eq!(
X86EmitGPRel32Value::format_gottpoff("tlsvar"),
"tlsvar@GOTTPOFF"
);
assert_eq!(X86EmitGPRel32Value::format_tpoff("tlsvar"), "tlsvar@TPOFF");
assert_eq!(
X86EmitGPRel32Value::format_dtpoff("tlsvar"),
"tlsvar@DTPOFF"
);
}
#[test]
fn test_gotpcrel_is_supported() {
let g_elf = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
assert!(g_elf.is_supported());
let g_coff = X86EmitGPRel32Value::new(TargetObjectFormat::COFF, true);
assert!(!g_coff.is_supported());
let g_macho = X86EmitGPRel32Value::new(TargetObjectFormat::MachO, true);
assert!(!g_macho.is_supported());
}
#[test]
fn test_gotpcrel_reset() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32("a", 0);
g.emit_plt32("b", -4);
g.reset();
assert_eq!(g.relocation_count, 0);
assert_eq!(g.count(), 0);
assert_eq!(g.streamer.fixups.len(), 0);
assert_eq!(g.streamer.current_offset, 0);
}
#[test]
fn test_gotpcrel_default() {
let g = X86EmitGPRel32Value::default();
assert!(g.is_supported());
assert!(g.streamer.is_64bit);
}
#[test]
fn test_integration_asm_streamer_with_cfi_and_seh() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_text();
a.emit_asm_globl("_start");
a.emit_global_label("_start");
a.emit_asm_cfi_startproc();
a.emit_asm_cfi_def_cfa(7, 8);
a.emit_asm_cfi_offset(6, -16);
a.emit_instruction("push", "%rbp");
a.emit_instruction("mov", "%rsp, %rbp");
a.emit_instruction("mov", "$0, %eax");
a.emit_instruction("leave", "");
a.emit_instruction("ret", "");
a.emit_asm_cfi_endproc();
a.emit_asm_size("_start", 16);
let output = a.as_str();
assert!(output.contains("_start:"));
assert!(output.contains(".cfi_def_cfa 7, 8"));
assert!(output.contains(".cfi_offset 6, -16"));
assert!(output.contains("leave"));
assert!(output.contains("ret"));
}
#[test]
fn test_integration_object_streamer_with_fixups() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.define_label(".Lfunc0", true);
o.define_symbol(
"external_func",
SymbolBinding::Global,
SymbolType::Func,
SymbolVisibility::Default,
);
o.emit_instruction_bytes(&[0x55]);
o.emit_instruction_bytes(&[0x48, 0x8D, 0x3D]);
o.emit_gotpcrel32_value("message", 0);
o.emit_instruction_bytes(&[0xE8]);
o.add_fixup(FixupKind::Plt32, "external_func", -4);
o.emit_instruction_bytes(&[0x5D, 0xC3]);
o.finalize();
let text_data = o.get_section_data(".text").unwrap();
assert_eq!(text_data.len(), 15);
assert_eq!(text_data[0], 0x55);
assert_eq!(text_data[14], 0xC3);
let fixups = o.get_section_fixups(".text").unwrap();
assert_eq!(fixups.len(), 2);
assert_eq!(fixups[0].kind, FixupKind::GotPcRel32);
assert_eq!(fixups[1].kind, FixupKind::Plt32);
}
#[test]
fn test_integration_directive_sequence() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_file(1, "main.c");
a.emit_asm_text();
a.emit_asm_globl("main");
a.emit_asm_type("main", "@function");
a.emit_global_label("main");
a.emit_asm_cfi_startproc();
a.emit_asm_loc(1, 1, 0);
a.emit_instruction("nop", "");
a.emit_asm_loc(1, 2, 0);
a.emit_asm_cfi_endproc();
a.emit_asm_size("main", 1);
a.emit_asm_data();
a.emit_asm_align(8, None, None);
a.emit_asm_byte(&[0x01, 0x02, 0x03, 0x04]);
a.emit_asm_asciz("hello world");
a.emit_asm_bss();
a.emit_asm_comm("global_buf", 4096, Some(16));
a.emit_asm_lcomm("local_buf", 256, Some(4));
a.emit_asm_ident("MyCompiler 1.0");
let output = a.as_str();
assert!(output.contains(".file"));
assert!(output.contains(".text"));
assert!(output.contains(".data"));
assert!(output.contains(".bss"));
assert!(output.contains(".comm"));
assert!(output.contains(".lcomm"));
assert!(output.contains(".ident"));
}
#[test]
fn test_integration_all_data_sizes() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_byte(&[0xFF]);
s.emit_short(&[0x1234]);
s.emit_long(&[0x12345678]);
s.emit_quad(&[0x1234567890ABCDEF]);
s.emit_octa(&[0]);
assert_eq!(s.current_offset, 31);
assert_eq!(s.binary_buffer.len(), 31);
}
#[test]
fn test_integration_pic_static_attributes() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.pic = true;
s.emit_globl("picfunc");
s.emit_weak("picvar");
s.emit_hidden("picfunc");
s.emit_protected("picvar");
assert!(s.symbols.get("picfunc").unwrap().is_hidden);
assert!(s.symbols.get("picvar").unwrap().is_protected);
assert!(s.symbols.get("picfunc").unwrap().is_global);
assert!(s.symbols.get("picvar").unwrap().is_weak);
}
#[test]
fn test_integration_macro_expansion_flow() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_macro_begin("SAVE_REGS", &[]);
s.emit_macro_line("SAVE_REGS", "push %rax");
s.emit_macro_line("SAVE_REGS", "push %rbx");
s.emit_macro_line("SAVE_REGS", "push %rcx");
s.emit_endm();
assert_eq!(s.macros.len(), 1);
let body = s.macros.get("SAVE_REGS").unwrap();
assert_eq!(body.len(), 3);
assert_eq!(body[2], "push %rcx");
}
#[test]
fn test_integration_conditional_with_equates() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_equ("DEBUG", "1");
s.emit_equ("VERSION", "2");
s.emit_ifdef("DEBUG");
assert!(!s.is_skipping());
s.emit_endif();
s.emit_ifgt("2", "1");
assert!(!s.is_skipping());
s.emit_endif();
}
#[test]
fn test_integration_section_stack_depth() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_pushsection(".data", None, None);
s.emit_pushsection(".bss", None, None);
s.emit_pushsection(".rodata", None, None);
assert_eq!(s.section_stack.len(), 3);
assert_eq!(s.current_section, ".rodata");
s.emit_popsection();
assert_eq!(s.current_section, ".bss");
s.emit_popsection();
assert_eq!(s.current_section, ".data");
s.emit_popsection();
assert_eq!(s.current_section, ".text");
}
#[test]
fn test_integration_string_escapes() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_ascii("hello\nworld\t!");
let output = a.as_str();
assert!(output.contains("\\n"));
assert!(output.contains("\\t"));
}
#[test]
fn test_integration_zero_size_emission() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ascii("");
assert_eq!(s.current_offset, 0);
s.emit_byte(&[]);
assert_eq!(s.current_offset, 0);
s.emit_space(0, None);
assert_eq!(s.current_offset, 0);
}
#[test]
fn test_integration_tls_model_enum() {
assert_eq!(TlsModel::default(), TlsModel::GeneralDynamic);
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(s.tls_model, TlsModel::GeneralDynamic);
s.emit_tls_model("local-dynamic");
assert_eq!(s.tls_model, TlsModel::LocalDynamic);
s.emit_tls_model("initial-exec");
assert_eq!(s.tls_model, TlsModel::InitialExec);
s.emit_tls_model("local-exec");
assert_eq!(s.tls_model, TlsModel::LocalExec);
s.emit_tls_model("unknown");
assert_eq!(s.tls_model, TlsModel::GeneralDynamic);
}
#[test]
fn test_integration_symbol_info_default() {
let info = SymbolInfo::default();
assert!(info.name.is_empty());
assert_eq!(info.value, 0);
assert!(!info.is_global);
assert!(!info.is_function);
}
#[test]
fn test_integration_cfi_state_default() {
let state = CfiState::default();
assert_eq!(state.cfa_register, 0);
assert_eq!(state.cfa_offset, 0);
assert!(!state.is_active);
}
#[test]
fn test_integration_seh_state_default() {
let state = SehState::default();
assert_eq!(state.push_regs.len(), 0);
assert_eq!(state.saveregs.len(), 0);
assert!(!state.prologue_end);
}
#[test]
fn test_integration_file_info_default() {
let info = FileInfo::default();
assert_eq!(info.filename, "<unknown>");
assert_eq!(info.directory, ".");
assert!(info.is_stmt);
}
#[test]
fn test_null_streamer_creation() {
let n = X86TargetNullStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(n.instruction_count, 0);
assert_eq!(n.directive_count, 0);
assert_eq!(n.byte_count, 0);
}
#[test]
fn test_null_streamer_counts_directives() {
let mut n = X86TargetNullStreamer::new(TargetObjectFormat::ELF, true);
n.streamer.emit_text();
n.streamer.emit_globl("main");
n.streamer.emit_byte(&[0x90, 0x90]);
n.update_counts();
assert!(n.directive_count > 0 || n.byte_count > 0);
}
#[test]
fn test_null_streamer_reset() {
let mut n = X86TargetNullStreamer::new(TargetObjectFormat::ELF, true);
n.instruction_count = 42;
n.directive_count = 10;
n.byte_count = 100;
n.label_count = 5;
n.fixup_count = 3;
n.reset();
assert_eq!(n.instruction_count, 0);
assert_eq!(n.directive_count, 0);
assert_eq!(n.byte_count, 0);
}
#[test]
fn test_null_streamer_default() {
let n = X86TargetNullStreamer::default();
assert_eq!(n.streamer.format, TargetObjectFormat::ELF);
}
#[test]
fn test_record_streamer_creation() {
let r = X86TargetRecordStreamer::new(TargetObjectFormat::ELF, true);
assert!(r.actions.is_empty());
assert!(!r.paused);
}
#[test]
fn test_record_streamer_records_directive() {
let mut r = X86TargetRecordStreamer::new(TargetObjectFormat::ELF, true);
r.emit_directive_action(DirectiveAction::EmitText);
assert_eq!(r.actions.len(), 1);
assert_eq!(r.actions[0], DirectiveAction::EmitText);
}
#[test]
fn test_record_streamer_pause_resume() {
let mut r = X86TargetRecordStreamer::new(TargetObjectFormat::ELF, true);
r.emit_directive_action(DirectiveAction::EmitText);
assert_eq!(r.actions.len(), 1);
r.pause();
r.emit_directive_action(DirectiveAction::EmitData);
assert_eq!(r.actions.len(), 1);
r.resume();
r.emit_directive_action(DirectiveAction::EmitBss);
assert_eq!(r.actions.len(), 2);
}
#[test]
fn test_record_streamer_clear() {
let mut r = X86TargetRecordStreamer::new(TargetObjectFormat::ELF, true);
r.emit_directive_action(DirectiveAction::EmitText);
r.emit_directive_action(DirectiveAction::EmitGlobl("main".to_string()));
r.clear();
assert!(r.actions.is_empty());
}
#[test]
fn test_record_streamer_replay_to_asm() {
let mut r = X86TargetRecordStreamer::new(TargetObjectFormat::ELF, true);
r.emit_directive_action(DirectiveAction::EmitText);
r.emit_directive_action(DirectiveAction::EmitGlobl("main".to_string()));
r.emit_directive_action(DirectiveAction::EmitCfiStartProc);
let mut asm = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
r.replay_to_asm(&mut asm);
let output = asm.as_str();
assert!(output.contains(".text"));
assert!(output.contains(".globl main"));
assert!(output.contains(".cfi_startproc"));
}
#[test]
fn test_record_streamer_replay_to_object() {
let mut r = X86TargetRecordStreamer::new(TargetObjectFormat::ELF, true);
r.emit_directive_action(DirectiveAction::EmitByte(vec![0x90, 0x90]));
r.emit_directive_action(DirectiveAction::EmitLabel("L0".to_string()));
let mut obj = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
r.replay_to_object(&mut obj);
obj.finalize();
let data = obj.get_section_data(".text").unwrap();
assert_eq!(data, &[0x90, 0x90]);
}
#[test]
fn test_record_streamer_default() {
let r = X86TargetRecordStreamer::default();
assert!(r.actions.is_empty());
}
#[test]
fn test_relaxable_instruction_creation() {
let ri = RelaxableInstruction {
bytes: vec![0xEB, 0x00],
mnemonic: "jmp".to_string(),
min_size: 2,
current_size: 2,
max_size: 5,
target_label: "L1".to_string(),
fixup_kind: FixupKind::Rel8,
can_relax_to_near: true,
};
assert_eq!(ri.bytes.len(), 2);
assert_eq!(ri.mnemonic, "jmp");
assert!(ri.can_relax_to_near);
}
#[test]
fn test_relaxable_instruction_size_range() {
let ri = RelaxableInstruction {
bytes: vec![0xEB, 0xFE],
mnemonic: "jmp".to_string(),
min_size: 2,
current_size: 2,
max_size: 5,
target_label: "L2".to_string(),
fixup_kind: FixupKind::Rel8,
can_relax_to_near: true,
};
assert!(ri.min_size <= ri.current_size);
assert!(ri.current_size <= ri.max_size);
}
#[test]
fn test_relaxable_instruction_jcc() {
let ri = RelaxableInstruction {
bytes: vec![0x74, 0x00],
mnemonic: "je".to_string(),
min_size: 2,
current_size: 2,
max_size: 6,
target_label: "L3".to_string(),
fixup_kind: FixupKind::Rel8,
can_relax_to_near: true,
};
assert_eq!(ri.mnemonic, "je");
assert_eq!(ri.min_size, 2);
assert_eq!(ri.max_size, 6);
}
#[test]
fn test_relaxation_manager_creation() {
let rm = RelaxationManager {
relaxable: Vec::new(),
label_offsets: HashMap::new(),
total_size: 0,
iteration: 0,
converged: false,
};
assert_eq!(rm.total_size, 0);
assert_eq!(rm.iteration, 0);
assert!(!rm.converged);
}
#[test]
fn test_relaxation_manager_add_instruction() {
let mut rm = RelaxationManager {
relaxable: Vec::new(),
label_offsets: HashMap::new(),
total_size: 0,
iteration: 0,
converged: false,
};
rm.relaxable.push(RelaxableInstruction {
bytes: vec![0xEB, 0x00],
mnemonic: "jmp".to_string(),
min_size: 2,
current_size: 2,
max_size: 5,
target_label: "L1".to_string(),
fixup_kind: FixupKind::Rel8,
can_relax_to_near: true,
});
rm.label_offsets.insert("L1".to_string(), 16);
assert_eq!(rm.relaxable.len(), 1);
assert_eq!(rm.label_offsets.get("L1"), Some(&16));
}
#[test]
fn test_relaxation_manager_relax_jmp_short_to_near() {
let mut rm = RelaxationManager {
relaxable: Vec::new(),
label_offsets: HashMap::new(),
total_size: 0,
iteration: 0,
converged: false,
};
rm.relaxable.push(RelaxableInstruction {
bytes: vec![0xEB, 0x00],
mnemonic: "jmp".to_string(),
min_size: 2,
current_size: 2,
max_size: 5,
target_label: "far_label".to_string(),
fixup_kind: FixupKind::Rel8,
can_relax_to_near: true,
});
rm.label_offsets.insert("far_label".to_string(), 300);
let changed = rm.relax_pass();
assert!(changed);
assert_eq!(rm.relaxable[0].current_size, 5);
}
#[test]
fn test_instruction_emitter_creation() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
let ie = InstructionEmitter::new(&mut s);
assert_eq!(ie.streamer.current_offset, 0);
}
#[test]
fn test_instruction_emitter_emit_nop() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_nop();
}
assert_eq!(s.current_offset, 1);
assert_eq!(s.binary_buffer[0], 0x90);
}
#[test]
fn test_instruction_emitter_emit_push_reg() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_push_reg(5);
}
assert_eq!(s.current_offset, 1);
assert_eq!(s.binary_buffer[0], 0x55);
}
#[test]
fn test_instruction_emitter_emit_ret() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_ret();
}
assert_eq!(s.current_offset, 1);
assert_eq!(s.binary_buffer[0], 0xC3);
}
#[test]
fn test_instruction_emitter_emit_mov_imm32() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_mov_imm32(0, 42);
}
assert_eq!(s.current_offset, 5);
assert_eq!(s.binary_buffer[0], 0xB8);
}
#[test]
fn test_instruction_emitter_emit_lea_rip_relative() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.add_fixup(3, FixupKind::RipRel32, "msg", 0);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_lea_rip_relative(7);
}
assert_eq!(s.current_offset, 7);
assert_eq!(s.binary_buffer[0], 0x48);
assert_eq!(s.binary_buffer[1], 0x8D);
assert_eq!(s.binary_buffer[2], 0x3D);
}
#[test]
fn test_instruction_emitter_emit_call_rel32_placeholder() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.add_fixup(1, FixupKind::Plt32, "puts", -4);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_call_rel32_placeholder();
}
assert_eq!(s.current_offset, 5);
assert_eq!(s.binary_buffer[0], 0xE8);
}
#[test]
fn test_instruction_emitter_emit_int3() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_int3();
}
assert_eq!(s.current_offset, 1);
assert_eq!(s.binary_buffer[0], 0xCC);
}
#[test]
fn test_instruction_emitter_emit_nop_multi() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_nop_n(5);
}
assert_eq!(s.current_offset, 5);
assert!(s.binary_buffer.iter().all(|&b| {
matches!(b, 0x90 | 0x66 | 0x0F | 0x1F | 0x00 | 0x40..=0x4F)
}));
}
#[test]
fn test_generate_nop_size_0() {
let nops = generate_optimal_nop_bytes(0, true);
assert!(nops.is_empty());
}
#[test]
fn test_generate_nop_size_1() {
let nops = generate_optimal_nop_bytes(1, true);
assert_eq!(nops.len(), 1);
assert_eq!(nops[0], 0x90);
}
#[test]
fn test_generate_nop_size_2() {
let nops = generate_optimal_nop_bytes(2, true);
assert_eq!(nops.len(), 2);
assert_eq!(nops[0], 0x66);
assert_eq!(nops[1], 0x90);
}
#[test]
fn test_generate_nop_size_3() {
let nops = generate_optimal_nop_bytes(3, true);
assert_eq!(nops.len(), 3);
assert_eq!(nops[0], 0x0F);
assert_eq!(nops[1], 0x1F);
assert_eq!(nops[2], 0x00);
}
#[test]
fn test_generate_nop_size_4() {
let nops = generate_optimal_nop_bytes(4, true);
assert_eq!(nops.len(), 4);
assert_eq!(nops[0], 0x0F);
assert_eq!(nops[1], 0x1F);
assert_eq!(nops[2], 0x40);
assert_eq!(nops[3], 0x00);
}
#[test]
fn test_generate_nop_size_5() {
let nops = generate_optimal_nop_bytes(5, true);
assert_eq!(nops.len(), 5);
assert_eq!(nops[0], 0x0F);
assert_eq!(nops[1], 0x1F);
assert_eq!(nops[2], 0x44);
assert_eq!(nops[3], 0x00);
assert_eq!(nops[4], 0x00);
}
#[test]
fn test_generate_nop_size_6() {
let nops = generate_optimal_nop_bytes(6, true);
assert_eq!(nops.len(), 6);
assert_eq!(nops[0], 0x66);
assert_eq!(nops[1], 0x0F);
assert_eq!(nops[2], 0x1F);
assert_eq!(nops[3], 0x44);
assert_eq!(nops[4], 0x00);
assert_eq!(nops[5], 0x00);
}
#[test]
fn test_generate_nop_size_7() {
let nops = generate_optimal_nop_bytes(7, true);
assert_eq!(nops.len(), 7);
}
#[test]
fn test_generate_nop_size_8() {
let nops = generate_optimal_nop_bytes(8, true);
assert_eq!(nops.len(), 8);
}
#[test]
fn test_generate_nop_size_9() {
let nops = generate_optimal_nop_bytes(9, true);
assert_eq!(nops.len(), 9);
}
#[test]
fn test_generate_nop_32bit_mode() {
let nops = generate_optimal_nop_bytes(4, false);
assert_eq!(nops.len(), 4);
assert_eq!(nops[0], 0x0F);
assert_eq!(nops[1], 0x1F);
assert_eq!(nops[2], 0x00);
assert_eq!(nops[3], 0x00);
}
#[test]
fn test_generate_nop_large_padding() {
let nops = generate_optimal_nop_bytes(15, true);
assert_eq!(nops.len(), 15);
}
#[test]
fn test_generate_nop_very_large() {
let nops = generate_optimal_nop_bytes(100, true);
assert_eq!(nops.len(), 100);
}
#[test]
fn test_map_relocation_to_elf_x86_64() {
assert_eq!(map_relocation_to_elf(FixupKind::Abs64, true), "R_X86_64_64");
assert_eq!(map_relocation_to_elf(FixupKind::Abs32, true), "R_X86_64_32");
assert_eq!(
map_relocation_to_elf(FixupKind::Rel32, true),
"R_X86_64_PC32"
);
assert_eq!(
map_relocation_to_elf(FixupKind::GotPcRel32, true),
"R_X86_64_GOTPCREL"
);
assert_eq!(
map_relocation_to_elf(FixupKind::Plt32, true),
"R_X86_64_PLT32"
);
}
#[test]
fn test_map_relocation_to_elf_i386() {
assert_eq!(map_relocation_to_elf(FixupKind::Abs32, false), "R_386_32");
assert_eq!(map_relocation_to_elf(FixupKind::Rel32, false), "R_386_PC32");
assert_eq!(
map_relocation_to_elf(FixupKind::GotPcRel32, false),
"R_386_GOTPC"
);
}
#[test]
fn test_map_relocation_to_coff() {
assert_eq!(
map_relocation_to_coff(FixupKind::Abs64),
"IMAGE_REL_AMD64_ADDR64"
);
assert_eq!(
map_relocation_to_coff(FixupKind::Rel32),
"IMAGE_REL_AMD64_REL32"
);
assert_eq!(
map_relocation_to_coff(FixupKind::RipRel32),
"IMAGE_REL_AMD64_REL32"
);
}
#[test]
fn test_map_relocation_to_macho() {
assert_eq!(
map_relocation_to_macho(FixupKind::Abs64),
"X86_64_RELOC_UNSIGNED"
);
assert_eq!(
map_relocation_to_macho(FixupKind::Rel32),
"X86_64_RELOC_BRANCH"
);
assert_eq!(
map_relocation_to_macho(FixupKind::GotPcRel32),
"X86_64_RELOC_GOT_LOAD"
);
}
#[test]
fn test_get_elf_relocation_number() {
assert_eq!(get_elf_relocation_number(FixupKind::Abs64, true), 1);
assert_eq!(get_elf_relocation_number(FixupKind::Abs32, false), 1);
assert_eq!(get_elf_relocation_number(FixupKind::Rel32, true), 2);
}
#[test]
fn test_label_resolver_creation() {
let lr = LabelResolver::new();
assert!(lr.label_offsets.is_empty());
assert!(lr.pending_fixups.is_empty());
}
#[test]
fn test_label_resolver_define_label() {
let mut lr = LabelResolver::new();
lr.define_label("L1", 100, ".text");
assert_eq!(lr.label_offsets.get("L1"), Some(&100));
}
#[test]
fn test_label_resolver_resolve_forward_ref() {
let mut lr = LabelResolver::new();
lr.add_fixup(FixupKind::Rel32, "forward_label", 50, ".text");
lr.define_label("forward_label", 200, ".text");
assert_eq!(lr.pending_fixups.len(), 1);
assert_eq!(lr.label_offsets.get("forward_label"), Some(&200));
}
#[test]
fn test_label_resolver_calculate_displacement() {
let mut lr = LabelResolver::new();
lr.define_label("L1", 100, ".text");
lr.define_label("L2", 200, ".text");
let disp = lr.calculate_displacement("L1", "L2");
assert_eq!(disp, Some(100));
}
#[test]
fn test_all_elf_section_names() {
let sections = get_all_section_names_for_format(TargetObjectFormat::ELF);
assert!(sections.contains(&".text".to_string()));
assert!(sections.contains(&".data".to_string()));
assert!(sections.contains(&".rodata".to_string()));
assert!(sections.contains(&".bss".to_string()));
assert!(sections.contains(&".debug_info".to_string()));
assert!(sections.contains(&".eh_frame".to_string()));
}
#[test]
fn test_all_coff_section_names() {
let sections = get_all_section_names_for_format(TargetObjectFormat::COFF);
assert!(sections.contains(&".text".to_string()));
assert!(sections.contains(&".data".to_string()));
assert!(sections.contains(&".rdata".to_string()));
assert!(sections.contains(&".pdata".to_string()));
}
#[test]
fn test_all_macho_section_names() {
let sections = get_all_section_names_for_format(TargetObjectFormat::MachO);
assert!(sections.contains(&"__text".to_string()));
assert!(sections.contains(&"__data".to_string()));
assert!(sections.contains(&"__const".to_string()));
}
#[test]
fn test_stress_many_labels() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
for i in 0..1000 {
s.emit_label(&format!(".L{}", i));
s.emit_byte(&[0x90]);
}
assert_eq!(s.labels.len(), 1000);
assert_eq!(s.current_offset, 1000);
}
#[test]
fn test_stress_many_symbols() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
for i in 0..500 {
s.emit_globl(&format!("sym_{}", i));
s.emit_hidden(&format!("sym_{}", i));
}
assert_eq!(s.symbols.len(), 500);
for i in 0..500 {
let sym = s.symbols.get(&format!("sym_{}", i)).unwrap();
assert!(sym.is_global);
assert!(sym.is_hidden);
}
}
#[test]
fn test_stress_many_equates() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
for i in 0..500 {
s.emit_equ(&format!("CONST_{}", i), &format!("{}", i * 10));
}
assert_eq!(s.equates.len(), 500);
assert_eq!(s.symbols.get("CONST_42").unwrap().value, 420);
}
#[test]
fn test_stress_deep_section_stack() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
let sections = [
".data",
".rodata",
".bss",
".eh_frame",
".debug_info",
".init_array",
".fini_array",
".tdata",
".tbss",
".note",
];
for sec in §ions {
s.emit_pushsection(sec, None, None);
}
assert_eq!(s.section_stack.len(), sections.len());
for _ in 0..sections.len() {
s.emit_popsection();
}
assert_eq!(s.section_stack.len(), 0);
assert_eq!(s.current_section, ".text");
}
#[test]
fn test_stress_large_data_emission() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
let data = vec![0xAA; 10000];
s.emit_byte(&data);
assert_eq!(s.current_offset, 10000);
assert_eq!(s.binary_buffer.len(), 10000);
assert!(s.binary_buffer.iter().all(|&b| b == 0xAA));
}
#[test]
fn test_stress_cfi_save_restore_depth() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_startproc();
for i in 0..50 {
s.emit_cfi_def_cfa(7, i * 8);
s.emit_cfi_remember_state();
}
assert_eq!(s.cfi_saved_states.len(), 50);
for _ in 0..50 {
s.emit_cfi_restore_state();
}
assert_eq!(s.cfi_saved_states.len(), 0);
assert_eq!(s.cfi_state.cfa_offset, 0);
}
#[test]
fn test_stress_conditional_nesting() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
for _ in 0..20 {
s.emit_if("1");
}
assert_eq!(s.cond_stack.len(), 20);
for _ in 0..20 {
s.emit_endif();
}
assert_eq!(s.cond_stack.len(), 0);
}
#[test]
fn test_roundtrip_data_byte_asm_vs_object() {
let bytes = vec![0x01, 0x02, 0x03, 0x04];
let mut asm = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
asm.emit_asm_byte(&bytes);
let mut obj = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
obj.emit_obj_byte(&bytes);
obj.finalize();
let obj_data = obj.get_section_data(".text").unwrap();
assert_eq!(obj_data, bytes.as_slice());
}
#[test]
fn test_roundtrip_section_switch() {
let mut asm = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
asm.emit_asm_text();
asm.emit_asm_data();
asm.emit_asm_bss();
asm.emit_asm_rodata();
let output = asm.as_str();
assert!(output.contains(".text"));
assert!(output.contains(".data"));
assert!(output.contains(".bss"));
assert!(output.contains(".rodata"));
}
#[test]
fn test_roundtrip_symbol_with_all_attributes() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_globl("all_attrs");
s.emit_weak("all_attrs");
s.emit_hidden("all_attrs");
s.emit_type("all_attrs", "@function");
s.emit_size("all_attrs", 128);
let sym = s.symbols.get("all_attrs").unwrap();
assert!(sym.is_global);
assert!(sym.is_weak);
assert!(sym.is_hidden);
assert!(sym.is_function);
assert_eq!(sym.size, 128);
}
#[test]
fn test_roundtrip_gotpcrel_fixup() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32("external_var", 0);
let fixup = &g.streamer.fixups[0];
assert_eq!(fixup.kind, FixupKind::GotPcRel32);
assert_eq!(fixup.symbol, "external_var");
assert_eq!(fixup.addend, 0);
assert_eq!(fixup.offset, 0);
}
#[test]
fn test_edge_empty_section_name() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_section("", None, None);
assert_eq!(s.current_section, "");
assert_eq!(s.current_section_kind, SectionKind::Custom);
}
#[test]
fn test_edge_very_long_section_name() {
let long_name = "a".repeat(256);
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_section(&long_name, None, None);
assert_eq!(s.current_section.len(), 256);
}
#[test]
fn test_edge_very_long_symbol_name() {
let long_name = "s".repeat(1024);
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_globl(&long_name);
assert!(s.symbols.contains_key(&long_name));
}
#[test]
fn test_edge_max_unsigned_values() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_quad(&[i64::MAX]);
assert_eq!(s.current_offset, 8);
s.emit_quad(&[i64::MIN]);
assert_eq!(s.current_offset, 16);
}
#[test]
fn test_edge_negative_immediates() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_long(&[-1]);
assert_eq!(s.current_offset, 4);
assert_eq!(s.binary_buffer[0], 0xFF);
assert_eq!(s.binary_buffer[1], 0xFF);
assert_eq!(s.binary_buffer[2], 0xFF);
assert_eq!(s.binary_buffer[3], 0xFF);
}
#[test]
fn test_edge_octa_zero() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_octa(&[0, u128::MAX]);
assert_eq!(s.current_offset, 32);
assert!(s.binary_buffer[0..16].iter().all(|&b| b == 0));
assert!(s.binary_buffer[16..32].iter().all(|&b| b == 0xFF));
}
#[test]
fn test_edge_align_max() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 1;
s.emit_align(4096, None, None);
assert_eq!(s.current_offset, 4096);
}
#[test]
fn test_edge_org_same_offset() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 100;
s.emit_org(100, None);
assert_eq!(s.current_offset, 100);
}
#[test]
fn test_edge_cfi_without_startproc() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_def_cfa(7, 8);
s.emit_cfi_offset(16, -8);
assert_eq!(s.cfi_state.cfa_register, 7);
}
#[test]
fn test_edge_restore_state_empty_stack() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_cfi_restore_state();
}
#[test]
fn test_edge_popsection_empty_stack() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_popsection();
assert_eq!(s.current_section, ".text");
}
#[test]
fn test_edge_endif_without_if() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_endif();
}
#[test]
fn test_edge_multiple_else() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_if("1");
s.emit_else();
s.emit_else();
s.emit_endif();
assert!(s.cond_stack.is_empty());
}
#[test]
fn test_edge_tls_model_unknown() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_tls_model("bogus-model");
assert_eq!(s.tls_model, TlsModel::GeneralDynamic);
}
#[test]
fn test_edge_equ_shadowing() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_equ("X", "10");
s.emit_equ("X", "20"); assert_eq!(s.symbols.get("X").unwrap().value, 20);
}
#[test]
fn test_edge_ascii_empty() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ascii("");
assert_eq!(s.current_offset, 0);
}
#[test]
fn test_edge_asciz_empty() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_asciz("");
assert_eq!(s.current_offset, 1); assert_eq!(s.binary_buffer[0], 0);
}
#[test]
fn test_edge_space_zero() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.current_offset = 10;
s.emit_space(0, Some(0xFF));
assert_eq!(s.current_offset, 10);
}
#[test]
fn test_disassembly_hint_prologue() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
let hint = DisassemblyHint {
is_prologue: true,
is_epilogue: false,
is_branch_target: false,
is_call_target: false,
alignment: 1,
comment: Some("function prologue".to_string()),
};
assert!(hint.is_prologue);
assert!(!hint.is_epilogue);
assert_eq!(hint.comment, Some("function prologue".to_string()));
}
#[test]
fn test_disassembly_hint_epilogue() {
let hint = DisassemblyHint {
is_prologue: false,
is_epilogue: true,
is_branch_target: false,
is_call_target: false,
alignment: 1,
comment: None,
};
assert!(hint.is_epilogue);
}
#[test]
fn test_disassembly_hint_default() {
let hint = DisassemblyHint::default();
assert!(!hint.is_prologue);
assert!(!hint.is_epilogue);
assert!(!hint.is_branch_target);
assert!(!hint.is_call_target);
assert_eq!(hint.alignment, 1);
assert_eq!(hint.comment, None);
}
#[test]
fn test_bundle_alignment_16() {
let ba = BundleAlignment { size: 16 };
assert_eq!(ba.size, 16);
}
#[test]
fn test_bundle_alignment_32() {
let ba = BundleAlignment { size: 32 };
assert_eq!(ba.size, 32);
}
#[test]
fn test_bundle_alignment_64() {
let ba = BundleAlignment { size: 64 };
assert_eq!(ba.size, 64);
}
#[test]
fn test_data_byte_all_zeros() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_byte(&[0; 256]);
assert_eq!(s.current_offset, 256);
assert!(s.binary_buffer.iter().all(|&b| b == 0));
}
#[test]
fn test_data_short_min_max() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_short(&[i16::MIN, i16::MAX, 0]);
assert_eq!(s.current_offset, 6);
assert_eq!(s.binary_buffer[0], 0x00);
assert_eq!(s.binary_buffer[1], 0x80);
assert_eq!(s.binary_buffer[2], 0xFF);
assert_eq!(s.binary_buffer[3], 0x7F);
}
#[test]
fn test_data_long_min_max() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_long(&[i32::MIN, i32::MAX]);
assert_eq!(s.current_offset, 8);
}
#[test]
fn test_data_quad_min_max() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_quad(&[i64::MIN, i64::MAX]);
assert_eq!(s.current_offset, 16);
}
#[test]
fn test_data_octa_mixed() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_octa(&[0, 1, u128::MAX / 2]);
assert_eq!(s.current_offset, 48);
}
#[test]
fn test_data_ascii_unicode() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ascii("hello world! 123");
assert_eq!(s.binary_buffer, b"hello world! 123");
}
#[test]
fn test_data_ascii_special_chars() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
let special = "\x01\x02\n\t\r";
s.emit_ascii(special);
assert_eq!(s.current_offset, special.len() as u64);
}
#[test]
fn test_data_space_large() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_space(50000, Some(0xCC));
assert_eq!(s.current_offset, 50000);
assert!(s.binary_buffer.iter().all(|&b| b == 0xCC));
}
#[test]
fn test_data_fill_multi_byte() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_fill(4, &[0xDE, 0xAD, 0xBE, 0xEF], 4);
assert_eq!(s.current_offset, 16);
assert_eq!(&s.binary_buffer[0..4], &[0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(&s.binary_buffer[12..16], &[0xDE, 0xAD, 0xBE, 0xEF]);
}
#[test]
fn test_symbol_all_combinations() {
let combinations = vec![
("global_func", true, false, false, false, true),
("local_obj", false, true, false, false, false),
("weak_func", false, false, true, false, true),
("hidden_var", false, false, false, true, false),
("global_weak", true, false, true, false, false),
("global_hidden_func", true, false, false, true, true),
];
for (name, glob, loc, weak, hidden, func) in &combinations {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
if *glob {
s.emit_globl(name);
}
if *loc {
s.emit_local(name);
}
if *weak {
s.emit_weak(name);
}
if *hidden {
s.emit_hidden(name);
}
if *func {
s.emit_type(name, "@function");
}
let sym = s.symbols.get(*name).unwrap();
assert_eq!(sym.is_global, *glob);
assert_eq!(sym.is_local, *loc);
assert_eq!(sym.is_weak, *weak);
assert_eq!(sym.is_hidden, *hidden);
assert_eq!(sym.is_function, *func);
}
}
#[test]
fn test_symbol_visibility_all() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_hidden("h");
s.emit_protected("p");
s.emit_internal("i");
assert!(s.symbols.get("h").unwrap().is_hidden);
assert!(s.symbols.get("p").unwrap().is_protected);
assert!(s.symbols.get("i").unwrap().is_internal);
}
#[test]
fn test_section_kind_all_builtin_names_elf() {
let mappings = vec![
(".text", SectionKind::Text),
(".data", SectionKind::Data),
(".bss", SectionKind::Bss),
(".rodata", SectionKind::ReadOnly),
(".rodata.str1.1", SectionKind::ReadOnlyStr),
(".eh_frame", SectionKind::EHFrame),
(".debug_info", SectionKind::DebugInfo),
(".debug_line", SectionKind::DebugLine),
(".init_array", SectionKind::InitArray),
(".fini_array", SectionKind::FiniArray),
(".tdata", SectionKind::ThreadData),
(".tbss", SectionKind::ThreadBss),
(".note", SectionKind::Note),
(".stab", SectionKind::Stabs),
(".stabstr", SectionKind::StabsStr),
];
for (name, _kind) in &mappings {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_section(name, None, None);
assert_eq!(
s.current_section_kind, *_kind,
"Mismatch for section: {}",
name
);
}
}
#[test]
fn test_section_kind_invalid_name() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_section(".totally_bogus_section_name", None, None);
assert_eq!(s.current_section_kind, SectionKind::Custom);
}
#[test]
fn test_fixup_all_kinds_have_size() {
let kinds = [
FixupKind::Abs8,
FixupKind::Abs16,
FixupKind::Abs32,
FixupKind::Abs64,
FixupKind::Rel8,
FixupKind::Rel16,
FixupKind::Rel32,
FixupKind::RipRel32,
FixupKind::GotPcRel32,
FixupKind::Plt32,
FixupKind::GotOff,
FixupKind::TlsGd,
FixupKind::TlsLd,
FixupKind::GotTpOff,
FixupKind::TpOff,
FixupKind::DtpOff,
FixupKind::GotPc32TlsDesc,
];
for kind in &kinds {
let size = kind.size_bytes();
assert!(size >= 1 && size <= 8, "kind {:?} has size {}", kind, size);
}
}
#[test]
fn test_fixup_record_properties() {
let fr = FixupRecord {
offset: 42,
kind: FixupKind::GotPcRel32,
symbol: "printf".to_string(),
addend: -4,
section: ".text".to_string(),
};
assert_eq!(fr.offset, 42);
assert_eq!(fr.kind, FixupKind::GotPcRel32);
assert_eq!(fr.symbol, "printf");
assert_eq!(fr.addend, -4);
assert_eq!(fr.section, ".text");
}
#[test]
fn test_format_elf_default_text_section() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(s.current_section, ".text");
}
#[test]
fn test_format_coff_default_text_section() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
assert_eq!(s.current_section, ".text");
}
#[test]
fn test_format_macho_default_text_section() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::MachO, true);
assert_eq!(s.current_section, "__text");
}
#[test]
fn test_format_elf_data_section_name() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(s.data_section_name(), ".data");
}
#[test]
fn test_format_coff_data_section_name() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
assert_eq!(s.data_section_name(), ".data");
}
#[test]
fn test_format_macho_data_section_name() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::MachO, true);
assert_eq!(s.data_section_name(), "__data");
}
#[test]
fn test_format_elf_rodata_section_name() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(s.rodata_section_name(), ".rodata");
}
#[test]
fn test_format_coff_rodata_section_name() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::COFF, true);
assert_eq!(s.rodata_section_name(), ".rdata");
}
#[test]
fn test_format_macho_rodata_section_name() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::MachO, true);
assert_eq!(s.rodata_section_name(), "__const");
}
#[test]
fn test_format_all_binary_sections() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::Binary, true);
assert_eq!(s.text_section_name(), ".text");
assert_eq!(s.data_section_name(), ".data");
assert_eq!(s.rodata_section_name(), ".rodata");
}
#[test]
fn test_format_all_xcoff_sections() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::XCOFF, true);
assert_eq!(s.text_section_name(), ".text");
assert_eq!(s.data_section_name(), ".data");
}
#[test]
fn test_format_all_goff_sections() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::GOFF, true);
assert_eq!(s.text_section_name(), ".text");
}
#[test]
fn test_asm_coff_directive_style() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::COFF, true);
a.emit_asm_text();
a.emit_asm_rodata();
let output = a.as_str();
assert!(output.contains(".text"));
assert!(output.contains(".rdata"));
}
#[test]
fn test_asm_macho_directive_style() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::MachO, true);
a.emit_asm_text();
a.emit_asm_rodata();
let output = a.as_str();
assert!(output.contains("__TEXT,__const"));
}
#[test]
fn test_asm_verbose_mode() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.verbose = true;
a.emit_asm_text();
assert!(a.output.contains(".text"));
}
#[test]
fn test_asm_show_encoding_mode() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.show_encoding = true;
a.emit_instruction_with_encoding("nop", "", &[0x90]);
let output = a.as_str();
assert!(output.contains("encoding: [90]"));
}
#[test]
fn test_asm_show_encoding_multi_byte() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.show_encoding = true;
a.emit_instruction_with_encoding("mov", "%rax, %rbx", &[0x48, 0x89, 0xC3]);
let output = a.as_str();
assert!(output.contains("encoding: [48 89 c3]"));
}
#[test]
fn test_asm_directive_count() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_text();
a.emit_asm_globl("main");
a.emit_asm_type("main", "@function");
a.emit_asm_align(16, None, None);
assert!(a.directive_count >= 3);
}
#[test]
fn test_object_section_fixups_independent() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.add_fixup(FixupKind::Rel32, "func1", -4);
o.switch_section(".data", None, None);
o.add_fixup(FixupKind::Abs64, "var1", 0);
o.finalize();
let text_fixups = o.get_section_fixups(".text").unwrap();
let data_fixups = o.get_section_fixups(".data").unwrap();
assert_eq!(text_fixups.len(), 1);
assert_eq!(text_fixups[0].symbol, "func1");
assert_eq!(data_fixups.len(), 1);
assert_eq!(data_fixups[0].symbol, "var1");
}
#[test]
fn test_object_divergent_sections() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_instruction_bytes(&[0x55, 0x48, 0x89, 0xE5]);
o.switch_section(".rodata", None, None);
o.emit_obj_asciz("hello");
o.switch_section(".data", None, None);
o.emit_obj_long(&[42]);
o.switch_section(".text", None, None);
o.emit_instruction_bytes(&[0x5D, 0xC3]);
o.finalize();
assert_eq!(o.get_section_data(".text").unwrap().len(), 6);
assert_eq!(o.get_section_data(".rodata").unwrap().len(), 6);
assert_eq!(o.get_section_data(".data").unwrap().len(), 4);
}
#[test]
fn test_object_gen_dwarf_mode() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.gen_dwarf = true;
o.gen_cfi = true;
o.define_symbol(
"func",
SymbolBinding::Global,
SymbolType::Func,
SymbolVisibility::Default,
);
o.streamer.emit_cfi_startproc();
o.streamer.emit_cfi_def_cfa(7, 8);
o.streamer.emit_cfi_offset(16, -8);
o.emit_instruction_bytes(&[0x55, 0x48, 0x89, 0xE5]);
o.streamer.emit_cfi_endproc();
o.finalize();
assert!(o.gen_dwarf);
assert!(o.gen_cfi);
}
#[test]
fn test_object_temp_label_uniqueness() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
let labels: Vec<String> = (0..100).map(|_| o.new_temp_label()).collect();
let mut unique: Vec<String> = labels.clone();
unique.sort();
unique.dedup();
assert_eq!(labels.len(), unique.len()); }
#[test]
fn test_gotpcrel_addend_correct() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32("var", -4);
assert_eq!(g.streamer.fixups[0].addend, -4);
g.emit_gotpcrel32("var2", 8);
assert_eq!(g.streamer.fixups[1].addend, 8);
}
#[test]
fn test_gotpcrel_negative_addend() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32("var", -128);
assert_eq!(g.streamer.fixups[0].addend, -128);
}
#[test]
fn test_gotpcrel_zero_addend() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32("var", 0);
assert_eq!(g.streamer.fixups[0].addend, 0);
}
#[test]
fn test_gotpcrel_no_plt_flag() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.no_plt = true;
g.emit_gotpcrel32("var", 0);
assert_eq!(g.streamer.fixups[0].kind, FixupKind::GotPcRel32);
}
#[test]
fn test_integration_full_elf_function() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.define_symbol(
"add",
SymbolBinding::Global,
SymbolType::Func,
SymbolVisibility::Default,
);
o.emit_instruction_bytes(&[0x55]);
o.emit_instruction_bytes(&[0x48, 0x89, 0xE5]);
o.emit_instruction_bytes(&[0x89, 0x7D, 0xFC]);
o.emit_instruction_bytes(&[0x89, 0x75, 0xF8]);
o.emit_instruction_bytes(&[0x8B, 0x45, 0xFC]);
o.emit_instruction_bytes(&[0x03, 0x45, 0xF8]);
o.emit_instruction_bytes(&[0x5D]);
o.emit_instruction_bytes(&[0xC3]);
o.finalize();
let text_data = o.get_section_data(".text").unwrap();
assert_eq!(text_data.len(), 18);
assert_eq!(text_data[0], 0x55);
assert_eq!(text_data[17], 0xC3);
}
#[test]
fn test_integration_plt_call() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_instruction_bytes(&[0x55]);
o.emit_instruction_bytes(&[0x48, 0x89, 0xE5]);
o.emit_instruction_bytes(&[0xE8]);
o.add_fixup(FixupKind::Plt32, "printf", -4);
o.streamer.emit_raw_bytes(&[0x00, 0x00, 0x00, 0x00]);
o.emit_instruction_bytes(&[0x5D, 0xC3]);
o.finalize();
let fixups = o.get_section_fixups(".text").unwrap();
assert_eq!(fixups.len(), 1);
assert_eq!(fixups[0].kind, FixupKind::Plt32);
assert_eq!(fixups[0].symbol, "printf");
}
#[test]
fn test_integration_gotpcrel_data_access() {
let mut o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
o.emit_instruction_bytes(&[0x48, 0x8B, 0x05]);
o.emit_gotpcrel32_value("global_var", 0);
o.finalize();
let text_fixups = o.get_section_fixups(".text").unwrap();
assert_eq!(text_fixups.len(), 1);
assert_eq!(text_fixups[0].kind, FixupKind::GotPcRel32);
assert_eq!(text_fixups[0].symbol, "global_var");
}
#[test]
fn test_integration_tls_access() {
let mut g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
g.emit_gotpcrel32_tls("tls_var");
g.emit_gotpcrel32_tlsdesc("tls_var2");
assert_eq!(g.streamer.fixups.len(), 2);
assert_eq!(g.streamer.fixups[0].kind, FixupKind::TlsGd);
assert_eq!(g.streamer.fixups[1].kind, FixupKind::GotPc32TlsDesc);
}
#[test]
fn test_integration_large_function_with_cfi() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
a.emit_asm_file(1, "large.c");
a.emit_asm_text();
a.emit_asm_globl("compute");
a.emit_asm_type("compute", "@function");
a.emit_global_label("compute");
a.emit_asm_cfi_startproc();
a.emit_asm_cfi_def_cfa(7, 8);
a.emit_asm_cfi_offset(6, -16);
for i in 0..10 {
a.emit_asm_loc(1, i + 1, 0);
a.emit_instruction("nop", "");
}
a.emit_asm_cfi_remember_state();
a.emit_asm_cfi_def_cfa(6, 16);
a.emit_instruction("mov", "$42, %eax");
a.emit_asm_cfi_restore_state();
a.emit_instruction("ret", "");
a.emit_asm_cfi_endproc();
a.emit_asm_size("compute", 100);
let output = a.as_str();
assert!(output.contains("compute:"));
assert!(output.contains(".cfi_remember_state"));
assert!(output.contains(".cfi_restore_state"));
assert!(output.contains("ret"));
}
#[test]
fn test_section_attributes_preserved_on_switch() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_section(".mytext", Some("ax"), Some("progbits"));
assert_eq!(s.current_section, ".mytext");
assert!(s.section_alignment >= 1);
s.emit_text();
assert_eq!(s.current_section, ".text");
s.emit_section(".mytext", Some("ax"), Some("progbits"));
assert_eq!(s.current_section, ".mytext");
}
#[test]
fn test_section_offset_reset_on_switch() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_byte(&[0x90, 0x90, 0x90]);
assert_eq!(s.current_offset, 3);
s.emit_text(); assert_eq!(s.current_offset, 0);
}
#[test]
fn test_msvc_procedure_frame() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::COFF, true);
a.emit_asm_seh_proc("WinMain");
a.emit_asm_seh_pushreg(4); a.emit_asm_seh_pushreg(5); a.emit_asm_seh_setframe(5, 0);
a.emit_asm_seh_endprologue();
a.emit_asm_seh_stackalloc(0x20);
a.emit_asm_seh_savereg(6, 0x10);
a.emit_asm_seh_handler("__GSHandlerCheck", Some("$SG3808"));
a.emit_asm_seh_endproc();
let output = a.as_str();
assert!(output.contains(".seh_proc WinMain"));
assert!(output.contains(".seh_pushreg 4"));
assert!(output.contains(".seh_pushreg 5"));
assert!(output.contains(".seh_setframe 5, 0"));
assert!(output.contains(".seh_endprologue"));
assert!(output.contains(".seh_stackalloc 32"));
assert!(output.contains(".seh_savereg 6, 16"));
assert!(output.contains(".seh_handler __GSHandlerCheck, $SG3808"));
assert!(output.contains(".seh_endproc"));
}
#[test]
fn test_codeview_debug_emission() {
let mut a = X86TargetAsmStreamer::new(TargetObjectFormat::COFF, true);
a.emit_asm_cv_file(1, "main.cpp");
a.emit_asm_cv_file(2, "helper.cpp");
a.emit_asm_cv_func_id(1, "main");
a.emit_asm_cv_func_id(2, "helper");
a.emit_asm_cv_loc(1, 1, 5, 1);
a.emit_asm_cv_loc(1, 1, 6, 1);
a.emit_asm_cv_inline_site_id(10, 1, 2, 42, 3);
let output = a.as_str();
assert!(output.contains(".cv_file 1, \"main.cpp\""));
assert!(output.contains(".cv_file 2, \"helper.cpp\""));
assert!(output.contains(".cv_func_id 1, main"));
assert!(output.contains(".cv_func_id 2, helper"));
assert!(output.contains(".cv_loc 1, 1, 5, 1"));
assert!(output.contains(".cv_inline_site_id 10, 1, 2, 42, 3"));
}
#[test]
fn test_preprocessor_full_pipeline() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_equ("FEATURE_X", "1");
s.emit_equ("FEATURE_Y", "0");
s.emit_ifdef("FEATURE_X");
s.emit_byte(&[0x90, 0x90]);
s.emit_else();
s.emit_byte(&[0xCC, 0xCC]);
s.emit_endif();
assert_eq!(s.binary_buffer, vec![0x90, 0x90]);
}
#[test]
fn test_preprocessor_nested_if_conditions() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_if("1"); s.emit_byte(&[1]);
s.emit_if("1"); s.emit_byte(&[2]);
s.emit_endif();
s.emit_if("0"); s.emit_byte(&[3]); s.emit_else();
s.emit_byte(&[4]);
s.emit_endif();
s.emit_endif();
assert_eq!(s.binary_buffer, vec![1, 2, 4]);
}
#[test]
fn test_preprocessor_comparison_operators() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_ifgt("5", "3");
assert!(!s.is_skipping()); s.emit_endif();
s.emit_iflt("2", "7");
assert!(!s.is_skipping()); s.emit_endif();
s.emit_ifge("5", "5");
assert!(!s.is_skipping()); s.emit_endif();
}
#[test]
fn test_macro_with_multiple_lines() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_macro_begin("PROLOGUE", &[]);
s.emit_macro_line("PROLOGUE", "push %rbp");
s.emit_macro_line("PROLOGUE", "mov %rsp, %rbp");
s.emit_macro_line("PROLOGUE", "sub $32, %rsp");
s.emit_endm();
let body = s.macros.get("PROLOGUE").unwrap();
assert_eq!(body.len(), 3);
assert!(body[0].contains("push"));
assert!(body[1].contains("mov"));
assert!(body[2].contains("sub"));
}
#[test]
fn test_multiple_macros_coexist() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_macro_begin("MACRO_A", &["reg"]);
s.emit_macro_line("MACRO_A", "push %reg");
s.emit_endm();
s.emit_macro_begin("MACRO_B", &[]);
s.emit_macro_line("MACRO_B", "nop");
s.emit_macro_line("MACRO_B", "ret");
s.emit_endm();
assert_eq!(s.macros.len(), 2);
assert_eq!(s.macros.get("MACRO_A").unwrap().len(), 1);
assert_eq!(s.macros.get("MACRO_B").unwrap().len(), 2);
}
#[test]
fn test_relaxation_jcc_within_range() {
let mut rm = RelaxationManager {
relaxable: Vec::new(),
label_offsets: HashMap::new(),
total_size: 0,
iteration: 0,
converged: false,
};
rm.relaxable.push(RelaxableInstruction {
bytes: vec![0x74, 0x00],
mnemonic: "je".to_string(),
min_size: 2,
current_size: 2,
max_size: 6,
target_label: "close_label".to_string(),
fixup_kind: FixupKind::Rel8,
can_relax_to_near: true,
});
rm.label_offsets.insert("close_label".to_string(), 10);
let changed = rm.relax_pass();
assert_eq!(rm.relaxable[0].current_size, 2);
}
#[test]
fn test_relaxation_jcc_out_of_range() {
let mut rm = RelaxationManager {
relaxable: Vec::new(),
label_offsets: HashMap::new(),
total_size: 0,
iteration: 0,
converged: false,
};
rm.relaxable.push(RelaxableInstruction {
bytes: vec![0x74, 0x00],
mnemonic: "je".to_string(),
min_size: 2,
current_size: 2,
max_size: 6,
target_label: "far_label".to_string(),
fixup_kind: FixupKind::Rel8,
can_relax_to_near: true,
});
rm.label_offsets.insert("far_label".to_string(), 500);
let changed = rm.relax_pass();
assert!(changed);
assert_eq!(rm.relaxable[0].current_size, 6);
}
#[test]
fn test_relaxation_convergence() {
let mut rm = RelaxationManager {
relaxable: Vec::new(),
label_offsets: HashMap::new(),
total_size: 0,
iteration: 0,
converged: false,
};
rm.relaxable.push(RelaxableInstruction {
bytes: vec![0xEB, 0x00],
mnemonic: "jmp".to_string(),
min_size: 2,
current_size: 2,
max_size: 5,
target_label: "far".to_string(),
fixup_kind: FixupKind::Rel8,
can_relax_to_near: true,
});
rm.label_offsets.insert("far".to_string(), 300);
let changed1 = rm.relax_pass();
assert!(changed1);
assert_eq!(rm.relaxable[0].current_size, 5);
let changed2 = rm.relax_pass();
assert!(!changed2);
assert!(rm.converged);
}
#[test]
fn test_instruction_emitter_xor_rr_basic() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_xor_rr(0, 0); }
assert_eq!(s.current_offset, 2);
assert_eq!(s.binary_buffer[0], 0x31);
assert_eq!(s.binary_buffer[1], 0xC0);
}
#[test]
fn test_instruction_emitter_xor_rr_rex() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_xor_rr(8, 9); }
assert_eq!(s.current_offset, 3);
assert_eq!(s.binary_buffer[0], 0x4D); assert_eq!(s.binary_buffer[1], 0x31);
assert_eq!(s.binary_buffer[2], 0xC9);
}
#[test]
fn test_instruction_emitter_pop_reg() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_pop_reg(5); }
assert_eq!(s.current_offset, 1);
assert_eq!(s.binary_buffer[0], 0x5D);
}
#[test]
fn test_instruction_emitter_leave() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
{
let mut ie = InstructionEmitter::new(&mut s);
ie.emit_leave();
}
assert_eq!(s.current_offset, 1);
assert_eq!(s.binary_buffer[0], 0xC9);
}
#[test]
fn test_nop_always_valid_x86() {
for size in 1..30 {
let nops = generate_optimal_nop_bytes(size, true);
assert_eq!(nops.len(), size as usize);
assert!(
nops[0] == 0x90 || nops[0] == 0x66 || nops[0] == 0x0F,
"Invalid NOP start byte for size {}",
size
);
}
}
#[test]
fn test_nop_32bit_small_sizes() {
for size in 1..=4 {
let nops = generate_optimal_nop_bytes(size, false);
assert_eq!(nops.len(), size as usize);
}
}
#[test]
fn test_label_resolver_backward_ref() {
let mut lr = LabelResolver::new();
lr.define_label("L1", 100, ".text");
lr.define_label("L2", 200, ".text");
assert_eq!(lr.calculate_displacement("L1", "L2"), Some(100));
assert_eq!(lr.calculate_displacement("L2", "L1"), Some(-100));
}
#[test]
fn test_label_resolver_undefined_label() {
let lr = LabelResolver::new();
assert_eq!(lr.calculate_displacement("L1", "L2"), None);
assert!(!lr.is_defined("L1"));
}
#[test]
fn test_label_resolver_default() {
let lr = LabelResolver::default();
assert!(lr.label_offsets.is_empty());
}
#[test]
fn test_disassembly_hint_branch_target() {
let hint = DisassemblyHint {
is_branch_target: true,
..DisassemblyHint::default()
};
assert!(hint.is_branch_target);
assert!(!hint.is_call_target);
}
#[test]
fn test_disassembly_hint_call_target() {
let hint = DisassemblyHint {
is_call_target: true,
..DisassemblyHint::default()
};
assert!(!hint.is_branch_target);
assert!(hint.is_call_target);
}
#[test]
fn test_disassembly_hint_with_comment() {
let hint = DisassemblyHint {
comment: Some("entry point".to_string()),
..DisassemblyHint::default()
};
assert_eq!(hint.comment, Some("entry point".to_string()));
}
#[test]
fn test_file_info_full_state() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_file(1, "/src/main.c");
s.emit_loc(1, 42, 5);
assert_eq!(s.file_info.file_id, 1);
assert_eq!(s.file_info.filename, "/src/main.c");
assert_eq!(s.file_info.line, 42);
assert_eq!(s.file_info.column, 5);
assert!(s.file_info.is_stmt);
}
#[test]
fn test_file_info_prologue_epilogue_flags() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.file_info.prologue_end = true;
s.file_info.epilogue_begin = false;
s.file_info.basic_block = true;
assert!(s.file_info.prologue_end);
assert!(!s.file_info.epilogue_begin);
assert!(s.file_info.basic_block);
}
#[test]
fn test_file_info_discriminator() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.file_info.discriminator = 3;
assert_eq!(s.file_info.discriminator, 3);
}
#[test]
fn test_symbol_record_binding_enum() {
assert_eq!(SymbolBinding::Local as i32, 0);
assert_eq!(SymbolBinding::Global as i32, 1);
assert_eq!(SymbolBinding::Weak as i32, 2);
}
#[test]
fn test_symbol_record_type_enum() {
let types = [
SymbolType::NoType,
SymbolType::Object,
SymbolType::Func,
SymbolType::Section,
SymbolType::File,
SymbolType::Common,
SymbolType::Tls,
SymbolType::GnuIfunc,
];
assert!(types.len() == 8);
}
#[test]
fn test_symbol_visibility_enum() {
let vis = [
SymbolVisibility::Default,
SymbolVisibility::Internal,
SymbolVisibility::Hidden,
SymbolVisibility::Protected,
];
assert!(vis.len() == 4);
}
#[test]
fn test_bound_align_zero_handling() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_align(0, None, None);
assert_eq!(s.current_offset, 0);
}
#[test]
fn test_bound_section_stack_overflow() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
for i in 0..200 {
s.emit_pushsection(&format!(".sec{}", i), None, None);
}
assert_eq!(s.section_stack.len(), 200);
for _ in 0..200 {
s.emit_popsection();
}
assert_eq!(s.section_stack.len(), 0);
}
#[test]
fn test_bound_empty_data_emission() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_byte(&[]);
s.emit_short(&[]);
s.emit_long(&[]);
s.emit_quad(&[]);
s.emit_octa(&[]);
assert_eq!(s.current_offset, 0);
}
#[test]
fn test_bound_extremely_large_fill() {
let mut s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
s.emit_space(1_000_000, Some(0));
assert_eq!(s.current_offset, 1_000_000);
assert_eq!(s.binary_buffer.len(), 1_000_000);
}
#[test]
fn test_from_triple_linux_gnu() {
let a = X86TargetAsmStreamer::from_triple("x86_64-unknown-linux-gnu");
assert_eq!(a.streamer.format, TargetObjectFormat::ELF);
assert!(a.streamer.is_64bit);
}
#[test]
fn test_from_triple_linux_musl() {
let a = X86TargetAsmStreamer::from_triple("x86_64-unknown-linux-musl");
assert_eq!(a.streamer.format, TargetObjectFormat::ELF);
assert!(a.streamer.is_64bit);
}
#[test]
fn test_from_triple_win_msvc() {
let a = X86TargetAsmStreamer::from_triple("x86_64-pc-windows-msvc");
assert_eq!(a.streamer.format, TargetObjectFormat::COFF);
}
#[test]
fn test_from_triple_win_mingw() {
let a = X86TargetAsmStreamer::from_triple("x86_64-w64-mingw32");
assert_eq!(a.streamer.format, TargetObjectFormat::COFF);
}
#[test]
fn test_from_triple_macos() {
let a = X86TargetAsmStreamer::from_triple("x86_64-apple-darwin20");
assert_eq!(a.streamer.format, TargetObjectFormat::MachO);
}
#[test]
fn test_from_triple_macos_arm() {
let a = X86TargetAsmStreamer::from_triple("x86_64-apple-macos11");
assert_eq!(a.streamer.format, TargetObjectFormat::MachO);
}
#[test]
fn test_from_triple_i386_linux() {
let a = X86TargetAsmStreamer::from_triple("i386-unknown-linux-gnu");
assert_eq!(a.streamer.format, TargetObjectFormat::ELF);
assert!(!a.streamer.is_64bit);
}
#[test]
fn test_from_triple_i686_linux() {
let a = X86TargetAsmStreamer::from_triple("i686-pc-linux-gnu");
assert_eq!(a.streamer.format, TargetObjectFormat::ELF);
assert!(!a.streamer.is_64bit);
}
#[test]
fn test_from_triple_amd64_freebsd() {
let a = X86TargetAsmStreamer::from_triple("amd64-unknown-freebsd");
assert!(a.streamer.is_64bit);
}
#[test]
fn test_from_triple_unknown_default() {
let a = X86TargetAsmStreamer::from_triple("x86_64-unknown-unknown");
assert_eq!(a.streamer.format, TargetObjectFormat::ELF);
}
#[test]
fn test_cross_format_byte_emission_identical() {
let data = vec![0x10, 0x20, 0x30];
let mut elf = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
let mut coff = X86TargetObjectStreamer::new(TargetObjectFormat::COFF, true);
let mut macho = X86TargetObjectStreamer::new(TargetObjectFormat::MachO, true);
elf.emit_obj_byte(&data);
coff.emit_obj_byte(&data);
macho.emit_obj_byte(&data);
elf.finalize();
coff.finalize();
macho.finalize();
assert_eq!(elf.get_section_data(".text").unwrap(), data.as_slice());
assert_eq!(coff.get_section_data(".text").unwrap(), data.as_slice());
assert_eq!(macho.get_section_data("__text").unwrap(), data.as_slice());
}
#[test]
fn test_cross_format_fixup_storage() {
let mut elf = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
let mut coff = X86TargetObjectStreamer::new(TargetObjectFormat::COFF, true);
elf.add_fixup(FixupKind::Rel32, "func", -4);
coff.add_fixup(FixupKind::Rel32, "func", -4);
elf.finalize();
coff.finalize();
assert_eq!(elf.get_section_fixups(".text").unwrap().len(), 1);
assert_eq!(coff.get_section_fixups(".text").unwrap().len(), 1);
}
#[test]
fn test_all_fixup_kinds_mapped_to_elf() {
let kinds = [
FixupKind::Abs64,
FixupKind::Abs32,
FixupKind::Rel32,
FixupKind::GotPcRel32,
FixupKind::Plt32,
FixupKind::TlsGd,
FixupKind::TpOff,
FixupKind::DtpOff,
];
for kind in &kinds {
let name = map_relocation_to_elf(*kind, true);
assert!(!name.is_empty(), "No ELF mapping for {:?}", kind);
}
}
#[test]
fn test_all_fixup_kinds_mapped_to_coff() {
let kinds = [
FixupKind::Abs64,
FixupKind::Abs32,
FixupKind::Rel32,
FixupKind::RipRel32,
];
for kind in &kinds {
let name = map_relocation_to_coff(*kind);
assert!(!name.is_empty(), "No COFF mapping for {:?}", kind);
}
}
#[test]
fn test_all_formats_have_text_section() {
let formats = [
TargetObjectFormat::ELF,
TargetObjectFormat::COFF,
TargetObjectFormat::MachO,
TargetObjectFormat::Binary,
TargetObjectFormat::XCOFF,
TargetObjectFormat::GOFF,
TargetObjectFormat::Wasm,
];
for fmt in &formats {
let s = X86MCTargetStreamer::new(*fmt, true);
assert!(!s.current_section.is_empty());
}
}
#[test]
fn test_all_streamer_types_constructable() {
let _s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
let _a = X86TargetAsmStreamer::new(TargetObjectFormat::ELF, true);
let _o = X86TargetObjectStreamer::new(TargetObjectFormat::ELF, true);
let _g = X86EmitGPRel32Value::new(TargetObjectFormat::ELF, true);
let _n = X86TargetNullStreamer::new(TargetObjectFormat::ELF, true);
let _r = X86TargetRecordStreamer::new(TargetObjectFormat::ELF, true);
}
#[test]
fn test_all_default_constructors() {
let _s = X86MCTargetStreamer::new(TargetObjectFormat::default(), true);
let _a = X86TargetAsmStreamer::default();
let _o = X86TargetObjectStreamer::default();
let _g = X86EmitGPRel32Value::default();
let _n = X86TargetNullStreamer::default();
let _r = X86TargetRecordStreamer::default();
let _lr = LabelResolver::default();
let _dh = DisassemblyHint::default();
}
#[test]
fn test_streamer_gotpcrel_fixup_kind() {
let s = X86MCTargetStreamer::new(TargetObjectFormat::ELF, true);
assert_eq!(s.gotpcrel_fixup_kind(), FixupKind::GotPcRel32);
}
}