use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum MCOperand {
Reg(u32),
Imm(i64),
FpImm(f64),
Expr(Box<MCExpr>),
Inst(Box<MCInst>),
}
impl MCOperand {
pub fn is_reg(&self) -> bool {
matches!(self, MCOperand::Reg(_))
}
pub fn is_imm(&self) -> bool {
matches!(self, MCOperand::Imm(_))
}
pub fn is_expr(&self) -> bool {
matches!(self, MCOperand::Expr(_))
}
pub fn reg(id: u32) -> Self {
MCOperand::Reg(id)
}
pub fn imm(val: i64) -> Self {
MCOperand::Imm(val)
}
pub fn get_reg(&self) -> Option<u32> {
if let MCOperand::Reg(r) = self {
Some(*r)
} else {
None
}
}
pub fn get_imm(&self) -> Option<i64> {
if let MCOperand::Imm(i) = self {
Some(*i)
} else {
None
}
}
pub fn fpimm(val: f64) -> Self {
MCOperand::FpImm(val)
}
pub fn is_fpimm(&self) -> bool {
matches!(self, MCOperand::FpImm(_))
}
pub fn is_inst(&self) -> bool {
matches!(self, MCOperand::Inst(_))
}
pub fn get_fpimm(&self) -> Option<f64> {
if let MCOperand::FpImm(v) = self {
Some(*v)
} else {
None
}
}
pub fn get_expr(&self) -> Option<&MCExpr> {
if let MCOperand::Expr(e) = self {
Some(e)
} else {
None
}
}
pub fn get_inst(&self) -> Option<&MCInst> {
if let MCOperand::Inst(i) = self {
Some(i)
} else {
None
}
}
pub fn kind_name(&self) -> &'static str {
match self {
MCOperand::Reg(_) => "Reg",
MCOperand::Imm(_) => "Imm",
MCOperand::FpImm(_) => "FpImm",
MCOperand::Expr(_) => "Expr",
MCOperand::Inst(_) => "Inst",
}
}
}
impl fmt::Display for MCOperand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MCOperand::Reg(r) => write!(f, "reg({})", r),
MCOperand::Imm(v) => write!(f, "{}", v),
MCOperand::FpImm(v) => write!(f, "{}", v),
MCOperand::Expr(e) => write!(f, "{}", e),
MCOperand::Inst(i) => write!(f, "{}", i),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum MCExpr {
Constant(i64),
SymbolRef { name: String, offset: i64 },
Binary {
op: MCBinaryOp,
lhs: Box<MCExpr>,
rhs: Box<MCExpr>,
},
Unary { op: MCUnaryOp, expr: Box<MCExpr> },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MCBinaryOp {
Add,
Sub,
Mul,
Div,
And,
Or,
Xor,
Shl,
Shr,
AShr,
LShr,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MCUnaryOp {
Neg,
Not,
}
impl MCExpr {
pub fn constant(val: i64) -> Self {
MCExpr::Constant(val)
}
pub fn symbol(name: &str) -> Self {
MCExpr::SymbolRef {
name: name.to_string(),
offset: 0,
}
}
pub fn expr_add(lhs: MCExpr, rhs: MCExpr) -> Self {
MCExpr::Binary {
op: MCBinaryOp::Add,
lhs: Box::new(lhs),
rhs: Box::new(rhs),
}
}
pub fn expr_sub(lhs: MCExpr, rhs: MCExpr) -> Self {
MCExpr::Binary {
op: MCBinaryOp::Sub,
lhs: Box::new(lhs),
rhs: Box::new(rhs),
}
}
pub fn evaluate(&self) -> Option<i64> {
match self {
MCExpr::Constant(v) => Some(*v),
MCExpr::SymbolRef { .. } => None,
MCExpr::Binary { op, lhs, rhs } => {
let l = lhs.evaluate()?;
let r = rhs.evaluate()?;
match op {
MCBinaryOp::Add => Some(l + r),
MCBinaryOp::Sub => Some(l - r),
MCBinaryOp::Mul => Some(l * r),
MCBinaryOp::Div => Some(if r != 0 { l / r } else { 0 }),
MCBinaryOp::And => Some(l & r),
MCBinaryOp::Or => Some(l | r),
MCBinaryOp::Xor => Some(l ^ r),
MCBinaryOp::Shl => Some(l << r),
MCBinaryOp::Shr => Some(l >> r),
MCBinaryOp::AShr => Some(l >> r),
MCBinaryOp::LShr => Some(l >> r),
}
}
MCExpr::Unary { op, expr } => {
let v = expr.evaluate()?;
match op {
MCUnaryOp::Neg => Some(-v),
MCUnaryOp::Not => Some(!v),
}
}
}
}
pub fn is_constant(&self) -> bool {
match self {
MCExpr::Constant(_) => true,
MCExpr::SymbolRef { .. } => false,
MCExpr::Binary { lhs, rhs, .. } => lhs.is_constant() && rhs.is_constant(),
MCExpr::Unary { expr, .. } => expr.is_constant(),
}
}
pub fn collect_symbols(&self) -> Vec<String> {
let mut syms = Vec::new();
self.collect_symbols_into(&mut syms);
syms
}
fn collect_symbols_into(&self, out: &mut Vec<String>) {
match self {
MCExpr::Constant(_) => {}
MCExpr::SymbolRef { name, .. } => {
if !out.contains(name) {
out.push(name.clone());
}
}
MCExpr::Binary { lhs, rhs, .. } => {
lhs.collect_symbols_into(out);
rhs.collect_symbols_into(out);
}
MCExpr::Unary { expr, .. } => {
expr.collect_symbols_into(out);
}
}
}
pub fn symbol_with_offset(name: &str, offset: i64) -> Self {
MCExpr::SymbolRef {
name: name.to_string(),
offset,
}
}
pub fn binary(op: MCBinaryOp, lhs: MCExpr, rhs: MCExpr) -> Self {
MCExpr::Binary {
op,
lhs: Box::new(lhs),
rhs: Box::new(rhs),
}
}
pub fn unary(op: MCUnaryOp, expr: MCExpr) -> Self {
MCExpr::Unary {
op,
expr: Box::new(expr),
}
}
pub fn fold_constants(&self) -> MCExpr {
match self {
MCExpr::Constant(_) | MCExpr::SymbolRef { .. } => self.clone(),
MCExpr::Binary { op, lhs, rhs } => {
let l = lhs.fold_constants();
let r = rhs.fold_constants();
if let (MCExpr::Constant(lv), MCExpr::Constant(rv)) = (&l, &r) {
let val = match op {
MCBinaryOp::Add => lv + rv,
MCBinaryOp::Sub => lv - rv,
MCBinaryOp::Mul => lv * rv,
MCBinaryOp::Div => {
if *rv != 0 {
lv / rv
} else {
return self.clone();
}
}
MCBinaryOp::And => lv & rv,
MCBinaryOp::Or => lv | rv,
MCBinaryOp::Xor => lv ^ rv,
MCBinaryOp::Shl => {
if *rv >= 0 && *rv < 64 {
lv << rv
} else {
return self.clone();
}
}
MCBinaryOp::Shr | MCBinaryOp::AShr => lv >> rv,
MCBinaryOp::LShr => {
let shift = *rv as u32;
if shift < 64 {
((*lv as u64) >> shift) as i64
} else {
return self.clone();
}
}
};
MCExpr::Constant(val)
} else {
MCExpr::Binary {
op: *op,
lhs: Box::new(l),
rhs: Box::new(r),
}
}
}
MCExpr::Unary { op, expr } => {
let e = expr.fold_constants();
if let MCExpr::Constant(v) = &e {
let val = match op {
MCUnaryOp::Neg => -v,
MCUnaryOp::Not => !v,
};
MCExpr::Constant(val)
} else {
MCExpr::Unary {
op: *op,
expr: Box::new(e),
}
}
}
}
}
}
impl fmt::Display for MCExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MCExpr::Constant(v) => write!(f, "{}", v),
MCExpr::SymbolRef { name, offset } => {
if *offset == 0 {
write!(f, "{}", name)
} else {
write!(f, "{}+{}", name, offset)
}
}
MCExpr::Binary { op, lhs, rhs } => {
let op_str = match op {
MCBinaryOp::Add => "+",
MCBinaryOp::Sub => "-",
MCBinaryOp::Mul => "*",
MCBinaryOp::Div => "/",
MCBinaryOp::And => "&",
MCBinaryOp::Or => "|",
MCBinaryOp::Xor => "^",
MCBinaryOp::Shl => "<<",
MCBinaryOp::Shr => ">>",
MCBinaryOp::AShr => ">>",
MCBinaryOp::LShr => ">>>",
};
write!(f, "({} {} {})", lhs, op_str, rhs)
}
MCExpr::Unary { op, expr } => match op {
MCUnaryOp::Neg => write!(f, "-{}", expr),
MCUnaryOp::Not => write!(f, "~{}", expr),
},
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MCInst {
pub opcode: u32,
pub operands: Vec<MCOperand>,
pub loc: Option<SourceLocation>,
pub flags: MCInstFlags,
pub size: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SourceLocation {
pub line: u32,
pub column: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct MCInstFlags {
pub is_terminator: bool,
pub is_branch: bool,
pub is_call: bool,
pub is_return: bool,
pub is_compare: bool,
pub is_move_imm: bool,
pub may_load: bool,
pub may_store: bool,
pub has_side_effects: bool,
pub is_barrier: bool,
pub is_indirect_branch: bool,
pub is_cond_branch: bool,
pub is_uncond_branch: bool,
pub is_trap: bool,
pub is_speculatable: bool,
}
impl MCInstFlags {
pub fn alu() -> Self {
Self::default()
}
pub fn load() -> Self {
Self {
may_load: true,
..Default::default()
}
}
pub fn store() -> Self {
Self {
may_store: true,
has_side_effects: true,
..Default::default()
}
}
pub fn branch() -> Self {
Self {
is_branch: true,
is_terminator: true,
..Default::default()
}
}
pub fn call() -> Self {
Self {
is_call: true,
may_store: true,
may_load: true,
..Default::default()
}
}
pub fn ret() -> Self {
Self {
is_return: true,
is_terminator: true,
..Default::default()
}
}
pub fn can_duplicate(&self) -> bool {
!self.has_side_effects && !self.is_terminator && !self.is_barrier
}
pub fn is_control_flow(&self) -> bool {
self.is_branch || self.is_call || self.is_return || self.is_barrier || self.is_trap
}
pub fn may_access_memory(&self) -> bool {
self.may_load || self.may_store
}
}
impl MCInst {
pub fn new(opcode: u32) -> Self {
Self {
opcode,
operands: Vec::new(),
loc: None,
flags: MCInstFlags::default(),
size: 0,
}
}
pub fn with_flags(opcode: u32, flags: MCInstFlags) -> Self {
Self {
opcode,
operands: Vec::new(),
loc: None,
flags,
size: 0,
}
}
pub fn add_operand(&mut self, op: MCOperand) {
self.operands.push(op);
}
pub fn operand(&self, i: usize) -> Option<&MCOperand> {
self.operands.get(i)
}
pub fn num_operands(&self) -> usize {
self.operands.len()
}
pub fn set_loc(&mut self, line: u32, column: u32) {
self.loc = Some(SourceLocation { line, column });
}
pub fn is_terminator(&self) -> bool {
self.flags.is_terminator
}
pub fn is_branch(&self) -> bool {
self.flags.is_branch
}
pub fn is_call(&self) -> bool {
self.flags.is_call
}
pub fn is_return(&self) -> bool {
self.flags.is_return
}
pub fn may_load(&self) -> bool {
self.flags.may_load
}
pub fn may_store(&self) -> bool {
self.flags.may_store
}
pub fn has_side_effects(&self) -> bool {
self.flags.has_side_effects
}
pub fn describe(&self) -> String {
format!(
"MCInst(opcode={}, ops={})",
self.opcode,
self.operands.len()
)
}
}
impl fmt::Display for MCInst {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<MCInst opcode={} ops=[", self.opcode)?;
for (i, op) in self.operands.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
match op {
MCOperand::Reg(r) => write!(f, "reg:{}", r)?,
MCOperand::Imm(v) => write!(f, "{}", v)?,
MCOperand::FpImm(v) => write!(f, "{}", v)?,
MCOperand::Expr(e) => write!(f, "{}", e)?,
MCOperand::Inst(_) => write!(f, "<inst>")?,
}
}
write!(f, "]>")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MCSection {
pub name: String,
pub section_type: MCSectionType,
pub flags: u32,
pub alignment: u32,
pub address: u64,
pub size: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MCSectionType {
Text,
Data,
Rodata,
Bss,
Custom,
}
impl MCSection {
pub fn new(name: &str, section_type: MCSectionType) -> Self {
Self {
name: name.to_string(),
section_type,
flags: 0,
alignment: 1,
address: 0,
size: 0,
}
}
pub fn text() -> Self {
Self {
name: ".text".to_string(),
section_type: MCSectionType::Text,
flags: 0x6, alignment: 16,
address: 0,
size: 0,
}
}
pub fn data() -> Self {
Self {
name: ".data".to_string(),
section_type: MCSectionType::Data,
flags: 0x3, alignment: 8,
address: 0,
size: 0,
}
}
pub fn rodata() -> Self {
Self {
name: ".rodata".to_string(),
section_type: MCSectionType::Rodata,
flags: 0x2, alignment: 8,
address: 0,
size: 0,
}
}
pub fn bss() -> Self {
Self {
name: ".bss".to_string(),
section_type: MCSectionType::Bss,
flags: 0x3, alignment: 8,
address: 0,
size: 0,
}
}
pub fn is_writable(&self) -> bool {
self.section_type == MCSectionType::Data || self.section_type == MCSectionType::Bss
}
pub fn is_text(&self) -> bool {
self.section_type == MCSectionType::Text
}
pub fn is_readonly(&self) -> bool {
self.section_type == MCSectionType::Rodata || self.section_type == MCSectionType::Text
}
pub fn with_alignment(mut self, align: u32) -> Self {
self.alignment = align;
self
}
pub fn with_flags(mut self, flags: u32) -> Self {
self.flags = flags;
self
}
}
impl fmt::Display for MCSection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} [{:?}]", self.name, self.section_type)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MCSymbol {
pub name: String,
pub section_index: Option<usize>,
pub value: u64,
pub size: u64,
pub is_global: bool,
pub is_weak: bool,
pub is_undefined: bool,
pub is_temporary: bool,
pub is_function: bool,
pub is_object: bool,
}
impl MCSymbol {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
section_index: None,
value: 0,
size: 0,
is_global: false,
is_weak: false,
is_undefined: true,
is_temporary: false,
is_function: false,
is_object: false,
}
}
pub fn temporary(id: u64) -> Self {
Self {
name: format!(".Ltmp{}", id),
section_index: None,
value: 0,
size: 0,
is_global: false,
is_weak: false,
is_undefined: false,
is_temporary: true,
is_function: false,
is_object: false,
}
}
pub fn set_global(mut self) -> Self {
self.is_global = true;
self.is_undefined = false;
self
}
pub fn set_weak(mut self) -> Self {
self.is_weak = true;
self
}
pub fn set_function(mut self) -> Self {
self.is_function = true;
self
}
pub fn set_object(mut self) -> Self {
self.is_object = true;
self
}
pub fn binding_str(&self) -> &'static str {
if self.is_weak {
"WEAK"
} else if self.is_global {
"GLOBAL"
} else {
"LOCAL"
}
}
pub fn type_str(&self) -> &'static str {
if self.is_function {
"FUNC"
} else if self.is_object {
"OBJECT"
} else {
"NOTYPE"
}
}
}
impl fmt::Display for MCSymbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}: value={:#x}, size={}, section={:?}, {} {}",
self.name,
self.value,
self.size,
self.section_index,
self.binding_str(),
self.type_str()
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MCLabel {
pub name: String,
pub section_index: usize,
pub offset: u64,
}
impl MCLabel {
pub fn new(name: &str, section_index: usize, offset: u64) -> Self {
Self {
name: name.to_string(),
section_index,
offset,
}
}
}
impl fmt::Display for MCLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}: section={} offset={:#x}",
self.name, self.section_index, self.offset
)
}
}
#[derive(Debug, Clone)]
pub enum MCFragment {
Instruction(MCInst),
Data(Vec<u8>),
Align(u32, u8),
Fill(u64, u8),
Org(u64, u8),
Label(MCLabel),
SymbolDef(String),
Relaxable(MCRelaxableFragment),
FillExpr(MCExpr, u64),
}
impl MCFragment {
pub fn size(&self) -> Option<u64> {
match self {
MCFragment::Instruction(_) | MCFragment::Relaxable(_) => None, MCFragment::Data(bytes) => Some(bytes.len() as u64),
MCFragment::Align(..) => None, MCFragment::Fill(size, _) => Some(*size),
MCFragment::Org(..) => None, MCFragment::Label(_) => Some(0),
MCFragment::SymbolDef(_) => Some(0),
MCFragment::FillExpr(_, size) => Some(*size),
}
}
pub fn is_instruction(&self) -> bool {
matches!(self, MCFragment::Instruction(_) | MCFragment::Relaxable(_))
}
pub fn is_directive(&self) -> bool {
matches!(
self,
MCFragment::Align(..)
| MCFragment::Fill(..)
| MCFragment::Org(..)
| MCFragment::Label(_)
| MCFragment::SymbolDef(_)
| MCFragment::FillExpr(..)
)
}
}
impl fmt::Display for MCFragment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MCFragment::Instruction(inst) => write!(f, "Instruction({})", inst.opcode),
MCFragment::Relaxable(rf) => write!(f, "Relaxable({})", rf.inst.opcode),
MCFragment::Data(bytes) => write!(f, "Data({} bytes)", bytes.len()),
MCFragment::Align(align, fill) => write!(f, "Align({}, {:#x})", align, fill),
MCFragment::Fill(size, fill) => write!(f, "Fill({}, {:#x})", size, fill),
MCFragment::Org(offset, fill) => write!(f, "Org({:#x}, {:#x})", offset, fill),
MCFragment::Label(lbl) => write!(f, "Label({})", lbl.name),
MCFragment::SymbolDef(name) => write!(f, "SymbolDef({})", name),
MCFragment::FillExpr(expr, size) => write!(f, "FillExpr({}, {})", expr, size),
}
}
}
#[derive(Debug, Clone)]
pub struct MCContext {
pub target_triple: String,
pub symbols: HashMap<String, MCSymbol>,
pub sections: Vec<MCSection>,
pub current_section: Option<usize>,
pub fragments: Vec<Vec<MCFragment>>,
pub next_temp_id: u64,
pub section_offsets: Vec<u64>,
}
impl MCContext {
pub fn new(triple: &str) -> Self {
Self {
target_triple: triple.to_string(),
symbols: HashMap::new(),
sections: Vec::new(),
current_section: None,
fragments: Vec::new(),
next_temp_id: 0,
section_offsets: Vec::new(),
}
}
pub fn create_symbol(&mut self, name: &str) -> MCSymbol {
if !self.symbols.contains_key(name) {
self.symbols.insert(name.to_string(), MCSymbol::new(name));
}
self.symbols.get(name).unwrap().clone()
}
pub fn create_temp_symbol(&mut self) -> MCSymbol {
let sym = MCSymbol::temporary(self.next_temp_id);
self.next_temp_id += 1;
let name = sym.name.clone();
self.symbols.insert(name, sym.clone());
sym
}
pub fn lookup_symbol(&self, name: &str) -> Option<&MCSymbol> {
self.symbols.get(name)
}
pub fn get_or_create_symbol(&mut self, name: &str) -> MCSymbol {
if !self.symbols.contains_key(name) {
self.symbols.insert(name.to_string(), MCSymbol::new(name));
}
self.symbols.get(name).unwrap().clone()
}
pub fn add_section(&mut self, name: &str, ty: MCSectionType) -> usize {
let index = self.sections.len();
self.sections.push(MCSection::new(name, ty));
self.fragments.push(Vec::new());
self.section_offsets.push(0);
index
}
pub fn switch_section(&mut self, index: usize) {
assert!(
index < self.sections.len(),
"MCContext: section index {} out of range",
index
);
self.current_section = Some(index);
}
pub fn get_current_section(&self) -> Option<usize> {
self.current_section
}
pub fn get_section(&self, index: usize) -> Option<&MCSection> {
self.sections.get(index)
}
pub fn emit_instruction(&mut self, inst: MCInst) {
if let Some(idx) = self.current_section {
self.fragments[idx].push(MCFragment::Instruction(inst));
}
}
pub fn emit_data(&mut self, data: Vec<u8>) {
if let Some(idx) = self.current_section {
self.section_offsets[idx] += data.len() as u64;
self.fragments[idx].push(MCFragment::Data(data));
}
}
pub fn emit_alignment(&mut self, align: u32) {
if let Some(idx) = self.current_section {
self.fragments[idx].push(MCFragment::Align(align, 0));
}
}
pub fn emit_label(&mut self, name: &str) {
if let Some(idx) = self.current_section {
let offset = self.section_offsets[idx];
let label = MCLabel::new(name, idx, offset);
self.fragments[idx].push(MCFragment::Label(label));
}
}
pub fn emit_symbol_def(&mut self, name: &str) {
if let Some(idx) = self.current_section {
self.fragments[idx].push(MCFragment::SymbolDef(name.to_string()));
}
}
pub fn emit_fill(&mut self, size: u64, fill_byte: u8) {
if let Some(idx) = self.current_section {
self.section_offsets[idx] += size;
self.fragments[idx].push(MCFragment::Fill(size, fill_byte));
}
}
pub fn get_current_offset(&self) -> u64 {
self.current_section
.map(|idx| self.section_offsets[idx])
.unwrap_or(0)
}
pub fn flatten(&self) -> Vec<u8> {
let mut result = Vec::new();
for (section_idx, fragment_list) in self.fragments.iter().enumerate() {
for fragment in fragment_list {
match fragment {
MCFragment::Data(bytes) => result.extend_from_slice(bytes),
MCFragment::Fill(size, fill_byte) => {
for _ in 0..*size {
result.push(*fill_byte);
}
}
MCFragment::Align(align, fill_byte) => {
let a = *align as u64;
let mask = a - 1;
let current = result.len() as u64;
let pad = (a - (current & mask)) & mask;
for _ in 0..pad {
result.push(*fill_byte);
}
}
MCFragment::Instruction(_) | MCFragment::Relaxable(_) => {
result.extend_from_slice(&[0x90]); }
_ => {} }
}
let align = self.sections[section_idx].alignment as u64;
if align > 1 {
let mask = align - 1;
let current = result.len() as u64;
let pad = (align - (current & mask)) & mask;
for _ in 0..pad {
result.push(0);
}
}
}
result
}
pub fn fragment_count(&self) -> usize {
self.fragments.iter().map(|f| f.len()).sum()
}
pub fn instruction_count(&self) -> usize {
self.fragments
.iter()
.flat_map(|f| f.iter())
.filter(|f| f.is_instruction())
.count()
}
pub fn clear(&mut self) {
self.symbols.clear();
self.sections.clear();
self.fragments.clear();
self.section_offsets.clear();
self.current_section = None;
self.next_temp_id = 0;
}
}
impl fmt::Display for MCContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "MCContext({})", self.target_triple)?;
writeln!(f, " Sections: {}", self.sections.len())?;
for (i, sec) in self.sections.iter().enumerate() {
let frag_count = self.fragments.get(i).map(|v| v.len()).unwrap_or(0);
writeln!(
f,
" [{}] {} ({} fragments, offset={:#x})",
i, sec.name, frag_count, self.section_offsets[i]
)?;
}
writeln!(f, " Symbols: {}", self.symbols.len())?;
for sym in self.symbols.values() {
writeln!(f, " {}", sym)?;
}
Ok(())
}
}
impl MCOperand {
pub fn create_imm_signed(val: i64, bits: u32) -> Option<Self> {
let min = -(1i64 << (bits - 1));
let max = (1i64 << (bits - 1)) - 1;
if val >= min && val <= max {
Some(MCOperand::Imm(val))
} else {
None
}
}
pub fn create_imm_unsigned(val: u64, bits: u32) -> Option<Self> {
let max = (1u64 << bits) - 1;
if val <= max {
Some(MCOperand::Imm(val as i64))
} else {
None
}
}
pub fn create_fpimm(val: f32) -> Self {
MCOperand::FpImm(val as f64)
}
pub fn create_dfpimm(val: f64) -> Self {
MCOperand::FpImm(val)
}
pub fn create_expr(expr: MCExpr) -> Self {
MCOperand::Expr(Box::new(expr))
}
pub fn create_inst(inst: MCInst) -> Self {
MCOperand::Inst(Box::new(inst))
}
pub fn create_mask(mask: u64) -> Self {
MCOperand::Imm(mask as i64)
}
pub fn get_imm_signed(&self) -> Option<i64> {
self.get_imm()
}
pub fn get_imm_unsigned(&self) -> Option<u64> {
self.get_imm().map(|v| v as u64)
}
pub fn get_fpimm_f32(&self) -> Option<f32> {
self.get_fpimm().map(|v| v as f32)
}
pub fn is_valid_reg(&self) -> bool {
matches!(self, MCOperand::Reg(_))
}
pub fn is_constant(&self) -> bool {
matches!(self, MCOperand::Imm(_))
}
pub fn is_expr_ref(&self) -> bool {
matches!(self, MCOperand::Expr(_))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MCSymbolRefVariantKind {
None,
Plt,
Got,
GotPcRel,
GotOff,
TlsGd,
TlsLd,
TlsLdm,
GotTpOff,
Indirect,
TpOff,
DtpOff,
NtpOff,
GotNtpOff,
TlsDesc,
}
impl MCSymbolRefVariantKind {
pub fn suffix(&self) -> &'static str {
match self {
MCSymbolRefVariantKind::None => "",
MCSymbolRefVariantKind::Plt => "@PLT",
MCSymbolRefVariantKind::Got => "@GOT",
MCSymbolRefVariantKind::GotPcRel => "@GOTPCREL",
MCSymbolRefVariantKind::GotOff => "@GOTOFF",
MCSymbolRefVariantKind::TlsGd => "@TLSGD",
MCSymbolRefVariantKind::TlsLd => "@TLSLD",
MCSymbolRefVariantKind::TlsLdm => "@TLSLDM",
MCSymbolRefVariantKind::GotTpOff => "@GOTTPOFF",
MCSymbolRefVariantKind::Indirect => "@INDIRECT",
MCSymbolRefVariantKind::TpOff => "@TPOFF",
MCSymbolRefVariantKind::DtpOff => "@DTPOFF",
MCSymbolRefVariantKind::NtpOff => "@NTPOFF",
MCSymbolRefVariantKind::GotNtpOff => "@GOTNTPOFF",
MCSymbolRefVariantKind::TlsDesc => "@TLS DESC",
}
}
}
#[derive(Debug, Clone)]
pub struct MCSymbolRefExpr {
pub name: String,
pub offset: i64,
pub variant_kind: MCSymbolRefVariantKind,
}
impl MCSymbolRefExpr {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
offset: 0,
variant_kind: MCSymbolRefVariantKind::None,
}
}
pub fn with_offset(name: &str, offset: i64) -> Self {
Self {
name: name.to_string(),
offset,
variant_kind: MCSymbolRefVariantKind::None,
}
}
pub fn with_variant(name: &str, variant: MCSymbolRefVariantKind) -> Self {
Self {
name: name.to_string(),
offset: 0,
variant_kind: variant,
}
}
}
impl fmt::Display for MCSymbolRefExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.offset == 0 {
write!(f, "{}{}", self.name, self.variant_kind.suffix())
} else if self.offset > 0 {
write!(
f,
"{}+0x{:x}{}",
self.name,
self.offset,
self.variant_kind.suffix()
)
} else {
write!(
f,
"{}-0x{:x}{}",
self.name,
-self.offset,
self.variant_kind.suffix()
)
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct MCConstantExpr {
pub value: i64,
}
impl MCConstantExpr {
pub fn new(value: i64) -> Self {
Self { value }
}
}
impl fmt::Display for MCConstantExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x{:x}", self.value)
}
}
#[derive(Debug, Clone)]
pub struct MCUnaryExpr {
pub op: MCUnaryOp,
pub expr: Box<MCExpr>,
}
impl MCUnaryExpr {
pub fn new(op: MCUnaryOp, expr: MCExpr) -> Self {
Self {
op,
expr: Box::new(expr),
}
}
}
#[derive(Debug, Clone)]
pub struct MCBinaryExpr {
pub op: MCBinaryOp,
pub lhs: Box<MCExpr>,
pub rhs: Box<MCExpr>,
}
impl MCBinaryExpr {
pub fn new(op: MCBinaryOp, lhs: MCExpr, rhs: MCExpr) -> Self {
Self {
op,
lhs: Box::new(lhs),
rhs: Box::new(rhs),
}
}
}
#[derive(Debug, Clone)]
pub struct MCTargetExpr {
pub target: String,
pub kind: u32,
pub data: Vec<u8>,
}
impl MCTargetExpr {
pub fn new(target: &str, kind: u32) -> Self {
Self {
target: target.to_string(),
kind,
data: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub enum MCValue {
Absolute(i64),
SectionRelative(usize, i64),
SymbolRelative(String, i64),
Unevaluable(String),
}
impl MCExpr {
pub fn evaluate_as_absolute(&self) -> Option<i64> {
self.evaluate()
}
pub fn evaluate_as_relocatable(&self) -> Option<(usize, i64)> {
None
}
pub fn evaluate_as_value(&self) -> MCValue {
match self.evaluate() {
Some(v) => MCValue::Absolute(v),
None => MCValue::Unevaluable(String::new()),
}
}
pub fn has_symbol(&self, name: &str) -> bool {
let symbols = self.collect_symbols();
symbols.iter().any(|n| n == name)
}
pub fn symbol_with_variant(name: &str, variant: MCSymbolRefVariantKind) -> Self {
MCExpr::SymbolRef {
name: name.to_string(),
offset: 0,
}
}
pub fn plt(name: &str) -> Self {
Self::symbol_with_variant(name, MCSymbolRefVariantKind::Plt)
}
pub fn got(name: &str) -> Self {
Self::symbol_with_variant(name, MCSymbolRefVariantKind::Got)
}
pub fn gotpcrel(name: &str) -> Self {
Self::symbol_with_variant(name, MCSymbolRefVariantKind::GotPcRel)
}
pub fn gotoff(name: &str) -> Self {
Self::symbol_with_variant(name, MCSymbolRefVariantKind::GotOff)
}
pub fn tlsgd(name: &str) -> Self {
Self::symbol_with_variant(name, MCSymbolRefVariantKind::TlsGd)
}
pub fn tlsld(name: &str) -> Self {
Self::symbol_with_variant(name, MCSymbolRefVariantKind::TlsLd)
}
pub fn gottpoff(name: &str) -> Self {
Self::symbol_with_variant(name, MCSymbolRefVariantKind::GotTpOff)
}
pub fn tpoff(name: &str) -> Self {
Self::symbol_with_variant(name, MCSymbolRefVariantKind::TpOff)
}
pub fn dtpoff(name: &str) -> Self {
Self::symbol_with_variant(name, MCSymbolRefVariantKind::DtpOff)
}
}
#[derive(Debug, Clone)]
pub struct MCInstBuilder {
inst: MCInst,
}
impl MCInstBuilder {
pub fn new(opcode: u32) -> Self {
Self {
inst: MCInst::new(opcode),
}
}
pub fn with_flags(mut self, flags: MCInstFlags) -> Self {
self.inst.flags = flags;
self
}
pub fn set_opcode(mut self, opcode: u32) -> Self {
self.inst.opcode = opcode;
self
}
pub fn set_loc(mut self, line: u32, column: u32) -> Self {
self.inst.loc = Some(SourceLocation { line, column });
self
}
pub fn add_operand(mut self, op: MCOperand) -> Self {
self.inst.add_operand(op);
self
}
pub fn add_reg(mut self, reg: u32) -> Self {
self.inst.add_operand(MCOperand::Reg(reg));
self
}
pub fn add_imm(mut self, imm: i64) -> Self {
self.inst.add_operand(MCOperand::Imm(imm));
self
}
pub fn add_fpimm(mut self, val: f64) -> Self {
self.inst.add_operand(MCOperand::FpImm(val));
self
}
pub fn add_expr(mut self, expr: MCExpr) -> Self {
self.inst.add_operand(MCOperand::Expr(Box::new(expr)));
self
}
pub fn add_inst(mut self, nested: MCInst) -> Self {
self.inst.add_operand(MCOperand::Inst(Box::new(nested)));
self
}
pub fn add_op_chain(self, ops: &[MCOperand]) -> Self {
let mut me = self;
for op in ops {
me.inst.add_operand(op.clone());
}
me
}
pub fn build(self) -> MCInst {
self.inst
}
}
impl From<MCInstBuilder> for MCInst {
fn from(builder: MCInstBuilder) -> Self {
builder.build()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MCFixupKind {
Abs32,
Abs64,
PcRel32,
PcRel8,
RelocRipRel4Byte,
RelocRipRel4ByteMovqLoad,
RelocSigned4Byte,
RelocUnsigned4Byte,
RelocBranch4BytePcrel,
RelocGotPcRel,
RelocPlt32,
RelocGotOff,
RelocTlsGd,
RelocTlsLd,
RelocGotTpOff,
RelocTpOff32,
RelocDtpOff32,
A64AdrPrelPgHi21,
A64AddAbsLo12Nc,
A64LdstImm12,
A64Branch26,
A64CondBranch19,
A64Movw,
A64GotLdPrel19,
A64TlsDesc,
RiscvHi20,
RiscvLo12I,
RiscvLo12S,
RiscvBranch,
RiscvJal,
RiscvCall,
RiscvPcrelHi20,
RiscvGotHi20,
Unknown,
}
impl MCFixupKind {
pub fn size(&self) -> u8 {
match self {
MCFixupKind::Abs32 | MCFixupKind::PcRel32 => 4,
MCFixupKind::Abs64 => 8,
MCFixupKind::PcRel8 => 1,
MCFixupKind::RelocRipRel4Byte
| MCFixupKind::RelocRipRel4ByteMovqLoad
| MCFixupKind::RelocSigned4Byte
| MCFixupKind::RelocUnsigned4Byte
| MCFixupKind::RelocBranch4BytePcrel
| MCFixupKind::RelocGotPcRel
| MCFixupKind::RelocPlt32
| MCFixupKind::RelocGotOff
| MCFixupKind::RelocTpOff32
| MCFixupKind::RelocDtpOff32 => 4,
MCFixupKind::A64AdrPrelPgHi21
| MCFixupKind::A64AddAbsLo12Nc
| MCFixupKind::A64LdstImm12 => 4,
MCFixupKind::A64Branch26 | MCFixupKind::A64CondBranch19 => 4,
MCFixupKind::RiscvHi20 | MCFixupKind::RiscvLo12I | MCFixupKind::RiscvLo12S => 4,
_ => 4,
}
}
pub fn is_pc_relative(&self) -> bool {
matches!(
self,
MCFixupKind::PcRel32
| MCFixupKind::PcRel8
| MCFixupKind::RelocRipRel4Byte
| MCFixupKind::RelocRipRel4ByteMovqLoad
| MCFixupKind::RelocBranch4BytePcrel
| MCFixupKind::RelocGotPcRel
| MCFixupKind::RelocPlt32
| MCFixupKind::A64Branch26
| MCFixupKind::A64CondBranch19
| MCFixupKind::RiscvBranch
| MCFixupKind::RiscvJal
| MCFixupKind::RiscvPcrelHi20
)
}
}
#[derive(Debug, Clone)]
pub struct MCFixup {
pub offset: usize,
pub kind: MCFixupKind,
pub value: i64,
pub symbol: Option<String>,
pub loc: Option<SourceLocation>,
}
impl MCFixup {
pub fn new(offset: usize, kind: MCFixupKind) -> Self {
Self {
offset,
kind,
value: 0,
symbol: None,
loc: None,
}
}
pub fn with_value(mut self, value: i64) -> Self {
self.value = value;
self
}
pub fn with_symbol(mut self, sym: &str) -> Self {
self.symbol = Some(sym.to_string());
self
}
pub fn size(&self) -> u8 {
self.kind.size()
}
}
#[derive(Debug, Clone)]
pub struct MCDataFragment {
pub data: Vec<u8>,
pub fixups: Vec<MCFixup>,
pub alignment: u8,
}
impl MCDataFragment {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
fixups: Vec::new(),
alignment: 1,
}
}
pub fn size(&self) -> usize {
self.data.len()
}
pub fn add_fixup(&mut self, fixup: MCFixup) {
self.fixups.push(fixup);
}
pub fn write_u8(&mut self, offset: usize, val: u8) {
if offset < self.data.len() {
self.data[offset] = val;
}
}
pub fn write_u32_le(&mut self, offset: usize, val: u32) {
if offset + 4 <= self.data.len() {
self.data[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
}
}
pub fn write_u64_le(&mut self, offset: usize, val: u64) {
if offset + 8 <= self.data.len() {
self.data[offset..offset + 8].copy_from_slice(&val.to_le_bytes());
}
}
}
#[derive(Debug, Clone)]
pub struct MCAlignFragment {
pub alignment: u32,
pub value: u8,
pub max_bytes_to_emit: u32,
pub emit_nops: bool,
}
impl MCAlignFragment {
pub fn new(alignment: u32, value: u8) -> Self {
Self {
alignment,
value,
max_bytes_to_emit: 0,
emit_nops: false,
}
}
pub fn with_max_bytes(mut self, max: u32) -> Self {
self.max_bytes_to_emit = max;
self
}
pub fn with_nops(mut self, emit: bool) -> Self {
self.emit_nops = emit;
self
}
pub fn pad_size(&self, current_offset: usize) -> usize {
let align = self.alignment as usize;
if align == 0 {
return 0;
}
let remainder = current_offset % align;
if remainder == 0 {
0
} else {
let pad = align - remainder;
if self.max_bytes_to_emit > 0 && pad > self.max_bytes_to_emit as usize {
return 0;
}
pad
}
}
}
#[derive(Debug, Clone)]
pub struct MCFillFragment {
pub num_values: usize,
pub value_size: usize,
pub value: u64,
}
impl MCFillFragment {
pub fn new(count: usize, value: u64, size: usize) -> Self {
Self {
num_values: count,
value_size: size,
value,
}
}
pub fn size(&self) -> usize {
self.num_values * self.value_size
}
pub fn emit(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.size());
for _ in 0..self.num_values {
match self.value_size {
1 => out.push(self.value as u8),
2 => out.extend_from_slice(&(self.value as u16).to_le_bytes()),
4 => out.extend_from_slice(&(self.value as u32).to_le_bytes()),
8 => out.extend_from_slice(&self.value.to_le_bytes()),
_ => {
for _ in 0..self.value_size {
out.push(self.value as u8);
}
}
}
}
out
}
}
#[derive(Debug, Clone)]
pub struct MCOrgFragment {
pub offset: i64,
pub value: u8,
}
impl MCOrgFragment {
pub fn new(offset: i64, fill: u8) -> Self {
Self {
offset,
value: fill,
}
}
}
#[derive(Debug, Clone)]
pub struct MCCompactEncodedInstFragment {
pub inst: MCInst,
pub encoded: Vec<u8>,
}
impl MCCompactEncodedInstFragment {
pub fn new(inst: MCInst, encoded: Vec<u8>) -> Self {
Self { inst, encoded }
}
pub fn size(&self) -> usize {
self.encoded.len()
}
}
#[derive(Debug, Clone)]
pub struct MCRelaxableFragment {
pub inst: MCInst,
pub small_encoding: Vec<u8>,
pub large_encoding: Vec<u8>,
pub is_relaxed: bool,
}
impl MCRelaxableFragment {
pub fn new(inst: MCInst, small: Vec<u8>, large: Vec<u8>) -> Self {
Self {
inst,
small_encoding: small,
large_encoding: large,
is_relaxed: false,
}
}
pub fn relax(&mut self) {
self.is_relaxed = true;
}
pub fn current_encoding(&self) -> &[u8] {
if self.is_relaxed {
&self.large_encoding
} else {
&self.small_encoding
}
}
pub fn size(&self) -> usize {
self.current_encoding().len()
}
}
#[derive(Debug, Clone)]
pub struct MCDwarfLineAddrFragment {
pub line_delta: i64,
pub addr_delta: i64,
}
impl MCDwarfLineAddrFragment {
pub fn new(line_delta: i64, addr_delta: i64) -> Self {
Self {
line_delta,
addr_delta,
}
}
}
#[derive(Debug, Clone)]
pub struct MCDwarfCallFrameFragment {
pub cfi_instructions: Vec<u8>,
}
impl MCDwarfCallFrameFragment {
pub fn new() -> Self {
Self {
cfi_instructions: Vec::new(),
}
}
pub fn emit(&self) -> &[u8] {
&self.cfi_instructions
}
}
#[derive(Debug, Clone)]
pub struct MCLEBFragment {
pub value: i64,
pub is_signed: bool,
}
impl MCLEBFragment {
pub fn new(value: i64, signed: bool) -> Self {
Self {
value,
is_signed: signed,
}
}
pub fn encode_uleb128(value: u64) -> Vec<u8> {
let mut v = value;
let mut out = Vec::new();
loop {
let mut byte = (v & 0x7F) as u8;
v >>= 7;
if v != 0 {
byte |= 0x80;
}
out.push(byte);
if v == 0 {
break;
}
}
out
}
pub fn encode_sleb128(value: i64) -> Vec<u8> {
let mut v = value;
let mut out = Vec::new();
loop {
let mut byte = (v as u8) & 0x7F;
v >>= 7;
let done = (v == 0 && (byte & 0x40) == 0) || (v == -1 && (byte & 0x40) != 0);
if !done {
byte |= 0x80;
}
out.push(byte);
if done {
break;
}
}
out
}
}
#[derive(Debug, Clone)]
pub struct MCBoundaryAlignFragment {
pub alignment: u32,
pub max_bytes_to_emit: u32,
}
impl MCBoundaryAlignFragment {
pub fn new(alignment: u32) -> Self {
Self {
alignment,
max_bytes_to_emit: 0,
}
}
pub fn with_max_bytes(mut self, max: u32) -> Self {
self.max_bytes_to_emit = max;
self
}
pub fn check_boundary_cross(&self, offset: usize, size: usize) -> bool {
let align = self.alignment as usize;
let start = offset / align;
let end = (offset + size - 1) / align;
start != end
}
}
#[derive(Debug, Clone)]
pub struct MCSectionELF {
pub name: String,
pub section_type: u32, pub flags: u64, pub entry_size: u64,
pub group_name: Option<String>,
pub unique_id: Option<u32>,
pub is_comdat: bool,
}
impl MCSectionELF {
pub fn new(name: &str, ty: u32, flags: u64) -> Self {
Self {
name: name.to_string(),
section_type: ty,
flags,
entry_size: 0,
group_name: None,
unique_id: None,
is_comdat: false,
}
}
pub fn text() -> Self {
const SHT_PROGBITS: u32 = 1;
const SHF_ALLOC: u64 = 0x2;
const SHF_EXECINSTR: u64 = 0x4;
Self::new(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR)
}
pub fn data() -> Self {
const SHT_PROGBITS: u32 = 1;
const SHF_ALLOC: u64 = 0x2;
const SHF_WRITE: u64 = 0x1;
Self::new(".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE)
}
pub fn rodata() -> Self {
const SHT_PROGBITS: u32 = 1;
const SHF_ALLOC: u64 = 0x2;
Self::new(".rodata", SHT_PROGBITS, SHF_ALLOC)
}
pub fn bss() -> Self {
const SHT_NOBITS: u32 = 8;
const SHF_ALLOC: u64 = 0x2;
const SHF_WRITE: u64 = 0x1;
Self::new(".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE)
}
pub fn is_writable(&self) -> bool {
const SHF_WRITE: u64 = 0x1;
self.flags & SHF_WRITE != 0
}
pub fn is_executable(&self) -> bool {
const SHF_EXECINSTR: u64 = 0x4;
self.flags & SHF_EXECINSTR != 0
}
pub fn is_allocatable(&self) -> bool {
const SHF_ALLOC: u64 = 0x2;
self.flags & SHF_ALLOC != 0
}
pub fn is_comdat(&self) -> bool {
self.is_comdat
}
}
impl fmt::Display for MCSectionELF {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"<ELF section '{}' type={} flags=0x{:x}>",
self.name, self.section_type, self.flags
)
}
}
#[derive(Debug, Clone)]
pub struct MCSectionMachO {
pub segment_name: String,
pub section_name: String,
pub section_type: u32,
pub attributes: u32,
pub reserved1: u32,
pub reserved2: u32,
}
impl MCSectionMachO {
pub fn new(segment: &str, section: &str) -> Self {
Self {
segment_name: segment.to_string(),
section_name: section.to_string(),
section_type: 0, attributes: 0,
reserved1: 0,
reserved2: 0,
}
}
pub fn text(segment: &str) -> Self {
Self {
segment_name: segment.to_string(),
section_name: "__text".to_string(),
section_type: 0,
attributes: 0x80000400, reserved1: 0,
reserved2: 0,
}
}
pub fn data(segment: &str) -> Self {
Self {
segment_name: segment.to_string(),
section_name: "__data".to_string(),
section_type: 0,
attributes: 0,
reserved1: 0,
reserved2: 0,
}
}
pub fn bss(segment: &str) -> Self {
Self {
segment_name: segment.to_string(),
section_name: "__bss".to_string(),
section_type: 1, attributes: 0,
reserved1: 0,
reserved2: 0,
}
}
pub fn rodata(segment: &str) -> Self {
Self {
segment_name: segment.to_string(),
section_name: "__const".to_string(),
section_type: 0,
attributes: 0,
reserved1: 0,
reserved2: 0,
}
}
}
impl fmt::Display for MCSectionMachO {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"<Mach-O section {},{},{}>",
self.segment_name, self.section_name, self.section_type
)
}
}
#[derive(Debug, Clone)]
pub struct MCSectionCOFF {
pub name: String,
pub characteristics: u32,
pub alignment: u32,
pub aux_section_def: Option<String>,
}
impl MCSectionCOFF {
pub fn new(name: &str, characteristics: u32) -> Self {
Self {
name: name.to_string(),
characteristics,
alignment: 16,
aux_section_def: None,
}
}
pub fn text() -> Self {
const IMAGE_SCN_CNT_CODE: u32 = 0x20;
const IMAGE_SCN_MEM_EXECUTE: u32 = 0x20000000;
const IMAGE_SCN_MEM_READ: u32 = 0x40000000;
Self::new(
".text",
IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ,
)
}
pub fn data() -> Self {
const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x40;
const IMAGE_SCN_MEM_READ: u32 = 0x40000000;
const IMAGE_SCN_MEM_WRITE: u32 = 0x80000000;
Self::new(
".data",
IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
)
}
pub fn bss() -> Self {
const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x80;
const IMAGE_SCN_MEM_READ: u32 = 0x40000000;
const IMAGE_SCN_MEM_WRITE: u32 = 0x80000000;
Self::new(
".bss",
IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
)
}
pub fn rodata() -> Self {
const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x40;
const IMAGE_SCN_MEM_READ: u32 = 0x40000000;
Self::new(
".rdata",
IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
)
}
pub fn is_executable(&self) -> bool {
const IMAGE_SCN_MEM_EXECUTE: u32 = 0x20000000;
self.characteristics & IMAGE_SCN_MEM_EXECUTE != 0
}
pub fn is_writable(&self) -> bool {
const IMAGE_SCN_MEM_WRITE: u32 = 0x80000000;
self.characteristics & IMAGE_SCN_MEM_WRITE != 0
}
pub fn is_readable(&self) -> bool {
const IMAGE_SCN_MEM_READ: u32 = 0x40000000;
self.characteristics & IMAGE_SCN_MEM_READ != 0
}
}
impl fmt::Display for MCSectionCOFF {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"<COFF section '{}' ch=0x{:x}>",
self.name, self.characteristics
)
}
}
#[derive(Debug, Clone)]
pub struct MCSectionWasm {
pub name: String,
pub section_type: u8,
pub group_name: Option<String>,
pub comdat_name: Option<String>,
pub unique_id: Option<u32>,
}
impl MCSectionWasm {
pub fn new(name: &str, ty: u8) -> Self {
Self {
name: name.to_string(),
section_type: ty,
group_name: None,
comdat_name: None,
unique_id: None,
}
}
pub const TYPE: u8 = 1;
pub const IMPORT: u8 = 2;
pub const FUNCTION: u8 = 3;
pub const TABLE: u8 = 4;
pub const MEMORY: u8 = 5;
pub const GLOBAL: u8 = 6;
pub const EXPORT: u8 = 7;
pub const START: u8 = 8;
pub const ELEMENT: u8 = 9;
pub const CODE: u8 = 10;
pub const DATA: u8 = 11;
pub const DATA_COUNT: u8 = 12;
pub fn code() -> Self {
Self::new("code", Self::CODE)
}
pub fn data_section() -> Self {
Self::new("data", Self::DATA)
}
pub fn is_code(&self) -> bool {
self.section_type == Self::CODE
}
pub fn is_data(&self) -> bool {
self.section_type == Self::DATA
}
}
impl fmt::Display for MCSectionWasm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"<Wasm section '{}' type={}>",
self.name, self.section_type
)
}
}
#[derive(Debug, Clone)]
pub struct MCInstrDesc {
pub opcode: u32,
pub mnemonic: String,
pub num_operands: u32,
pub num_defs: u32,
pub size: u32,
pub flags: MCInstFlags,
pub operand_info: Vec<MCOperandInfo>,
pub implicit_defs: Vec<u32>,
pub implicit_uses: Vec<u32>,
pub scheduling_class: u32,
}
impl MCInstrDesc {
pub fn new(opcode: u32, mnemonic: &str) -> Self {
Self {
opcode,
mnemonic: mnemonic.to_string(),
num_operands: 0,
num_defs: 0,
size: 0,
flags: MCInstFlags {
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
is_compare: false,
is_move_imm: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_barrier: false,
is_indirect_branch: false,
is_cond_branch: false,
is_uncond_branch: false,
is_trap: false,
is_speculatable: true,
},
operand_info: Vec::new(),
implicit_defs: Vec::new(),
implicit_uses: Vec::new(),
scheduling_class: 0,
}
}
pub fn with_flags(mut self, flags: MCInstFlags) -> Self {
self.flags = flags;
self
}
pub fn add_operand_info(mut self, info: MCOperandInfo) -> Self {
self.operand_info.push(info);
self.num_operands = self.operand_info.len() as u32;
self
}
pub fn add_implicit_def(mut self, reg: u32) -> Self {
self.implicit_defs.push(reg);
self
}
pub fn add_implicit_use(mut self, reg: u32) -> Self {
self.implicit_uses.push(reg);
self
}
pub fn may_load(&self) -> bool {
self.flags.may_load
}
pub fn may_store(&self) -> bool {
self.flags.may_store
}
pub fn is_branch(&self) -> bool {
self.flags.is_branch
}
pub fn is_call(&self) -> bool {
self.flags.is_call
}
pub fn is_return(&self) -> bool {
self.flags.is_return
}
pub fn is_terminal(&self) -> bool {
self.flags.is_terminator
}
}
#[derive(Debug, Clone)]
pub struct MCOperandInfo {
pub reg_class: u32,
pub operand_type: MCOperandType,
pub flags: u32,
pub constraints: Vec<MCOperandConstraint>,
}
impl MCOperandInfo {
pub fn new(reg_class: u32, ty: MCOperandType) -> Self {
Self {
reg_class,
operand_type: ty,
flags: 0,
constraints: Vec::new(),
}
}
pub fn is_optional(&self) -> bool {
self.flags & 1 != 0
}
pub fn is_tied_to_def(&self) -> bool {
self.flags & 2 != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MCOperandType {
Register,
Immediate,
Memory,
PCRelative,
BranchTarget,
FloatingPoint,
Predicate,
Unknown,
}
#[derive(Debug, Clone)]
pub enum MCOperandConstraint {
Range(i64, i64),
AlignTo(u32),
PowerOf2,
NotZero,
SpecificValue(i64),
RegisterClass(u32),
}
#[derive(Debug, Clone)]
pub struct MCInstrInfo {
pub target: String,
pub descs: HashMap<u32, MCInstrDesc>,
pub opcode_names: HashMap<String, u32>,
}
impl MCInstrInfo {
pub fn new(target: &str) -> Self {
Self {
target: target.to_string(),
descs: HashMap::new(),
opcode_names: HashMap::new(),
}
}
pub fn add_desc(&mut self, desc: MCInstrDesc) {
self.opcode_names.insert(desc.mnemonic.clone(), desc.opcode);
self.descs.insert(desc.opcode, desc);
}
pub fn get_desc(&self, opcode: u32) -> Option<&MCInstrDesc> {
self.descs.get(&opcode)
}
pub fn lookup_opcode(&self, name: &str) -> Option<u32> {
self.opcode_names.get(name).copied()
}
pub fn num_instructions(&self) -> usize {
self.descs.len()
}
}
#[derive(Debug, Clone)]
pub struct MCRegisterDesc {
pub name: String,
pub encoding: u32,
pub is_allocatable: bool,
pub cost: u8,
pub aliases: Vec<u32>,
pub sub_regs: Vec<(u32, u32)>, }
impl MCRegisterDesc {
pub fn new(name: &str, encoding: u32) -> Self {
Self {
name: name.to_string(),
encoding,
is_allocatable: true,
cost: 1,
aliases: Vec::new(),
sub_regs: Vec::new(),
}
}
pub fn reserved(name: &str, encoding: u32) -> Self {
Self {
name: name.to_string(),
encoding,
is_allocatable: false,
cost: 0,
aliases: Vec::new(),
sub_regs: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct MCRegisterClass {
pub name: String,
pub id: u32,
pub regs: Vec<u32>,
pub alignment: u32,
pub copy_cost: u32,
pub is_allocatable: bool,
}
impl MCRegisterClass {
pub fn new(name: &str, id: u32) -> Self {
Self {
name: name.to_string(),
id,
regs: Vec::new(),
alignment: 1,
copy_cost: 1,
is_allocatable: true,
}
}
pub fn contains(&self, reg: u32) -> bool {
self.regs.contains(®)
}
pub fn size(&self) -> usize {
self.regs.len()
}
pub fn reg_id(&self, idx: usize) -> Option<u32> {
self.regs.get(idx).copied()
}
}
#[derive(Debug, Clone)]
pub struct MCRegisterInfo {
pub target: String,
pub regs: Vec<MCRegisterDesc>,
pub classes: Vec<MCRegisterClass>,
pub reg_to_name: HashMap<u32, String>,
pub name_to_reg: HashMap<String, u32>,
pub num_allocatable_regs: u32,
pub subreg_indices: Vec<String>,
}
impl MCRegisterInfo {
pub fn new(target: &str) -> Self {
Self {
target: target.to_string(),
regs: Vec::new(),
classes: Vec::new(),
reg_to_name: HashMap::new(),
name_to_reg: HashMap::new(),
num_allocatable_regs: 0,
subreg_indices: Vec::new(),
}
}
pub fn add_reg(&mut self, reg: MCRegisterDesc) -> u32 {
let id = self.regs.len() as u32;
let name = reg.name.clone();
let encoding = reg.encoding;
self.regs.push(reg);
self.reg_to_name.insert(encoding, name.clone());
self.name_to_reg.insert(name, encoding);
if self.regs[id as usize].is_allocatable {
self.num_allocatable_regs += 1;
}
id
}
pub fn add_class(&mut self, class: MCRegisterClass) -> u32 {
let id = self.classes.len() as u32;
self.classes.push(class);
id
}
pub fn get_reg_name(&self, encoding: u32) -> Option<&String> {
self.reg_to_name.get(&encoding)
}
pub fn lookup_reg(&self, name: &str) -> Option<u32> {
self.name_to_reg.get(name).copied()
}
pub fn get_class(&self, id: u32) -> Option<&MCRegisterClass> {
self.classes.get(id as usize)
}
pub fn get_reg_desc(&self, id: u32) -> Option<&MCRegisterDesc> {
self.regs.get(id as usize)
}
}
#[derive(Debug, Clone)]
pub struct FeatureBits {
pub bits: Vec<u64>,
}
impl FeatureBits {
pub fn new(num_features: usize) -> Self {
Self {
bits: vec![0u64; (num_features + 63) / 64],
}
}
pub fn set(&mut self, idx: usize) {
let word = idx / 64;
let bit = idx % 64;
if word < self.bits.len() {
self.bits[word] |= 1u64 << bit;
}
}
pub fn clear(&mut self, idx: usize) {
let word = idx / 64;
let bit = idx % 64;
if word < self.bits.len() {
self.bits[word] &= !(1u64 << bit);
}
}
pub fn test(&self, idx: usize) -> bool {
let word = idx / 64;
let bit = idx % 64;
if word < self.bits.len() {
self.bits[word] & (1u64 << bit) != 0
} else {
false
}
}
pub fn union(&self, other: &FeatureBits) -> Self {
let mut result = self.clone();
for (a, b) in result.bits.iter_mut().zip(other.bits.iter()) {
*a |= b;
}
result
}
pub fn intersect(&self, other: &FeatureBits) -> Self {
let mut result = self.clone();
for (a, b) in result.bits.iter_mut().zip(other.bits.iter()) {
*a &= b;
}
result
}
}
#[derive(Debug, Clone)]
pub struct MCSubtargetInfo {
pub target_triple: String,
pub cpu: String,
pub features: FeatureBits,
pub feature_names: Vec<String>,
pub has_fpu: bool,
pub has_vector: bool,
pub has_atomics: bool,
pub has_compressed: bool,
}
impl MCSubtargetInfo {
pub fn new(triple: &str, cpu: &str) -> Self {
Self {
target_triple: triple.to_string(),
cpu: cpu.to_string(),
features: FeatureBits::new(128),
feature_names: Vec::new(),
has_fpu: false,
has_vector: false,
has_atomics: false,
has_compressed: false,
}
}
pub fn add_feature(&mut self, name: &str) {
let idx = self.feature_names.len();
self.feature_names.push(name.to_string());
self.features.set(idx);
match name {
"fp" | "fpu" | "hard-float" => self.has_fpu = true,
"simd" | "neon" | "sve" | "vector" => self.has_vector = true,
"atomics" | "a" => self.has_atomics = true,
"compressed" | "c" => self.has_compressed = true,
_ => {}
}
}
pub fn has_feature(&self, name: &str) -> bool {
if let Some(idx) = self.feature_names.iter().position(|n| n == name) {
self.features.test(idx)
} else {
false
}
}
}
#[derive(Debug, Clone)]
pub struct MCRelocationEntry {
pub offset: u64,
pub symbol: String,
pub kind: MCFixupKind,
pub addend: i64,
pub section_index: u32,
}
pub trait MCObjectWriter {
fn write_header(&mut self, buf: &mut Vec<u8>);
fn write_section_header(
&mut self,
buf: &mut Vec<u8>,
section: &MCSection,
offset: u64,
size: u64,
);
fn write_section_data(&mut self, buf: &mut Vec<u8>, data: &[u8]);
fn record_relocation(&mut self, reloc: MCRelocationEntry);
fn write_relocations(&mut self, buf: &mut Vec<u8>);
fn write_symbol_table(&mut self, buf: &mut Vec<u8>, symbols: &[MCSymbol]);
fn finish(&mut self) -> Vec<u8>;
}
pub struct NullObjectWriter;
impl MCObjectWriter for NullObjectWriter {
fn write_header(&mut self, _buf: &mut Vec<u8>) {}
fn write_section_header(
&mut self,
_buf: &mut Vec<u8>,
_section: &MCSection,
_offset: u64,
_size: u64,
) {
}
fn write_section_data(&mut self, _buf: &mut Vec<u8>, _data: &[u8]) {}
fn record_relocation(&mut self, _reloc: MCRelocationEntry) {}
fn write_relocations(&mut self, _buf: &mut Vec<u8>) {}
fn write_symbol_table(&mut self, _buf: &mut Vec<u8>, _symbols: &[MCSymbol]) {}
fn finish(&mut self) -> Vec<u8> {
Vec::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MatchResult {
MatchSuccess,
MatchFailure,
MatchInvalidOperand,
MatchMissingFeature,
MatchUnsupported,
MatchNearMiss(String),
}
#[derive(Debug, Clone)]
pub struct ParsedOperand {
pub start_loc: usize,
pub end_loc: usize,
pub operand: MCOperand,
pub is_reg: bool,
pub is_mem: bool,
pub is_imm: bool,
pub is_expr: bool,
}
impl ParsedOperand {
pub fn from_reg(reg: u32) -> Self {
Self {
start_loc: 0,
end_loc: 0,
operand: MCOperand::Reg(reg),
is_reg: true,
is_mem: false,
is_imm: false,
is_expr: false,
}
}
pub fn from_imm(val: i64) -> Self {
Self {
start_loc: 0,
end_loc: 0,
operand: MCOperand::Imm(val),
is_reg: false,
is_mem: false,
is_imm: true,
is_expr: false,
}
}
pub fn from_expr(expr: MCExpr) -> Self {
Self {
start_loc: 0,
end_loc: 0,
operand: MCOperand::Expr(Box::new(expr)),
is_reg: false,
is_mem: false,
is_imm: false,
is_expr: true,
}
}
}
pub trait MCTargetAsmParser {
fn match_instruction(&self, mnemonic: &str, operands: &[MCOperand])
-> Result<u32, MatchResult>;
fn parse_register(&self, name: &str) -> Option<u32>;
fn parse_immediate(&self, text: &str) -> Option<i64>;
fn get_register_info(&self) -> &MCRegisterInfo;
fn get_instruction_info(&self) -> &MCInstrInfo;
fn get_subtarget_info(&self) -> &MCSubtargetInfo;
}
impl MCExpr {
pub fn is_absolute(&self) -> bool {
match self {
MCExpr::Constant(_) => true,
MCExpr::SymbolRef { .. } => false,
MCExpr::Binary { lhs, rhs, .. } => lhs.is_absolute() && rhs.is_absolute(),
MCExpr::Unary { expr, .. } => expr.is_absolute(),
}
}
pub fn kind_name(&self) -> &'static str {
match self {
MCExpr::Constant(_) => "Constant",
MCExpr::SymbolRef { .. } => "SymbolRef",
MCExpr::Binary { .. } => "Binary",
MCExpr::Unary { .. } => "Unary",
}
}
pub fn complexity(&self) -> usize {
match self {
MCExpr::Constant(_) => 1,
MCExpr::SymbolRef { .. } => 1,
MCExpr::Binary { lhs, rhs, .. } => 1 + lhs.complexity() + rhs.complexity(),
MCExpr::Unary { expr, .. } => 1 + expr.complexity(),
}
}
pub fn negate(&self) -> Option<MCExpr> {
match self {
MCExpr::Constant(v) => Some(MCExpr::Constant(-v)),
MCExpr::Unary {
op: MCUnaryOp::Neg,
expr,
} => Some((**expr).clone()),
MCExpr::Unary {
op: MCUnaryOp::Not,
expr,
} => Some(MCExpr::Unary {
op: MCUnaryOp::Not,
expr: Box::new(expr.negate().unwrap_or((**expr).clone())),
}),
_ => Some(MCExpr::Unary {
op: MCUnaryOp::Neg,
expr: Box::new(self.clone()),
}),
}
}
pub fn get_symbol_name(&self) -> Option<&str> {
match self {
MCExpr::SymbolRef { name, .. } => Some(name.as_str()),
MCExpr::Unary { expr, .. } => expr.get_symbol_name(),
MCExpr::Binary { lhs, rhs, .. } => {
lhs.get_symbol_name().or_else(|| rhs.get_symbol_name())
}
MCExpr::Constant(_) => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AsmToken {
Identifier(String),
Integer(i64),
Real(f64),
String(String),
Comma,
EndOfLine,
EndOfFile,
Hash,
Equal,
Colon,
LBracket,
RBracket,
LParen,
RParen,
Plus,
Minus,
Star,
Slash,
Percent,
Dollar,
At,
Exclaim,
Tilde,
Ampersand,
Pipe,
Error(String),
}
#[derive(Debug, Clone, PartialEq)]
pub enum AsmDirective {
Text,
Data,
Rodata,
Bss,
Global(String),
Weak(String),
Align(u32),
Size(String, u64),
Type(String, String),
Section(String),
Byte(Vec<i8>),
Short(Vec<i16>),
Long(Vec<i32>),
Quad(Vec<i64>),
Ascii(String),
Asciz(String),
Fill(u64, u8, u64),
Org(u64),
Equ(String, i64),
Set(String, i64),
End,
Unknown(String),
}
#[derive(Debug, Clone)]
pub enum AsmStatement {
Directive(AsmDirective),
Instruction {
mnemonic: String,
operands: Vec<String>,
},
Label(String),
Comment(String),
Empty,
}
#[derive(Debug, Clone)]
pub struct MCAsmParser {
pub target: String,
tokens: Vec<AsmToken>,
position: usize,
statements: Vec<AsmStatement>,
}
impl MCAsmParser {
pub fn new(target: &str) -> Self {
Self {
target: target.to_string(),
tokens: Vec::new(),
position: 0,
statements: Vec::new(),
}
}
pub fn lex_line(&mut self, line: &str) -> Vec<AsmToken> {
let mut tokens = Vec::new();
let chars: Vec<char> = line.chars().collect();
let mut i = 0;
while i < chars.len() {
let c = chars[i];
match c {
' ' | '\t' | '\r' => {
i += 1;
}
',' => {
tokens.push(AsmToken::Comma);
i += 1;
}
':' => {
tokens.push(AsmToken::Colon);
i += 1;
}
'[' => {
tokens.push(AsmToken::LBracket);
i += 1;
}
']' => {
tokens.push(AsmToken::RBracket);
i += 1;
}
'(' => {
tokens.push(AsmToken::LParen);
i += 1;
}
')' => {
tokens.push(AsmToken::RParen);
i += 1;
}
'+' => {
tokens.push(AsmToken::Plus);
i += 1;
}
'-' => {
if i + 1 < chars.len() && chars[i + 1].is_ascii_digit() {
i += 1;
let (val, next) = Self::parse_number(&chars, i - 1);
tokens.push(AsmToken::Integer(val));
i = next;
} else {
tokens.push(AsmToken::Minus);
i += 1;
}
}
'*' => {
tokens.push(AsmToken::Star);
i += 1;
}
'/' => {
if i + 1 < chars.len() && chars[i + 1] == '/' {
break;
}
tokens.push(AsmToken::Slash);
i += 1;
}
'#' => {
if i + 1 < chars.len() && chars[i + 1].is_ascii_digit() {
i += 1;
let (val, next) = Self::parse_number(&chars, i);
tokens.push(AsmToken::Integer(val));
i = next;
} else {
break;
}
}
';' => {
break;
}
'%' => {
tokens.push(AsmToken::Percent);
i += 1;
}
'$' => {
if i + 1 < chars.len() && chars[i + 1].is_ascii_digit() {
i += 1;
let (val, next) = Self::parse_number(&chars, i);
tokens.push(AsmToken::Integer(val));
i = next;
} else {
tokens.push(AsmToken::Dollar);
i += 1;
}
}
'@' => {
tokens.push(AsmToken::At);
i += 1;
}
'!' => {
tokens.push(AsmToken::Exclaim);
i += 1;
}
'~' => {
tokens.push(AsmToken::Tilde);
i += 1;
}
'&' => {
tokens.push(AsmToken::Ampersand);
i += 1;
}
'|' => {
tokens.push(AsmToken::Pipe);
i += 1;
}
'=' => {
tokens.push(AsmToken::Equal);
i += 1;
}
'0'..='9' => {
let (val, next) = Self::parse_number(&chars, i);
tokens.push(AsmToken::Integer(val));
i = next;
}
'"' => {
i += 1;
let mut s = String::new();
while i < chars.len() && chars[i] != '"' {
if chars[i] == '\\' && i + 1 < chars.len() {
i += 1;
match chars[i] {
'n' => s.push('\n'),
't' => s.push('\t'),
'r' => s.push('\r'),
'\\' => s.push('\\'),
'"' => s.push('"'),
'0' => s.push('\0'),
other => {
s.push('\\');
s.push(other);
}
}
} else {
s.push(chars[i]);
}
i += 1;
}
if i < chars.len() {
i += 1; }
tokens.push(AsmToken::String(s));
}
c if c.is_alphabetic() || c == '_' || c == '.' => {
let start = i;
while i < chars.len()
&& (chars[i].is_alphanumeric()
|| chars[i] == '_'
|| chars[i] == '.'
|| chars[i] == '$')
{
i += 1;
}
let ident: String = chars[start..i].iter().collect();
tokens.push(AsmToken::Identifier(ident));
}
_ => {
tokens.push(AsmToken::Error(format!("unexpected character '{}'", c)));
i += 1;
}
}
}
tokens
}
fn parse_number(chars: &[char], start: usize) -> (i64, usize) {
let mut i = start;
let negative = if i < chars.len() && chars[i] == '-' {
i += 1;
true
} else {
false
};
let base = if i + 1 < chars.len() && chars[i] == '0' {
match chars[i + 1] {
'x' | 'X' => {
i += 2;
16
}
'b' | 'B' => {
i += 2;
2
}
'o' | 'O' => {
i += 2;
8
}
_ => 10,
}
} else {
10
};
let mut val: i64 = 0;
while i < chars.len() {
let digit = match chars[i] {
'0'..='9' => chars[i] as u32 - '0' as u32,
'a'..='f' => chars[i] as u32 - 'a' as u32 + 10,
'A'..='F' => chars[i] as u32 - 'A' as u32 + 10,
'_' => {
i += 1;
continue;
}
_ => break,
};
if digit >= base {
break;
}
val = val * (base as i64) + digit as i64;
i += 1;
}
if negative {
val = -val;
}
(val, i)
}
pub fn parse_line(&mut self, line: &str) -> AsmStatement {
let tokens = self.lex_line(line);
if tokens.is_empty() {
return AsmStatement::Empty;
}
if tokens.len() >= 2
&& matches!(&tokens[0], AsmToken::Identifier(_))
&& matches!(&tokens[1], AsmToken::Colon)
{
if let AsmToken::Identifier(name) = &tokens[0] {
return AsmStatement::Label(name.clone());
}
}
if let AsmToken::Identifier(name) = &tokens[0] {
if name.starts_with('.') {
return self.parse_directive(name, &tokens[1..]);
}
}
if let AsmToken::Identifier(mnemonic) = &tokens[0] {
let mut operands = Vec::new();
let mut current = String::new();
for tok in &tokens[1..] {
match tok {
AsmToken::Comma => {
if !current.is_empty() {
operands.push(current.clone());
current.clear();
}
}
AsmToken::EndOfLine | AsmToken::EndOfFile => break,
_ => {
if !current.is_empty() {
current.push(' ');
}
current.push_str(&format!("{:?}", tok));
}
}
}
if !current.is_empty() {
operands.push(current);
}
return AsmStatement::Instruction {
mnemonic: mnemonic.clone(),
operands,
};
}
AsmStatement::Empty
}
fn parse_directive(&self, name: &str, tokens: &[AsmToken]) -> AsmStatement {
match name {
".text" => AsmStatement::Directive(AsmDirective::Text),
".data" => AsmStatement::Directive(AsmDirective::Data),
".rodata" => AsmStatement::Directive(AsmDirective::Rodata),
".bss" => AsmStatement::Directive(AsmDirective::Bss),
".end" => AsmStatement::Directive(AsmDirective::End),
_ => AsmStatement::Directive(AsmDirective::Unknown(name.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_operand_reg() {
let op = MCOperand::reg(5);
assert!(op.is_reg());
assert!(!op.is_imm());
assert!(!op.is_fpimm());
assert!(!op.is_expr());
assert_eq!(op.get_reg(), Some(5));
assert_eq!(op.get_imm(), None);
assert_eq!(op.kind_name(), "Reg");
}
#[test]
fn test_operand_imm() {
let op = MCOperand::imm(42);
assert!(op.is_imm());
assert!(!op.is_reg());
assert_eq!(op.get_imm(), Some(42));
assert_eq!(op.get_reg(), None);
}
#[test]
fn test_operand_fpimm() {
let op = MCOperand::fpimm(3.14);
assert!(op.is_fpimm());
assert!(!op.is_imm());
assert_eq!(op.get_fpimm(), Some(3.14));
}
#[test]
fn test_operand_expr() {
let expr = MCExpr::constant(100);
let op = MCOperand::Expr(Box::new(expr));
assert!(op.is_expr());
assert_eq!(op.get_expr().and_then(|e| e.evaluate()), Some(100));
}
#[test]
fn test_operand_inst() {
let inst = MCInst::new(0x90);
let op = MCOperand::Inst(Box::new(inst.clone()));
assert!(op.is_inst());
assert!(op.get_inst().is_some());
assert_eq!(op.get_inst().unwrap().opcode, 0x90);
}
#[test]
fn test_operand_display() {
assert_eq!(format!("{}", MCOperand::reg(1)), "reg(1)");
assert_eq!(format!("{}", MCOperand::imm(-5)), "-5");
assert_eq!(format!("{}", MCOperand::fpimm(2.5)), "2.5");
assert_eq!(
format!("{}", MCOperand::Expr(Box::new(MCExpr::constant(3)))),
"3"
);
}
#[test]
fn test_expr_evaluate_constant() {
let e = MCExpr::constant(42);
assert_eq!(e.evaluate(), Some(42));
assert!(e.is_constant());
}
#[test]
fn test_expr_evaluate_symbol() {
let e = MCExpr::symbol("main");
assert_eq!(e.evaluate(), None);
assert!(!e.is_constant());
}
#[test]
fn test_expr_evaluate_binary() {
let e = MCExpr::binary(MCBinaryOp::Add, MCExpr::constant(10), MCExpr::constant(20));
assert_eq!(e.evaluate(), Some(30));
assert!(e.is_constant());
let e2 = MCExpr::binary(MCBinaryOp::Sub, MCExpr::constant(100), MCExpr::constant(30));
assert_eq!(e2.evaluate(), Some(70));
}
#[test]
fn test_expr_evaluate_unary() {
let e = MCExpr::unary(MCUnaryOp::Neg, MCExpr::constant(5));
assert_eq!(e.evaluate(), Some(-5));
assert!(e.is_constant());
let e2 = MCExpr::unary(MCUnaryOp::Not, MCExpr::constant(0));
assert_eq!(e2.evaluate(), Some(!0));
}
#[test]
fn test_expr_collect_symbols() {
let e = MCExpr::binary(
MCBinaryOp::Add,
MCExpr::symbol("foo"),
MCExpr::symbol_with_offset("bar", 8),
);
let syms = e.collect_symbols();
assert_eq!(syms.len(), 2);
assert!(syms.contains(&"foo".to_string()));
assert!(syms.contains(&"bar".to_string()));
}
#[test]
fn test_expr_fold_constants() {
let e = MCExpr::binary(
MCBinaryOp::Add,
MCExpr::binary(MCBinaryOp::Mul, MCExpr::constant(3), MCExpr::constant(4)),
MCExpr::constant(2),
);
let folded = e.fold_constants();
assert_eq!(folded.evaluate(), Some(14));
assert!(folded.is_constant());
}
#[test]
fn test_expr_fold_unary() {
let e = MCExpr::unary(MCUnaryOp::Neg, MCExpr::constant(10));
let folded = e.fold_constants();
assert_eq!(folded.evaluate(), Some(-10));
}
#[test]
fn test_expr_display() {
assert_eq!(format!("{}", MCExpr::constant(5)), "5");
assert_eq!(format!("{}", MCExpr::symbol("main")), "main");
assert_eq!(
format!(
"{}",
MCExpr::binary(MCBinaryOp::Add, MCExpr::constant(1), MCExpr::constant(2))
),
"(1 + 2)"
);
}
#[test]
fn test_mcinst_create() {
let inst = MCInst::new(0x50);
assert_eq!(inst.opcode, 0x50);
assert_eq!(inst.num_operands(), 0);
assert!(!inst.is_terminator());
assert!(!inst.may_load());
}
#[test]
fn test_mcinst_with_flags() {
let inst = MCInst::with_flags(0x50, MCInstFlags::branch());
assert!(inst.is_branch());
assert!(inst.is_terminator());
}
#[test]
fn test_mcinst_operands() {
let mut inst = MCInst::new(0x90);
inst.add_operand(MCOperand::reg(7));
inst.add_operand(MCOperand::imm(42));
assert_eq!(inst.num_operands(), 2);
assert_eq!(inst.operand(0).and_then(|o| o.get_reg()), Some(7));
assert_eq!(inst.operand(1).and_then(|o| o.get_imm()), Some(42));
assert!(inst.operand(2).is_none());
}
#[test]
fn test_mcinst_flags_load() {
let flags = MCInstFlags::load();
assert!(flags.may_load);
assert!(!flags.may_store);
assert!(!flags.has_side_effects);
}
#[test]
fn test_mcinst_flags_store() {
let flags = MCInstFlags::store();
assert!(flags.may_store);
assert!(flags.has_side_effects);
}
#[test]
fn test_mcinst_flags_call() {
let flags = MCInstFlags::call();
assert!(flags.is_call);
assert!(flags.may_load);
assert!(flags.may_store);
}
#[test]
fn test_mcinst_flags_ret() {
let flags = MCInstFlags::ret();
assert!(flags.is_return);
assert!(flags.is_terminator);
}
#[test]
fn test_mcinst_flags_can_duplicate() {
assert!(MCInstFlags::alu().can_duplicate());
assert!(MCInstFlags::load().can_duplicate());
assert!(!MCInstFlags::store().can_duplicate());
assert!(!MCInstFlags::branch().can_duplicate());
}
#[test]
fn test_mcinst_flags_control_flow() {
assert!(!MCInstFlags::alu().is_control_flow());
assert!(MCInstFlags::branch().is_control_flow());
assert!(MCInstFlags::call().is_control_flow());
assert!(MCInstFlags::ret().is_control_flow());
}
#[test]
fn test_mcinst_display() {
let mut inst = MCInst::new(0x90);
inst.add_operand(MCOperand::reg(0));
let s = format!("{}", inst);
assert!(s.contains("0x90"));
assert!(s.contains("reg:0"));
}
#[test]
fn test_section_text() {
let sec = MCSection::text();
assert_eq!(sec.name, ".text");
assert_eq!(sec.section_type, MCSectionType::Text);
assert!(sec.is_text());
assert!(sec.is_readonly());
assert!(!sec.is_writable());
}
#[test]
fn test_section_data() {
let sec = MCSection::data();
assert!(sec.is_writable());
assert!(!sec.is_readonly());
}
#[test]
fn test_section_builder() {
let sec = MCSection::new("custom", MCSectionType::Custom)
.with_alignment(64)
.with_flags(0x1);
assert_eq!(sec.alignment, 64);
assert_eq!(sec.flags, 0x1);
}
#[test]
fn test_symbol_create() {
let sym = MCSymbol::new("main");
assert_eq!(sym.name, "main");
assert!(sym.is_undefined);
assert!(!sym.is_global);
}
#[test]
fn test_symbol_global() {
let sym = MCSymbol::new("foo").set_global().set_function();
assert!(sym.is_global);
assert!(sym.is_function);
assert_eq!(sym.binding_str(), "GLOBAL");
assert_eq!(sym.type_str(), "FUNC");
}
#[test]
fn test_symbol_temporary() {
let sym = MCSymbol::temporary(42);
assert!(sym.is_temporary);
assert_eq!(sym.name, ".Ltmp42");
assert!(!sym.is_global);
}
#[test]
fn test_label_create() {
let lbl = MCLabel::new("L0", 1, 0x100);
assert_eq!(lbl.name, "L0");
assert_eq!(lbl.section_index, 1);
assert_eq!(lbl.offset, 0x100);
}
#[test]
fn test_fragment_data_size() {
let frag = MCFragment::Data(vec![0x90, 0x90, 0x90]);
assert_eq!(frag.size(), Some(3));
assert!(!frag.is_instruction());
assert!(!frag.is_directive());
}
#[test]
fn test_fragment_instruction() {
let inst = MCInst::new(0x50);
let frag = MCFragment::Instruction(inst);
assert!(frag.is_instruction());
assert!(!frag.is_directive());
}
#[test]
fn test_fragment_directives() {
assert!(MCFragment::Align(16, 0).is_directive());
assert!(MCFragment::Fill(10, 0x90).is_directive());
assert!(MCFragment::Label(MCLabel::new("L", 0, 0)).is_directive());
assert!(MCFragment::SymbolDef("foo".into()).is_directive());
}
#[test]
fn test_context_create() {
let ctx = MCContext::new("x86_64-unknown-linux-gnu");
assert_eq!(ctx.target_triple, "x86_64-unknown-linux-gnu");
assert_eq!(ctx.sections.len(), 0);
assert_eq!(ctx.symbols.len(), 0);
assert_eq!(ctx.current_section, None);
}
#[test]
fn test_context_add_section() {
let mut ctx = MCContext::new("x86_64");
let idx = ctx.add_section(".text", MCSectionType::Text);
assert_eq!(idx, 0);
assert_eq!(ctx.sections.len(), 1);
assert_eq!(ctx.fragments.len(), 1);
assert_eq!(ctx.section_offsets.len(), 1);
}
#[test]
fn test_context_create_symbol() {
let mut ctx = MCContext::new("x86_64");
let sym = ctx.create_symbol("main");
assert_eq!(sym.name, "main");
assert_eq!(ctx.symbols.len(), 1);
let sym2 = ctx.create_symbol("main");
assert_eq!(ctx.symbols.len(), 1);
assert_eq!(sym2.name, "main");
}
#[test]
fn test_context_temp_symbol() {
let mut ctx = MCContext::new("x86_64");
let sym1 = ctx.create_temp_symbol();
let sym2 = ctx.create_temp_symbol();
assert_eq!(sym1.name, ".Ltmp0");
assert_eq!(sym2.name, ".Ltmp1");
assert!(sym1.is_temporary);
}
#[test]
fn test_context_emit_data() {
let mut ctx = MCContext::new("x86_64");
ctx.add_section(".data", MCSectionType::Data);
ctx.switch_section(0);
ctx.emit_data(vec![0x01, 0x02, 0x03]);
assert_eq!(ctx.section_offsets[0], 3);
assert_eq!(ctx.fragments[0].len(), 1);
}
#[test]
fn test_context_emit_alignment() {
let mut ctx = MCContext::new("x86_64");
ctx.add_section(".text", MCSectionType::Text);
ctx.switch_section(0);
ctx.emit_alignment(16);
assert_eq!(ctx.fragments[0].len(), 1);
assert!(matches!(ctx.fragments[0][0], MCFragment::Align(16, _)));
}
#[test]
fn test_context_emit_label() {
let mut ctx = MCContext::new("x86_64");
ctx.add_section(".text", MCSectionType::Text);
ctx.switch_section(0);
ctx.emit_label("test_label");
assert_eq!(ctx.fragments[0].len(), 1);
if let MCFragment::Label(lbl) = &ctx.fragments[0][0] {
assert_eq!(lbl.name, "test_label");
assert_eq!(lbl.offset, 0);
} else {
panic!("Expected Label fragment");
}
}
#[test]
fn test_context_flatten_data() {
let mut ctx = MCContext::new("x86_64");
ctx.add_section(".data", MCSectionType::Data);
ctx.switch_section(0);
ctx.emit_data(vec![0xAA, 0xBB, 0xCC]);
let flat = ctx.flatten();
assert!(flat.len() >= 3);
assert_eq!(&flat[0..3], &[0xAA, 0xBB, 0xCC]);
}
#[test]
fn test_context_flatten_with_fill() {
let mut ctx = MCContext::new("x86_64");
ctx.add_section(".data", MCSectionType::Data);
ctx.switch_section(0);
ctx.emit_fill(4, 0xFF);
let flat = ctx.flatten();
assert!(flat.len() >= 4);
assert_eq!(&flat[0..4], &[0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_context_fragment_count() {
let mut ctx = MCContext::new("x86_64");
ctx.add_section(".text", MCSectionType::Text);
ctx.add_section(".data", MCSectionType::Data);
ctx.switch_section(0);
ctx.emit_alignment(16);
ctx.emit_label("entry");
ctx.switch_section(1);
ctx.emit_data(vec![0x00]);
assert_eq!(ctx.fragment_count(), 3);
}
#[test]
fn test_context_clear() {
let mut ctx = MCContext::new("x86_64");
ctx.add_section(".text", MCSectionType::Text);
ctx.create_symbol("foo");
ctx.clear();
assert_eq!(ctx.sections.len(), 0);
assert_eq!(ctx.symbols.len(), 0);
assert_eq!(ctx.current_section, None);
assert_eq!(ctx.next_temp_id, 0);
}
}