use crate::mc_assembler::*;
use crate::mc_inst::*;
use crate::x86::x86_full_instr_info::{
EncodingForm, InstrEncodingInfo, X86FullInstrInfo, X86FullOpcode,
};
use crate::x86::x86_instr_info::{
OperandType, X86InstrDesc, X86InstrInfo, X86MemOperand, X86Opcode, X86Operand, X86SchedInfo,
};
use crate::x86::x86_register_info::{
RegClass, X86Reg, X86RegisterInfo, AH, AL, ATT_NAMES, AX, BH, BL, BND0, BND1, BND2, BND3, BP,
BPL, BX, CH, CL, CR0, CR1, CR2, CR3, CR4, CR5, CR6, CR7, CR8, CS, CX, DH, DI, DIL, DL, DR0,
DR1, DR2, DR3, DR4, DR5, DR6, DR7, DS, DX, EAX, EBP, EBX, ECX, EDI, EDX, EFLAGS, EIP, ES, ESI,
ESP, FS, GS, INTEL_NAMES, K0, K1, K2, K3, K4, K5, K6, K7, MM0, MM1, MM2, MM3, MM4, MM5, MM6,
MM7, R10, R10B, R10D, R10W, R11, R11B, R11D, R11W, R12, R12B, R12D, R12W, R13, R13B, R13D,
R13W, R14, R14B, R14D, R14W, R15, R15B, R15D, R15W, R8, R8B, R8D, R8W, R9, R9B, R9D, R9W, RAX,
RBP, RBX, RCX, RDI, RDX, RFLAGS, RIP, RSI, RSP, SI, SIL, SP, SPL, SS, ST0, ST1, ST2, ST3, ST4,
ST5, ST6, ST7, X86_64_REG_COUNT, XMM0, XMM1, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM16,
XMM17, XMM18, XMM19, XMM2, XMM20, XMM21, XMM22, XMM23, XMM24, XMM25, XMM26, XMM27, XMM28,
XMM29, XMM3, XMM30, XMM31, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9, YMM0, YMM1, YMM10, YMM11, YMM12,
YMM13, YMM14, YMM15, YMM16, YMM17, YMM18, YMM19, YMM2, YMM20, YMM21, YMM22, YMM23, YMM24,
YMM25, YMM26, YMM27, YMM28, YMM29, YMM3, YMM30, YMM31, YMM4, YMM5, YMM6, YMM7, YMM8, YMM9,
ZMM0, ZMM1, ZMM10, ZMM11, ZMM12, ZMM13, ZMM14, ZMM15, ZMM16, ZMM17, ZMM18, ZMM19, ZMM2, ZMM20,
ZMM21, ZMM22, ZMM23, ZMM24, ZMM25, ZMM26, ZMM27, ZMM28, ZMM29, ZMM3, ZMM30, ZMM31, ZMM4, ZMM5,
ZMM6, ZMM7, ZMM8, ZMM9,
};
use crate::x86::*;
#[derive(Debug, Clone, PartialEq)]
pub enum X86AsmToken {
Identifier(String),
Integer(i64),
Real(f64),
StringLiteral(String),
CharLiteral(char),
Comma,
EndOfLine,
EndOfFile,
Hash,
Equal,
Colon,
LBracket,
RBracket,
LParen,
RParen,
Plus,
Minus,
Star,
Slash,
Percent,
Dollar,
At,
Exclaim,
Tilde,
Ampersand,
Pipe,
Caret,
Dot,
LAngle,
RAngle,
Error(String),
}
impl X86AsmToken {
pub fn is_identifier(&self) -> bool {
matches!(self, X86AsmToken::Identifier(_))
}
pub fn is_integer(&self) -> bool {
matches!(self, X86AsmToken::Integer(_))
}
pub fn is_end(&self) -> bool {
matches!(self, X86AsmToken::EndOfLine | X86AsmToken::EndOfFile)
}
pub fn kind_name(&self) -> &'static str {
match self {
X86AsmToken::Identifier(_) => "identifier",
X86AsmToken::Integer(_) => "integer",
X86AsmToken::Real(_) => "real",
X86AsmToken::StringLiteral(_) => "string",
X86AsmToken::CharLiteral(_) => "char",
X86AsmToken::Comma => "comma",
X86AsmToken::EndOfLine => "end-of-line",
X86AsmToken::EndOfFile => "end-of-file",
X86AsmToken::Hash => "hash",
X86AsmToken::Equal => "equal",
X86AsmToken::Colon => "colon",
X86AsmToken::LBracket => "lbracket",
X86AsmToken::RBracket => "rbracket",
X86AsmToken::LParen => "lparen",
X86AsmToken::RParen => "rparen",
X86AsmToken::Plus => "plus",
X86AsmToken::Minus => "minus",
X86AsmToken::Star => "star",
X86AsmToken::Slash => "slash",
X86AsmToken::Percent => "percent",
X86AsmToken::Dollar => "dollar",
X86AsmToken::At => "at",
X86AsmToken::Exclaim => "exclaim",
X86AsmToken::Tilde => "tilde",
X86AsmToken::Ampersand => "ampersand",
X86AsmToken::Pipe => "pipe",
X86AsmToken::Caret => "caret",
X86AsmToken::Dot => "dot",
X86AsmToken::LAngle => "langle",
X86AsmToken::RAngle => "rangle",
X86AsmToken::Error(_) => "error",
}
}
}
impl std::fmt::Display for X86AsmToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
X86AsmToken::Identifier(s) => write!(f, "{}", s),
X86AsmToken::Integer(n) => write!(f, "{}", n),
X86AsmToken::Real(n) => write!(f, "{}", n),
X86AsmToken::StringLiteral(s) => write!(f, "\"{}\"", s),
X86AsmToken::CharLiteral(c) => write!(f, "'{}'", c),
X86AsmToken::Comma => write!(f, ","),
X86AsmToken::EndOfLine => write!(f, "\\n"),
X86AsmToken::EndOfFile => write!(f, "<EOF>"),
X86AsmToken::Hash => write!(f, "#"),
X86AsmToken::Equal => write!(f, "="),
X86AsmToken::Colon => write!(f, ":"),
X86AsmToken::LBracket => write!(f, "["),
X86AsmToken::RBracket => write!(f, "]"),
X86AsmToken::LParen => write!(f, "("),
X86AsmToken::RParen => write!(f, ")"),
X86AsmToken::Plus => write!(f, "+"),
X86AsmToken::Minus => write!(f, "-"),
X86AsmToken::Star => write!(f, "*"),
X86AsmToken::Slash => write!(f, "/"),
X86AsmToken::Percent => write!(f, "%"),
X86AsmToken::Dollar => write!(f, "$"),
X86AsmToken::At => write!(f, "@"),
X86AsmToken::Exclaim => write!(f, "!"),
X86AsmToken::Tilde => write!(f, "~"),
X86AsmToken::Ampersand => write!(f, "&"),
X86AsmToken::Pipe => write!(f, "|"),
X86AsmToken::Caret => write!(f, "^"),
X86AsmToken::Dot => write!(f, "."),
X86AsmToken::LAngle => write!(f, "<"),
X86AsmToken::RAngle => write!(f, ">"),
X86AsmToken::Error(s) => write!(f, "<error: {}>", s),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AsmSyntaxMode {
ATT,
Intel,
}
impl Default for AsmSyntaxMode {
fn default() -> Self {
AsmSyntaxMode::ATT
}
}
#[derive(Debug, Clone)]
pub enum AsmOperand {
Reg(X86Reg),
Imm(i64),
Mem(MemRef),
Label(String),
Expr(MCExpr),
RipRel(String, i64),
KReg(u8),
RoundingCtrl(RoundingMode),
}
#[derive(Debug, Clone, Default)]
pub struct MemRef {
pub segment: Option<X86Reg>,
pub base: Option<X86Reg>,
pub index: Option<X86Reg>,
pub scale: u8,
pub displacement: i64,
pub symbol: Option<String>,
pub is_rip_relative: bool,
pub ptr_qualifier: Option<MemPtrQualifier>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemPtrQualifier {
Byte,
Word,
DWord,
QWord,
TByte,
XmmWord,
YmmWord,
ZmmWord,
OWord,
}
impl MemPtrQualifier {
pub fn from_str(s: &str) -> Option<Self> {
match s.to_uppercase().as_str() {
"BYTE" => Some(MemPtrQualifier::Byte),
"WORD" => Some(MemPtrQualifier::Word),
"DWORD" => Some(MemPtrQualifier::DWord),
"QWORD" => Some(MemPtrQualifier::QWord),
"TBYTE" => Some(MemPtrQualifier::TByte),
"XMMWORD" => Some(MemPtrQualifier::XmmWord),
"YMMWORD" => Some(MemPtrQualifier::YmmWord),
"ZMMWORD" => Some(MemPtrQualifier::ZmmWord),
"OWORD" => Some(MemPtrQualifier::OWord),
_ => None,
}
}
pub fn size_bytes(&self) -> u32 {
match self {
MemPtrQualifier::Byte => 1,
MemPtrQualifier::Word => 2,
MemPtrQualifier::DWord => 4,
MemPtrQualifier::QWord => 8,
MemPtrQualifier::TByte => 10,
MemPtrQualifier::XmmWord => 16,
MemPtrQualifier::YmmWord => 32,
MemPtrQualifier::ZmmWord => 64,
MemPtrQualifier::OWord => 16,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RoundingMode {
RNE, RD, RU, RZ, SAE, }
impl RoundingMode {
pub fn from_modifier(s: &str) -> Option<Self> {
match s {
"{rn-sae}" | "rn-sae" => Some(RoundingMode::RNE),
"{rd-sae}" | "rd-sae" => Some(RoundingMode::RD),
"{ru-sae}" | "ru-sae" => Some(RoundingMode::RU),
"{rz-sae}" | "rz-sae" => Some(RoundingMode::RZ),
"{sae}" | "sae" => Some(RoundingMode::SAE),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct ParsedInstruction {
pub mnemonic: String,
pub opcode: X86FullOpcode,
pub operands: Vec<AsmOperand>,
pub size_suffix: Option<SizeSuffix>,
pub encoding_form: EncodingForm,
pub is_branch: bool,
pub is_call: bool,
pub loc: SourceLocation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizeSuffix {
Byte,
Word,
Long,
Quad,
Single,
Double,
PackedSingle,
PackedDouble,
ScalarSingle,
ScalarDouble,
TByte,
}
impl SizeSuffix {
pub fn from_att_suffix(s: &str) -> Option<Self> {
match s {
"b" => Some(SizeSuffix::Byte),
"w" => Some(SizeSuffix::Word),
"l" => Some(SizeSuffix::Long),
"q" => Some(SizeSuffix::Quad),
"s" => Some(SizeSuffix::Single),
"d" => Some(SizeSuffix::Double),
"ps" => Some(SizeSuffix::PackedSingle),
"pd" => Some(SizeSuffix::PackedDouble),
"ss" => Some(SizeSuffix::ScalarSingle),
"sd" => Some(SizeSuffix::ScalarDouble),
"t" => Some(SizeSuffix::TByte),
_ => None,
}
}
pub fn byte_size(&self) -> u32 {
match self {
SizeSuffix::Byte => 1,
SizeSuffix::Word => 2,
SizeSuffix::Long => 4,
SizeSuffix::Quad => 8,
SizeSuffix::Single => 4,
SizeSuffix::Double => 8,
SizeSuffix::PackedSingle => 16,
SizeSuffix::PackedDouble => 16,
SizeSuffix::ScalarSingle => 4,
SizeSuffix::ScalarDouble => 8,
SizeSuffix::TByte => 10,
}
}
}
#[derive(Debug, Clone)]
pub enum AsmDirective {
Section {
name: String,
flags: Vec<String>,
},
Text,
Data,
Bss,
Rodata,
Byte(Vec<i64>),
Short(Vec<i64>),
Long(Vec<i64>),
Quad(Vec<i64>),
Octa(Vec<i64>),
Ascii(String),
Asciz(String),
String(String),
Fill {
count: i64,
value: i64,
size: Option<i64>,
},
Space(i64),
Skip(i64),
Align {
alignment: u32,
fill: Option<u8>,
max: Option<u32>,
},
P2Align {
alignment: u32,
fill: Option<u8>,
max: Option<u32>,
},
BAlign {
alignment: u32,
fill: Option<u8>,
},
Org(i64),
Globl(String),
Global(String),
Local(String),
Weak(String),
Hidden(String),
Protected(String),
Type {
name: String,
typ: String,
},
Size {
name: String,
size: String,
},
Comm {
name: String,
size: i64,
alignment: Option<u32>,
},
LComm {
name: String,
size: i64,
alignment: Option<u32>,
},
Zero(i64),
Equ {
name: String,
value: String,
},
Set {
name: String,
value: String,
},
Macro {
name: String,
body: Vec<String>,
},
EndM,
RepT {
count: i64,
body: Vec<String>,
},
EndR,
IrP {
param: String,
values: Vec<String>,
body: Vec<String>,
},
IrPC {
param: String,
chars: String,
body: Vec<String>,
},
If(String),
Else,
EndIf,
IfDef(String),
IfNDef(String),
CfiStartProc,
CfiEndProc,
CfiDefCfa {
reg: String,
offset: i64,
},
CfiOffset {
reg: String,
offset: i64,
},
File {
name: String,
},
Loc {
file: u32,
line: u32,
column: Option<u32>,
},
Line(u32),
Ident(String),
End,
}
#[derive(Debug, Clone)]
pub enum AsmLine {
Directive(AsmDirective),
Instruction {
mnemonic: String,
operands: Vec<String>,
suffixes: Vec<String>,
},
Label(String),
Comment(String),
Empty,
}
pub struct X86AsmLexer {
input: Vec<char>,
position: usize,
line: u32,
column: u32,
}
impl X86AsmLexer {
pub fn new(input: &str) -> Self {
X86AsmLexer {
input: input.chars().collect(),
position: 0,
line: 1,
column: 1,
}
}
fn current(&self) -> Option<char> {
self.input.get(self.position).copied()
}
fn peek(&self) -> Option<char> {
self.input.get(self.position).copied()
}
fn advance(&mut self) -> Option<char> {
let c = self.input.get(self.position).copied();
self.position += 1;
if let Some(ch) = c {
if ch == '\n' {
self.line += 1;
self.column = 1;
} else {
self.column += 1;
}
}
c
}
fn skip_whitespace(&mut self) {
while let Some(c) = self.current() {
if c.is_whitespace() && c != '\n' {
self.advance();
} else {
break;
}
}
}
fn read_identifier(&mut self) -> String {
let mut s = String::new();
while let Some(c) = self.current() {
if c.is_alphanumeric() || c == '_' || c == '.' || c == '$' || c == '@' {
s.push(c);
self.advance();
} else {
break;
}
}
s
}
fn read_number(&mut self, negative: bool) -> X86AsmToken {
let mut s = String::new();
if negative {
s.push('-');
}
let mut radix = 10;
let start_pos = self.position;
if self.current() == Some('0') {
self.advance();
match self.current() {
Some('x') | Some('X') => {
self.advance();
radix = 16;
s.push_str("0x");
}
Some('b') | Some('B') => {
self.advance();
radix = 2;
s.push_str("0b");
}
Some('o') | Some('O') => {
self.advance();
radix = 8;
s.push_str("0o");
}
_ => {
if self.current().map_or(false, |c| c.is_ascii_digit()) {
radix = 8;
}
s.push('0');
self.position = start_pos + 1;
s.clear();
s.push('0');
}
}
} else if self.current() == Some('$') {
self.advance();
radix = 16;
} else if self.current() == Some('&') {
self.advance();
radix = 16;
}
while let Some(c) = self.current() {
match radix {
16 => {
if c.is_ascii_hexdigit() {
s.push(c);
self.advance();
} else {
break;
}
}
2 => {
if c == '0' || c == '1' {
s.push(c);
self.advance();
} else {
break;
}
}
8 => {
if c >= '0' && c <= '7' {
s.push(c);
self.advance();
} else {
break;
}
}
_ => {
if c.is_ascii_digit() {
s.push(c);
self.advance();
} else {
break;
}
}
}
}
let num_str = if s.starts_with("0x") || s.starts_with("0X") {
&s[2..]
} else if s.starts_with("0b") || s.starts_with("0B") {
&s[2..]
} else if s.starts_with("0o") || s.starts_with("0O") {
&s[2..]
} else {
&s
};
if num_str.is_empty() {
return X86AsmToken::Integer(0);
}
match i64::from_str_radix(num_str, radix as u32) {
Ok(v) => X86AsmToken::Integer(v),
Err(_) => X86AsmToken::Error(format!("invalid number: {}", s)),
}
}
fn read_string(&mut self, quote: char) -> X86AsmToken {
let mut s = String::new();
self.advance(); loop {
match self.current() {
None => return X86AsmToken::Error("unterminated string".into()),
Some(c) if c == quote => {
self.advance(); break;
}
Some('\\') => {
self.advance();
match self.current() {
Some('n') => {
s.push('\n');
self.advance();
}
Some('t') => {
s.push('\t');
self.advance();
}
Some('r') => {
s.push('\r');
self.advance();
}
Some('\\') => {
s.push('\\');
self.advance();
}
Some('"') => {
s.push('"');
self.advance();
}
Some('0') => {
s.push('\0');
self.advance();
}
Some(c) => {
s.push(c);
self.advance();
}
None => return X86AsmToken::Error("unterminated escape".into()),
}
}
Some(c) => {
s.push(c);
self.advance();
}
}
}
if quote == '\'' {
if s.chars().count() == 1 {
X86AsmToken::CharLiteral(s.chars().next().unwrap())
} else {
X86AsmToken::StringLiteral(s)
}
} else {
X86AsmToken::StringLiteral(s)
}
}
fn read_line_comment(&mut self) {
while let Some(c) = self.current() {
if c == '\n' {
break;
}
self.advance();
}
}
fn read_block_comment(&mut self) {
self.advance(); loop {
match self.current() {
None => return,
Some('*') => {
self.advance();
if self.current() == Some('/') {
self.advance();
return;
}
}
Some(_) => {
self.advance();
}
}
}
}
pub fn next_token(&mut self) -> X86AsmToken {
self.skip_whitespace();
let c = match self.current() {
None => return X86AsmToken::EndOfFile,
Some(c) => c,
};
if c == '#' {
self.read_line_comment();
return X86AsmToken::EndOfLine;
}
if c == '/' {
self.advance();
match self.current() {
Some('/') => {
self.read_line_comment();
return X86AsmToken::EndOfLine;
}
Some('*') => {
self.read_block_comment();
return self.next_token();
}
_ => return X86AsmToken::Slash,
}
}
if c == '\n' {
self.advance();
return X86AsmToken::EndOfLine;
}
if c == '"' || c == '\'' {
return self.read_string(c);
}
if c == '-' || c == '+' {
let sign = c == '-';
self.advance();
if let Some(next) = self.current() {
if next.is_ascii_digit() {
return self.read_number(sign);
}
return if sign {
X86AsmToken::Minus
} else {
X86AsmToken::Plus
};
}
return if sign {
X86AsmToken::Minus
} else {
X86AsmToken::Plus
};
}
if c.is_ascii_digit() {
return self.read_number(false);
}
if c == '$' {
self.advance();
if let Some(next) = self.current() {
if next.is_ascii_hexdigit() {
let mut digits = String::new();
while let Some(ch) = self.current() {
if ch.is_ascii_hexdigit() {
digits.push(ch);
self.advance();
} else {
break;
}
}
match i64::from_str_radix(&digits, 16) {
Ok(v) => return X86AsmToken::Integer(v),
Err(_) => return X86AsmToken::Error(format!("invalid hex: ${}", digits)),
}
}
}
return X86AsmToken::Dollar;
}
if c.is_alphabetic() || c == '_' || c == '.' {
return X86AsmToken::Identifier(self.read_identifier());
}
if c == '%' {
self.advance();
return X86AsmToken::Percent;
}
self.advance();
match c {
',' => X86AsmToken::Comma,
':' => X86AsmToken::Colon,
'[' => X86AsmToken::LBracket,
']' => X86AsmToken::RBracket,
'(' => X86AsmToken::LParen,
')' => X86AsmToken::RParen,
'*' => X86AsmToken::Star,
'@' => X86AsmToken::At,
'!' => X86AsmToken::Exclaim,
'~' => X86AsmToken::Tilde,
'&' => X86AsmToken::Ampersand,
'|' => X86AsmToken::Pipe,
'^' => X86AsmToken::Caret,
'<' => X86AsmToken::LAngle,
'>' => X86AsmToken::RAngle,
'=' => X86AsmToken::Equal,
_ => X86AsmToken::Error(format!("unexpected character: '{}'", c)),
}
}
pub fn lex_all(&mut self) -> Vec<(X86AsmToken, u32, u32)> {
let mut tokens = Vec::new();
loop {
let line = self.line;
let col = self.column;
let token = self.next_token();
let is_eof = matches!(token, X86AsmToken::EndOfFile);
tokens.push((token, line, col));
if is_eof {
break;
}
}
tokens
}
}
pub struct X86MnemonicParser {
full_info: X86FullInstrInfo,
instr_info: X86InstrInfo,
syntax: AsmSyntaxMode,
}
impl X86MnemonicParser {
pub fn new(syntax: AsmSyntaxMode) -> Self {
X86MnemonicParser {
full_info: X86FullInstrInfo::new(),
instr_info: X86InstrInfo::new(),
syntax,
}
}
pub fn parse_att(&self, raw: &str) -> Option<(String, Option<SizeSuffix>, X86FullOpcode)> {
let mut base = raw.to_lowercase();
let mut suffix = None;
let known_suffixes = [
("b", SizeSuffix::Byte),
("w", SizeSuffix::Word),
("l", SizeSuffix::Long),
("q", SizeSuffix::Quad),
("s", SizeSuffix::Single),
("t", SizeSuffix::TByte),
("ps", SizeSuffix::PackedSingle),
("pd", SizeSuffix::PackedDouble),
("ss", SizeSuffix::ScalarSingle),
("sd", SizeSuffix::ScalarDouble),
];
for (sfx_str, sfx_val) in known_suffixes.iter() {
if base.ends_with(sfx_str) && base.len() > sfx_str.len() {
let before = &base[..base.len() - sfx_str.len()];
if !before.ends_with("p") || *sfx_str == "s" {
let last_char = before.chars().last().unwrap_or(' ');
if last_char.is_alphabetic() || last_char == 'd' {
base = before.to_string();
suffix = Some(*sfx_val);
break;
}
}
}
}
if suffix.is_none() && base.ends_with('d') && base.len() > 1 {
if let Some(without_d) = base.strip_suffix('d') {
if !without_d.ends_with("an")
&& !without_d.ends_with("or")
&& !without_d.ends_with("x")
{
base = without_d.to_string();
suffix = Some(SizeSuffix::Double);
}
}
}
if let Some(opcode) = self.full_info.find_by_mnemonic(&base) {
return Some((base, suffix, opcode));
}
let alias = match base.as_str() {
"as" | "aas" => Some("aas"),
"cbw" => Some("cbw"),
"cwd" => Some("cwd"),
"cdq" => Some("cdq"),
"cqo" => Some("cqo"),
"cwtl" => Some("cwde"),
"cltq" => Some("cdqe"),
"cltd" => Some("cdq"),
"cbtw" => Some("cbw"),
"cwtd" => Some("cwd"),
"cltd" => Some("cdq"),
"cqto" => Some("cqo"),
"setnle" => Some("setg"),
"setnge" => Some("setl"),
"setnae" => Some("setb"),
"setnb" => Some("setae"),
"setnbe" => Some("seta"),
"setna" => Some("setbe"),
"setnz" => Some("setne"),
"setz" => Some("sete"),
"setpe" => Some("setp"),
"setpo" => Some("setnp"),
"cmovnle" => Some("cmovg"),
"cmovnge" => Some("cmovl"),
"cmovnae" => Some("cmovb"),
"cmovnb" => Some("cmovae"),
"cmovnbe" => Some("cmova"),
"cmovna" => Some("cmovbe"),
"cmovnz" => Some("cmovne"),
"cmovz" => Some("cmove"),
"cmovpe" => Some("cmovp"),
"cmovpo" => Some("cmovnp"),
"jnle" => Some("jg"),
"jnge" => Some("jl"),
"jnae" => Some("jb"),
"jnb" => Some("jae"),
"jnbe" => Some("ja"),
"jna" => Some("jbe"),
"jnz" => Some("jne"),
"jz" => Some("je"),
"jpe" => Some("jp"),
"jpo" => Some("jnp"),
_ => None,
};
if let Some(alias_name) = alias {
if let Some(opcode) = self.full_info.find_by_mnemonic(alias_name) {
return Some((alias_name.to_string(), suffix, opcode));
}
}
if let Some(_opcode) = self.instr_info.find_by_mnemonic(&base) {
return Some((base, suffix, X86FullOpcode::MOVrr));
}
None
}
pub fn parse_intel(&self, raw: &str) -> Option<(String, X86FullOpcode)> {
let base = raw.to_lowercase();
if let Some(opcode) = self.full_info.find_by_mnemonic(&base) {
return Some((base, opcode));
}
None
}
pub fn get_encoding_info(&self, opcode: &X86FullOpcode) -> Option<InstrEncodingInfo> {
Some(InstrEncodingInfo {
opcode: *opcode,
mnemonic: opcode.mnemonic(),
intel_mnemonic: opcode.intel_mnemonic(),
num_operands: opcode.num_operands(),
encoding_form: opcode.encoding_form(),
base_opcode: opcode.base_opcode(),
opcode_map: opcode.opcode_map(),
mandatory_prefix: opcode.mandatory_prefix(),
requires_modrm: opcode.requires_modrm(),
modrm_extension: opcode.modrm_extension(),
operand_types: &[],
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
is_compare: false,
is_move_immediate: false,
is_convert: false,
has_side_effects: false,
may_load: false,
may_store: false,
is_commutative: false,
is_three_address: false,
})
}
pub fn all_mnemonics(&self) -> Vec<String> {
ALL_MNEMONICS.iter().map(|s| s.to_string()).collect()
}
pub fn mnemonic_count(&self) -> usize {
ALL_MNEMONICS.len()
}
}
pub const ALL_MNEMONICS: &[&str] = &[
"aaa",
"aad",
"aam",
"aas",
"adc",
"adcx",
"add",
"addpd",
"addps",
"addsd",
"addss",
"addsubpd",
"addsubps",
"adox",
"aesdec",
"aesdec128kl",
"aesdec256kl",
"aesdeclast",
"aesdecwide128kl",
"aesdecwide256kl",
"aesenc",
"aesenc128kl",
"aesenc256kl",
"aesenclast",
"aesencwide128kl",
"aesencwide256kl",
"aesimc",
"aeskeygenassist",
"and",
"andn",
"andnpd",
"andnps",
"andpd",
"andps",
"arpl",
"bextr",
"blcfill",
"blci",
"blcic",
"blcmsk",
"blcs",
"blendpd",
"blendps",
"blendvpd",
"blendvps",
"blsfill",
"blsi",
"blsic",
"blsmsk",
"blsr",
"bndcl",
"bndcn",
"bndcu",
"bndldx",
"bndmk",
"bndmov",
"bndstx",
"bound",
"bsf",
"bsr",
"bswap",
"bt",
"btc",
"btr",
"bts",
"bzhi",
"call",
"cbw",
"cdq",
"cdqe",
"clac",
"clc",
"cld",
"cldemote",
"clflush",
"clflushopt",
"cli",
"clrssbsy",
"clts",
"clui",
"clwb",
"cmc",
"cmovae",
"cmova",
"cmovb",
"cmovbe",
"cmovc",
"cmove",
"cmovg",
"cmovge",
"cmovl",
"cmovle",
"cmovna",
"cmovnae",
"cmovnb",
"cmovnbe",
"cmovnc",
"cmovne",
"cmovng",
"cmovnge",
"cmovnl",
"cmovnle",
"cmovno",
"cmovnp",
"cmovns",
"cmovnz",
"cmovo",
"cmovp",
"cmovpe",
"cmovpo",
"cmovs",
"cmovz",
"cmp",
"cmppd",
"cmpps",
"cmps",
"cmpsb",
"cmpsd",
"cmpsq",
"cmpss",
"cmpsw",
"cmpxchg",
"cmpxchg16b",
"cmpxchg8b",
"comisd",
"comiss",
"cpuid",
"cqo",
"crc32",
"cvtdq2pd",
"cvtdq2ps",
"cvtpd2dq",
"cvtpd2pi",
"cvtpd2ps",
"cvtpi2pd",
"cvtpi2ps",
"cvtps2dq",
"cvtps2pd",
"cvtps2pi",
"cvtsd2si",
"cvtsd2ss",
"cvtsi2sd",
"cvtsi2ss",
"cvtss2sd",
"cvtss2si",
"cvttpd2dq",
"cvttpd2pi",
"cvttps2dq",
"cvttps2pi",
"cvttsd2si",
"cvttss2si",
"cwd",
"cwde",
"daa",
"das",
"dec",
"div",
"divpd",
"divps",
"divsd",
"divss",
"dppd",
"dpps",
"emms",
"encls",
"enclu",
"enclv",
"encodekey128",
"encodekey256",
"endbr32",
"endbr64",
"enqcmd",
"enqcmds",
"enter",
"extractps",
"extrq",
"f2xm1",
"fabs",
"fadd",
"faddp",
"fbld",
"fbstp",
"fchs",
"fclex",
"fcmovb",
"fcmovbe",
"fcmove",
"fcmovnb",
"fcmovnbe",
"fcmovne",
"fcmovnu",
"fcmovu",
"fcom",
"fcomi",
"fcomip",
"fcomp",
"fcompp",
"fcos",
"fdecstp",
"fdiv",
"fdivp",
"fdivr",
"fdivrp",
"femms",
"ffree",
"fiadd",
"ficom",
"ficomp",
"fidiv",
"fidivr",
"fild",
"fimul",
"fincstp",
"fist",
"fistp",
"fisttp",
"fisub",
"fisubr",
"fld",
"fld1",
"fldcw",
"fldenv",
"fldl2e",
"fldl2t",
"fldlg2",
"fldln2",
"fldpi",
"fldz",
"fmul",
"fmulp",
"fnclex",
"fninit",
"fnop",
"fnsave",
"fnstcw",
"fnstenv",
"fnstsw",
"fpatan",
"fprem",
"fprem1",
"fptan",
"frndint",
"frstor",
"fscale",
"fsin",
"fsincos",
"fsqrt",
"fst",
"fstcw",
"fstenv",
"fstp",
"fstsw",
"fsub",
"fsubp",
"fsubr",
"fsubrp",
"ftst",
"fucom",
"fucomi",
"fucomip",
"fucomp",
"fucompp",
"fwait",
"fxam",
"fxch",
"fxrstor",
"fxrstor64",
"fxsave",
"fxsave64",
"fxtract",
"fyl2x",
"fyl2xp1",
"getsec",
"gf2p8affineinvqb",
"gf2p8affineqb",
"gf2p8mulb",
"haddpd",
"haddps",
"hlt",
"hreset",
"hsubpd",
"hsubps",
"idiv",
"imul",
"in",
"inc",
"incssp",
"incsspd",
"incsspq",
"ins",
"insb",
"insd",
"insertps",
"insw",
"int",
"int3",
"into",
"invd",
"invept",
"invlpg",
"invpcid",
"invvpid",
"iret",
"iretd",
"iretq",
"ja",
"jae",
"jb",
"jbe",
"jc",
"jcxz",
"je",
"jecxz",
"jg",
"jge",
"jl",
"jle",
"jmp",
"jna",
"jnae",
"jnb",
"jnbe",
"jnc",
"jne",
"jng",
"jnge",
"jnl",
"jnle",
"jno",
"jnp",
"jns",
"jnz",
"jo",
"jp",
"jpe",
"jpo",
"jrcxz",
"js",
"jz",
"kaddb",
"kaddd",
"kaddq",
"kaddw",
"kand",
"kandb",
"kandd",
"kandnb",
"kandnd",
"kandnq",
"kandnw",
"kandq",
"kandw",
"kmovb",
"kmovd",
"kmovq",
"kmovw",
"knot",
"knotb",
"knotd",
"knotq",
"knotw",
"kor",
"korb",
"kord",
"korq",
"kortestb",
"kortestd",
"kortestq",
"kortestw",
"korw",
"kshiftlb",
"kshiftld",
"kshiftlq",
"kshiftlw",
"kshiftrb",
"kshiftrd",
"kshiftrq",
"kshiftrw",
"ktestb",
"ktestd",
"ktestq",
"ktestw",
"kunpckbw",
"kunpckdq",
"kunpckwd",
"kxnor",
"kxnorb",
"kxnord",
"kxnorq",
"kxnorw",
"kxor",
"kxorb",
"kxord",
"kxorq",
"kxorw",
"lahf",
"lar",
"lddqu",
"ldmxcsr",
"lds",
"lea",
"leave",
"les",
"lfence",
"lfs",
"lgdt",
"lgs",
"lidt",
"lldt",
"llwpcb",
"lmsw",
"loadiwkey",
"lods",
"lodsb",
"lodsd",
"lodsq",
"lodsw",
"loop",
"loope",
"loopne",
"lsl",
"lss",
"ltr",
"lwpins",
"lwpval",
"lzcnt",
"maskmovdqu",
"maskmovq",
"maxpd",
"maxps",
"maxsd",
"maxss",
"mfence",
"minpd",
"minps",
"minsd",
"minss",
"monitor",
"monitorx",
"mov",
"movabs",
"movapd",
"movaps",
"movbe",
"movd",
"movddup",
"movdir64b",
"movdiri",
"movdq2q",
"movdqa",
"movdqu",
"movhlps",
"movhpd",
"movhps",
"movlhps",
"movlpd",
"movlps",
"movmskpd",
"movmskps",
"movntdq",
"movntdqa",
"movnti",
"movntpd",
"movntps",
"movntq",
"movq",
"movq2dq",
"movs",
"movsb",
"movsd",
"movshdup",
"movsldup",
"movsq",
"movss",
"movsw",
"movsx",
"movsxd",
"movupd",
"movups",
"movzx",
"mpsadbw",
"mul",
"mulpd",
"mulps",
"mulsd",
"mulss",
"mulx",
"mwait",
"mwaitx",
"neg",
"nop",
"not",
"or",
"orpd",
"orps",
"out",
"outs",
"outsb",
"outsd",
"outsw",
"pabsb",
"pabsd",
"pabsw",
"packssdw",
"packsswb",
"packusdw",
"packuswb",
"paddb",
"paddd",
"paddq",
"paddsb",
"paddsw",
"paddusb",
"paddusw",
"paddw",
"palignr",
"pand",
"pandn",
"pause",
"pavgb",
"pavgw",
"pblendvb",
"pblendw",
"pclmulqdq",
"pcmpeqb",
"pcmpeqd",
"pcmpeqq",
"pcmpeqw",
"pcmpestri",
"pcmpestrm",
"pcmpgtb",
"pcmpgtd",
"pcmpgtq",
"pcmpgtw",
"pcmpistri",
"pcmpistrm",
"pconfig",
"pdep",
"pext",
"pextrb",
"pextrd",
"pextrq",
"pextrw",
"pf2id",
"pf2iw",
"pfacc",
"pfadd",
"pfcmpeq",
"pfcmpge",
"pfcmpgt",
"pfcpit1",
"pfmax",
"pfmin",
"pfmul",
"pfnacc",
"pfpnacc",
"pfrcp",
"pfrcpit1",
"pfrcpit2",
"pfrsqit1",
"pfrsqrt",
"pfsub",
"pfsubr",
"phaddd",
"phaddsw",
"phaddw",
"phminposuw",
"phsubd",
"phsubsw",
"phsubw",
"pi2fd",
"pi2fw",
"pinsrb",
"pinsrd",
"pinsrq",
"pinsrw",
"pmaddubsw",
"pmaddwd",
"pmaxsb",
"pmaxsd",
"pmaxsw",
"pmaxub",
"pmaxud",
"pmaxuw",
"pminsb",
"pminsd",
"pminsw",
"pminub",
"pminud",
"pminuw",
"pmovmskb",
"pmovsxbd",
"pmovsxbq",
"pmovsxbw",
"pmovsxdq",
"pmovsxwd",
"pmovsxwq",
"pmovzxbd",
"pmovzxbq",
"pmovzxbw",
"pmovzxdq",
"pmovzxwd",
"pmovzxwq",
"pmuldq",
"pmulhrsw",
"pmulhuw",
"pmulhw",
"pmulld",
"pmullw",
"pmuludq",
"pop",
"popa",
"popcnt",
"popf",
"popfq",
"por",
"prefetch",
"prefetchit0",
"prefetchit1",
"prefetchnta",
"prefetcht0",
"prefetcht1",
"prefetcht2",
"prefetchw",
"prefetchwt1",
"psadbw",
"pshufb",
"pshufd",
"pshufhw",
"pshuflw",
"pshufw",
"psignb",
"psignd",
"psignw",
"pslld",
"pslldq",
"psllq",
"psllw",
"psrad",
"psraw",
"psrld",
"psrldq",
"psrlq",
"psrlw",
"psubb",
"psubd",
"psubq",
"psubsb",
"psubsw",
"psubusb",
"psubusw",
"psubw",
"pswapd",
"ptest",
"ptwrite",
"punpckhbw",
"punpckhdq",
"punpckhqdq",
"punpckhwd",
"punpcklbw",
"punpckldq",
"punpcklqdq",
"punpcklwd",
"push",
"pusha",
"pushf",
"pushfq",
"pxor",
"rcl",
"rcpps",
"rcpss",
"rcl",
"rcr",
"rdfsbase",
"rdgsbase",
"rdmsr",
"rdmsrlist",
"rdpid",
"rdpkru",
"rdpmc",
"rdrand",
"rdseed",
"rdssp",
"rdsspd",
"rdsspq",
"rdtsc",
"rdtscp",
"rep",
"repe",
"repne",
"repnz",
"repz",
"ret",
"retf",
"rol",
"ror",
"rorx",
"roundpd",
"roundps",
"roundsd",
"roundss",
"rsm",
"rsqrtps",
"rsqrtss",
"rstorssp",
"sahf",
"sal",
"sar",
"sarx",
"saveprevssp",
"sbb",
"scas",
"scasb",
"scasd",
"scasq",
"scasw",
"serialize",
"seta",
"setae",
"setb",
"setbe",
"setc",
"sete",
"setg",
"setge",
"setl",
"setle",
"setna",
"setnae",
"setnb",
"setnbe",
"setnc",
"setne",
"setng",
"setnge",
"setnl",
"setnle",
"setno",
"setnp",
"setns",
"setnz",
"seto",
"setp",
"setpe",
"setpo",
"sets",
"setssbsy",
"setz",
"sfence",
"sgdt",
"sha1msg1",
"sha1msg2",
"sha1nexte",
"sha1rnds4",
"sha256msg1",
"sha256msg2",
"sha256rnds2",
"shl",
"shld",
"shlx",
"shr",
"shrd",
"shrx",
"shufpd",
"shufps",
"sidt",
"skinit",
"sldt",
"slwpcb",
"sms w",
"sqrtpd",
"sqrtps",
"sqrtsd",
"sqrtss",
"stac",
"stc",
"std",
"sti",
"stmxcsr",
"stos",
"stosb",
"stosd",
"stosq",
"stosw",
"str",
"stui",
"sub",
"subpd",
"subps",
"subsd",
"subss",
"swapgs",
"syscall",
"sysenter",
"sysexit",
"sysret",
"t1mskc",
"tdpbssd",
"tdpbsud",
"tdpbusd",
"tdpbuud",
"test",
"testui",
"tileconfig",
"tileloadd",
"tileloaddt1",
"tilerelease",
"tilestored",
"tilezero",
"tpause",
"tzcnt",
"tzmsk",
"ucomisd",
"ucomiss",
"ud0",
"ud1",
"ud2",
"umonitor",
"umwait",
"unpckhpd",
"unpckhps",
"unpcklpd",
"unpcklps",
"vaddpd",
"vaddph",
"vaddps",
"vaddsd",
"vaddsh",
"vaddss",
"vaddsubpd",
"vaddsubps",
"vaesdec",
"vaesdeclast",
"vaesenc",
"vaesenclast",
"valignd",
"valignq",
"vandnpd",
"vandnps",
"vandpd",
"vandps",
"vblendmpd",
"vblendmps",
"vblendpd",
"vblendps",
"vblendvpd",
"vblendvps",
"vbroadcastf128",
"vbroadcastf32x2",
"vbroadcastf32x4",
"vbroadcastf32x8",
"vbroadcastf64x2",
"vbroadcastf64x4",
"vbroadcasti128",
"vbroadcasti32x2",
"vbroadcasti32x4",
"vbroadcasti32x8",
"vbroadcasti64x2",
"vbroadcasti64x4",
"vbroadcastsd",
"vbroadcastss",
"vcmppd",
"vcmpph",
"vcmpps",
"vcmpsd",
"vcmpss",
"vcomisd",
"vcomiss",
"vcompresspd",
"vcompressps",
"vcvtdq2pd",
"vcvtdq2ps",
"vcvtne2ps2bf16",
"vcvtneps2bf16",
"vcvtpd2dq",
"vcvtpd2ph",
"vcvtpd2ps",
"vcvtpd2qq",
"vcvtpd2udq",
"vcvtpd2uqq",
"vcvtph2dq",
"vcvtph2pd",
"vcvtph2ps",
"vcvtph2psx",
"vcvtph2qq",
"vcvtph2udq",
"vcvtph2uqq",
"vcvtph2uw",
"vcvtph2w",
"vcvtps2dq",
"vcvtps2pd",
"vcvtps2ph",
"vcvtps2phx",
"vcvtps2qq",
"vcvtps2udq",
"vcvtps2uqq",
"vcvtqq2pd",
"vcvtqq2ph",
"vcvtqq2ps",
"vcvtsd2si",
"vcvtsd2ss",
"vcvtsd2usi",
"vcvtsh2si",
"vcvtsh2ss",
"vcvtsh2usi",
"vcvtsi2sd",
"vcvtsi2sh",
"vcvtsi2ss",
"vcvtss2sd",
"vcvtss2sh",
"vcvtss2si",
"vcvtss2usi",
"vcvttpd2dq",
"vcvttpd2qq",
"vcvttpd2udq",
"vcvttpd2uqq",
"vcvttph2dq",
"vcvttph2qq",
"vcvttph2udq",
"vcvttph2uqq",
"vcvttph2uw",
"vcvttph2w",
"vcvttps2dq",
"vcvttps2qq",
"vcvttps2udq",
"vcvttps2uqq",
"vcvttsd2si",
"vcvttsd2usi",
"vcvttsh2si",
"vcvttsh2usi",
"vcvttss2si",
"vcvttss2usi",
"vcvtudq2pd",
"vcvtudq2ph",
"vcvtudq2ps",
"vcvtuqq2pd",
"vcvtuqq2ph",
"vcvtuqq2ps",
"vcvtusi2sd",
"vcvtusi2sh",
"vcvtusi2ss",
"vcvtuw2ph",
"vcvtw2ph",
"vdbpsadbw",
"vdivpd",
"vdivph",
"vdivps",
"vdivsd",
"vdivsh",
"vdivss",
"vdpbf16ps",
"verr",
"verw",
"vexp2pd",
"vexp2ps",
"vexpandpd",
"vexpandps",
"vextractf128",
"vextractf32x4",
"vextractf32x8",
"vextractf64x2",
"vextractf64x4",
"vextracti128",
"vextracti32x4",
"vextracti32x8",
"vextracti64x2",
"vextracti64x4",
"vextractps",
"vfcmaddcph",
"vfcmaddcsh",
"vfcmulcph",
"vfcmulcsh",
"vfixupimmpd",
"vfixupimmps",
"vfixupimmsd",
"vfixupimmss",
"vfmadd132pd",
"vfmadd132ph",
"vfmadd132ps",
"vfmadd132sd",
"vfmadd132sh",
"vfmadd132ss",
"vfmadd213pd",
"vfmadd213ph",
"vfmadd213ps",
"vfmadd213sd",
"vfmadd213sh",
"vfmadd213ss",
"vfmadd231pd",
"vfmadd231ph",
"vfmadd231ps",
"vfmadd231sd",
"vfmadd231sh",
"vfmadd231ss",
"vfmaddcph",
"vfmaddcsh",
"vfmaddsub132pd",
"vfmaddsub132ps",
"vfmaddsub213pd",
"vfmaddsub213ps",
"vfmaddsub231pd",
"vfmaddsub231ps",
"vfmsub132pd",
"vfmsub132ph",
"vfmsub132ps",
"vfmsub132sd",
"vfmsub132sh",
"vfmsub132ss",
"vfmsub213pd",
"vfmsub213ph",
"vfmsub213ps",
"vfmsub213sd",
"vfmsub213sh",
"vfmsub213ss",
"vfmsub231pd",
"vfmsub231ph",
"vfmsub231ps",
"vfmsub231sd",
"vfmsub231sh",
"vfmsub231ss",
"vfmsubadd132pd",
"vfmsubadd132ps",
"vfmsubadd213pd",
"vfmsubadd213ps",
"vfmsubadd231pd",
"vfmsubadd231ps",
"vfmulcph",
"vfmulcsh",
"vfnmadd132pd",
"vfnmadd132ph",
"vfnmadd132ps",
"vfnmadd132sd",
"vfnmadd132sh",
"vfnmadd132ss",
"vfnmadd213pd",
"vfnmadd213ph",
"vfnmadd213ps",
"vfnmadd213sd",
"vfnmadd213sh",
"vfnmadd213ss",
"vfnmadd231pd",
"vfnmadd231ph",
"vfnmadd231ps",
"vfnmadd231sd",
"vfnmadd231sh",
"vfnmadd231ss",
"vfnmsub132pd",
"vfnmsub132ph",
"vfnmsub132ps",
"vfnmsub132sd",
"vfnmsub132sh",
"vfnmsub132ss",
"vfnmsub213pd",
"vfnmsub213ph",
"vfnmsub213ps",
"vfnmsub213sd",
"vfnmsub213sh",
"vfnmsub213ss",
"vfnmsub231pd",
"vfnmsub231ph",
"vfnmsub231ps",
"vfnmsub231sd",
"vfnmsub231sh",
"vfnmsub231ss",
"vfpclasspd",
"vfpclassph",
"vfpclassps",
"vfpclasssd",
"vfpclasssh",
"vfpclassss",
"vfrczpd",
"vfrczps",
"vfrczsd",
"vfrczss",
"vgatherdpd",
"vgatherdps",
"vgatherpf0dpd",
"vgatherpf0dps",
"vgatherpf0hintdpd",
"vgatherpf0hintdps",
"vgatherpf0qpd",
"vgatherpf0qps",
"vgatherpf1dpd",
"vgatherpf1dps",
"vgatherpf1qpd",
"vgatherpf1qps",
"vgatherqpd",
"vgatherqps",
"vgetexppd",
"vgetexpph",
"vgetexpps",
"vgetexpsd",
"vgetexpsh",
"vgetexpss",
"vgetmantpd",
"vgetmantph",
"vgetmantps",
"vgetmantsd",
"vgetmantsh",
"vgetmantss",
"vgf2p8affineinvqb",
"vgf2p8affineqb",
"vgf2p8mulb",
"vhaddpd",
"vhaddps",
"vhsubpd",
"vhsubps",
"vinsertf128",
"vinsertf32x4",
"vinsertf32x8",
"vinsertf64x2",
"vinsertf64x4",
"vinserti128",
"vinserti32x4",
"vinserti32x8",
"vinserti64x2",
"vinserti64x4",
"vinsertps",
"vlddqu",
"vldmxcsr",
"vmaskmovdqu",
"vmaskmovpd",
"vmaskmovps",
"vmaxpd",
"vmaxph",
"vmaxps",
"vmaxsd",
"vmaxsh",
"vmaxss",
"vmcall",
"vmclear",
"vmfunc",
"vminpd",
"vminph",
"vminps",
"vminsd",
"vminsh",
"vminss",
"vmlaunch",
"vmload",
"vmmcall",
"vmovapd",
"vmovaps",
"vmovd",
"vmovddup",
"vmovdqa",
"vmovdqa32",
"vmovdqa64",
"vmovdqu",
"vmovdqu16",
"vmovdqu32",
"vmovdqu64",
"vmovdqu8",
"vmovhlps",
"vmovhpd",
"vmovhps",
"vmovlhps",
"vmovlpd",
"vmovlps",
"vmovmskpd",
"vmovmskps",
"vmovntdq",
"vmovntdqa",
"vmovntpd",
"vmovntps",
"vmovq",
"vmovsd",
"vmovshdup",
"vmovsldup",
"vmovss",
"vmovupd",
"vmovups",
"vmpsadbw",
"vmptrld",
"vmptrst",
"vmread",
"vmresume",
"vmrun",
"vmsave",
"vmulpd",
"vmulph",
"vmulps",
"vmulsd",
"vmulsh",
"vmulss",
"vmwrite",
"vmxoff",
"vmxon",
"vorpd",
"vorps",
"vp2intersectd",
"vp2intersectq",
"vp4dpwssd",
"vp4dpwssds",
"vpabdb",
"vpabsd",
"vpabsq",
"vpabsw",
"vpackssdw",
"vpacksswb",
"vpackusdw",
"vpackuswb",
"vpadcd",
"vpaddb",
"vpaddd",
"vpaddq",
"vpaddsb",
"vpaddsw",
"vpaddusb",
"vpaddusw",
"vpaddw",
"vpalignr",
"vpand",
"vpandd",
"vpandn",
"vpandnd",
"vpandnq",
"vpandq",
"vpavgb",
"vpavgw",
"vpblendd",
"vpblendmb",
"vpblendmd",
"vpblendmq",
"vpblendmw",
"vpblendvb",
"vpblendw",
"vpbroadcastb",
"vpbroadcastd",
"vpbroadcastmb2q",
"vpbroadcastmw2d",
"vpbroadcastq",
"vpbroadcastw",
"vpclmulqdq",
"vpcmov",
"vpcmpb",
"vpcmpd",
"vpcmpq",
"vpcmpub",
"vpcmpud",
"vpcmpuq",
"vpcmpuw",
"vpcmpw",
"vpcomb",
"vpcomd",
"vpcompressb",
"vpcompressd",
"vpcompressq",
"vpcompressw",
"vpcomq",
"vpcomub",
"vpcomud",
"vpcomuq",
"vpcomuw",
"vpcomw",
"vpconflictd",
"vpconflictq",
"vpdpbusd",
"vpdpbusds",
"vpdpbssd",
"vpdpbssds",
"vpdpbuud",
"vpdpbuuds",
"vpdpwssd",
"vpdpwssds",
"vpdpwsud",
"vpdpwsuds",
"vpdpwuud",
"vpdpwuuds",
"vperm2f128",
"vperm2i128",
"vpermb",
"vpermd",
"vpermi2b",
"vpermi2d",
"vpermi2pd",
"vpermi2ps",
"vpermi2q",
"vpermi2w",
"vpermilpd",
"vpermilps",
"vpermpd",
"vpermps",
"vpermq",
"vpermt2b",
"vpermt2d",
"vpermt2pd",
"vpermt2ps",
"vpermt2q",
"vpermt2w",
"vpermw",
"vpexpandb",
"vpexpandd",
"vpexpandq",
"vpexpandw",
"vpextrb",
"vpextrd",
"vpextrq",
"vpextrw",
"vpgatherdd",
"vpgatherdq",
"vpgatherqd",
"vpgatherqq",
"vphaddbd",
"vphaddbq",
"vphaddbw",
"vphaddd",
"vphadddq",
"vphaddsw",
"vphaddubd",
"vphaddubq",
"vphaddubw",
"vphaddudq",
"vphadduwd",
"vphadduwq",
"vphaddw",
"vphaddwd",
"vphaddwq",
"vphminposuw",
"vphsubbw",
"vphsubd",
"vphsubdq",
"vphsubsw",
"vphsubw",
"vphsubwd",
"vpinsrb",
"vpinsrd",
"vpinsrq",
"vpinsrw",
"vplzcntd",
"vplzcntq",
"vpmacsdd",
"vpmacsdqh",
"vpmacsdql",
"vpmacssdd",
"vpmacssdqh",
"vpmacssdql",
"vpmacsswd",
"vpmacssww",
"vpmacswd",
"vpmacsww",
"vpmadcsswd",
"vpmadcswd",
"vpmadd52huq",
"vpmadd52luq",
"vpmaddubsw",
"vpmaddwd",
"vpmaskmovd",
"vpmaskmovq",
"vpmaxsb",
"vpmaxsd",
"vpmaxsq",
"vpmaxsw",
"vpmaxub",
"vpmaxud",
"vpmaxuq",
"vpmaxuw",
"vpminsb",
"vpminsd",
"vpminsq",
"vpminsw",
"vpminub",
"vpminud",
"vpminuq",
"vpminuw",
"vpmovb2m",
"vpmovd2m",
"vpmovdb",
"vpmovdw",
"vpmovm2b",
"vpmovm2d",
"vpmovm2q",
"vpmovm2w",
"vpmovmskb",
"vpmovq2m",
"vpmovqb",
"vpmovqd",
"vpmovqw",
"vpmovsdb",
"vpmovsdw",
"vpmovsqb",
"vpmovsqd",
"vpmovsqw",
"vpmovswb",
"vpmovsxbd",
"vpmovsxbq",
"vpmovsxbw",
"vpmovsxdq",
"vpmovsxwd",
"vpmovsxwq",
"vpmovusdb",
"vpmovusdw",
"vpmovusqb",
"vpmovusqd",
"vpmovusqw",
"vpmovuswb",
"vpmovw2m",
"vpmovwb",
"vpmovzxbd",
"vpmovzxbq",
"vpmovzxbw",
"vpmovzxdq",
"vpmovzxwd",
"vpmovzxwq",
"vpmuldq",
"vpmulhrsw",
"vpmulhuw",
"vpmulhw",
"vpmulld",
"vpmullq",
"vpmullw",
"vpmuludq",
"vpmultishiftqb",
"vpord",
"vporq",
"vpor",
"vpperm",
"vprold",
"vprolq",
"vprolvd",
"vprolvq",
"vprord",
"vprorq",
"vprorvd",
"vprorvq",
"vprotb",
"vprotd",
"vprotq",
"vprotw",
"vpsadbw",
"vpscatterdd",
"vpscatterdq",
"vpscatterqd",
"vpscatterqq",
"vpshab",
"vpshad",
"vpshaq",
"vpshaw",
"vpshlb",
"vpshld",
"vpshldd",
"vpshldq",
"vpshldvd",
"vpshldvq",
"vpshldvw",
"vpshldw",
"vpshlq",
"vpshlw",
"vpshrdd",
"vpshrdq",
"vpshrdvd",
"vpshrdvq",
"vpshrdvw",
"vpshrdw",
"vpshufb",
"vpshufbitqmb",
"vpshufd",
"vpshufhw",
"vpshuflw",
"vpsignb",
"vpsignd",
"vpsignw",
"vpslld",
"vpslldq",
"vpsllq",
"vpsllvd",
"vpsllvq",
"vpsllvw",
"vpsllw",
"vpsrad",
"vpsraq",
"vpsravd",
"vpsravq",
"vpsravw",
"vpsraw",
"vpsrld",
"vpsrldq",
"vpsrlq",
"vpsrlvd",
"vpsrlvq",
"vpsrlvw",
"vpsrlw",
"vpsubb",
"vpsubd",
"vpsubq",
"vpsubsb",
"vpsubsw",
"vpsubusb",
"vpsubusw",
"vpsubw",
"vpternlogd",
"vpternlogq",
"vptest",
"vptestmb",
"vptestmd",
"vptestmq",
"vptestmw",
"vptestnmb",
"vptestnmd",
"vptestnmq",
"vptestnmw",
"vpunpckhbw",
"vpunpckhdq",
"vpunpckhqdq",
"vpunpckhwd",
"vpunpcklbw",
"vpunpckldq",
"vpunpcklqdq",
"vpunpcklwd",
"vpxor",
"vpxord",
"vpxorq",
"vrangepd",
"vrangeph",
"vrangeps",
"vrangesd",
"vrangesh",
"vrangess",
"vrcp14pd",
"vrcp14ps",
"vrcp14sd",
"vrcp14ss",
"vrcp28pd",
"vrcp28ps",
"vrcp28sd",
"vrcp28ss",
"vrcpph",
"vrcpps",
"vrcpsh",
"vrcpss",
"vreducepd",
"vreduceph",
"vreduceps",
"vreducesd",
"vreducesh",
"vreducess",
"vrndscalepd",
"vrndscaleph",
"vrndscaleps",
"vrndscalesd",
"vrndscalesh",
"vrndscaless",
"vroundpd",
"vroundps",
"vroundsd",
"vroundss",
"vrsqrt14pd",
"vrsqrt14ps",
"vrsqrt14sd",
"vrsqrt14ss",
"vrsqrt28pd",
"vrsqrt28ps",
"vrsqrt28sd",
"vrsqrt28ss",
"vrsqrtph",
"vrsqrtps",
"vrsqrtsh",
"vrsqrtss",
"vscalefpd",
"vscalefph",
"vscalefps",
"vscalefsd",
"vscalefsh",
"vscalefss",
"vscatterdpd",
"vscatterdps",
"vscatterpf0dpd",
"vscatterpf0dps",
"vscatterpf0qpd",
"vscatterpf0qps",
"vscatterpf1dpd",
"vscatterpf1dps",
"vscatterpf1qpd",
"vscatterpf1qps",
"vscatterqpd",
"vscatterqps",
"vshuff32x4",
"vshuff64x2",
"vshufi32x4",
"vshufi64x2",
"vshufpd",
"vshufps",
"vsqrtpd",
"vsqrtph",
"vsqrtps",
"vsqrtsd",
"vsqrtsh",
"vsqrtss",
"vstmxcsr",
"vsubpd",
"vsubph",
"vsubps",
"vsubsd",
"vsubsh",
"vsubss",
"vtestpd",
"vtestps",
"vucomisd",
"vucomiss",
"vunpckhpd",
"vunpckhps",
"vunpcklpd",
"vunpcklps",
"vxorpd",
"vxorps",
"vzeroall",
"vzeroupper",
"wait",
"wbinvd",
"wrfsbase",
"wrgsbase",
"wrmsr",
"wrmsrlist",
"wrmsrns",
"wrpkru",
"wrss",
"wrssd",
"wrssq",
"wruss",
"wrussd",
"wrussq",
"xabort",
"xadd",
"xbegin",
"xchg",
"xcryptecb",
"xcryptcbc",
"xcryptctr",
"xcryptcfb",
"xcryptofb",
"xend",
"xgetbv",
"xlat",
"xlatb",
"xor",
"xorpd",
"xorps",
"xrstor",
"xrstor64",
"xrstors",
"xrstors64",
"xsave",
"xsave64",
"xsavec",
"xsavec64",
"xsaveopt",
"xsaveopt64",
"xsaves",
"xsaves64",
"xsetbv",
"xtest",
];
pub struct X86OperandParser {
reg_info: X86RegisterInfo,
syntax: AsmSyntaxMode,
}
impl X86OperandParser {
pub fn new(syntax: AsmSyntaxMode) -> Self {
X86OperandParser {
reg_info: X86RegisterInfo,
syntax,
}
}
pub fn parse_register(&self, name: &str) -> Option<X86Reg> {
let clean = name.trim_start_matches('%').to_lowercase();
match clean.as_str() {
"rax" => Some(X86Reg(RAX)),
"rcx" => Some(X86Reg(RCX)),
"rdx" => Some(X86Reg(RDX)),
"rbx" => Some(X86Reg(RBX)),
"rsp" => Some(X86Reg(RSP)),
"rbp" => Some(X86Reg(RBP)),
"rsi" => Some(X86Reg(RSI)),
"rdi" => Some(X86Reg(RDI)),
"r8" => Some(X86Reg(R8)),
"r9" => Some(X86Reg(R9)),
"r10" => Some(X86Reg(R10)),
"r11" => Some(X86Reg(R11)),
"r12" => Some(X86Reg(R12)),
"r13" => Some(X86Reg(R13)),
"r14" => Some(X86Reg(R14)),
"r15" => Some(X86Reg(R15)),
"eax" => Some(X86Reg(EAX)),
"ecx" => Some(X86Reg(ECX)),
"edx" => Some(X86Reg(EDX)),
"ebx" => Some(X86Reg(EBX)),
"esp" => Some(X86Reg(ESP)),
"ebp" => Some(X86Reg(EBP)),
"esi" => Some(X86Reg(ESI)),
"edi" => Some(X86Reg(EDI)),
"r8d" => Some(X86Reg(R8D)),
"r9d" => Some(X86Reg(R9D)),
"r10d" => Some(X86Reg(R10D)),
"r11d" => Some(X86Reg(R11D)),
"r12d" => Some(X86Reg(R12D)),
"r13d" => Some(X86Reg(R13D)),
"r14d" => Some(X86Reg(R14D)),
"r15d" => Some(X86Reg(R15D)),
"ax" => Some(X86Reg(AX)),
"cx" => Some(X86Reg(CX)),
"dx" => Some(X86Reg(DX)),
"bx" => Some(X86Reg(BX)),
"sp" => Some(X86Reg(SP)),
"bp" => Some(X86Reg(BP)),
"si" => Some(X86Reg(SI)),
"di" => Some(X86Reg(DI)),
"r8w" => Some(X86Reg(R8W)),
"r9w" => Some(X86Reg(R9W)),
"r10w" => Some(X86Reg(R10W)),
"r11w" => Some(X86Reg(R11W)),
"r12w" => Some(X86Reg(R12W)),
"r13w" => Some(X86Reg(R13W)),
"r14w" => Some(X86Reg(R14W)),
"r15w" => Some(X86Reg(R15W)),
"al" => Some(X86Reg(AL)),
"cl" => Some(X86Reg(CL)),
"dl" => Some(X86Reg(DL)),
"bl" => Some(X86Reg(BL)),
"spl" => Some(X86Reg(SPL)),
"bpl" => Some(X86Reg(BPL)),
"sil" => Some(X86Reg(SIL)),
"dil" => Some(X86Reg(DIL)),
"r8b" => Some(X86Reg(R8B)),
"r9b" => Some(X86Reg(R9B)),
"r10b" => Some(X86Reg(R10B)),
"r11b" => Some(X86Reg(R11B)),
"r12b" => Some(X86Reg(R12B)),
"r13b" => Some(X86Reg(R13B)),
"r14b" => Some(X86Reg(R14B)),
"r15b" => Some(X86Reg(R15B)),
"ah" => Some(X86Reg(AH)),
"ch" => Some(X86Reg(CH)),
"dh" => Some(X86Reg(DH)),
"bh" => Some(X86Reg(BH)),
"cs" => Some(X86Reg(CS)),
"ds" => Some(X86Reg(DS)),
"ss" => Some(X86Reg(SS)),
"es" => Some(X86Reg(ES)),
"fs" => Some(X86Reg(FS)),
"gs" => Some(X86Reg(GS)),
"xmm0" => Some(X86Reg(XMM0)),
"xmm1" => Some(X86Reg(XMM1)),
"xmm2" => Some(X86Reg(XMM2)),
"xmm3" => Some(X86Reg(XMM3)),
"xmm4" => Some(X86Reg(XMM4)),
"xmm5" => Some(X86Reg(XMM5)),
"xmm6" => Some(X86Reg(XMM6)),
"xmm7" => Some(X86Reg(XMM7)),
"xmm8" => Some(X86Reg(XMM8)),
"xmm9" => Some(X86Reg(XMM9)),
"xmm10" => Some(X86Reg(XMM10)),
"xmm11" => Some(X86Reg(XMM11)),
"xmm12" => Some(X86Reg(XMM12)),
"xmm13" => Some(X86Reg(XMM13)),
"xmm14" => Some(X86Reg(XMM14)),
"xmm15" => Some(X86Reg(XMM15)),
"xmm16" => Some(X86Reg(XMM16)),
"xmm17" => Some(X86Reg(XMM17)),
"xmm18" => Some(X86Reg(XMM18)),
"xmm19" => Some(X86Reg(XMM19)),
"xmm20" => Some(X86Reg(XMM20)),
"xmm21" => Some(X86Reg(XMM21)),
"xmm22" => Some(X86Reg(XMM22)),
"xmm23" => Some(X86Reg(XMM23)),
"xmm24" => Some(X86Reg(XMM24)),
"xmm25" => Some(X86Reg(XMM25)),
"xmm26" => Some(X86Reg(XMM26)),
"xmm27" => Some(X86Reg(XMM27)),
"xmm28" => Some(X86Reg(XMM28)),
"xmm29" => Some(X86Reg(XMM29)),
"xmm30" => Some(X86Reg(XMM30)),
"xmm31" => Some(X86Reg(XMM31)),
"ymm0" => Some(X86Reg(YMM0)),
"ymm1" => Some(X86Reg(YMM1)),
"ymm2" => Some(X86Reg(YMM2)),
"ymm3" => Some(X86Reg(YMM3)),
"ymm4" => Some(X86Reg(YMM4)),
"ymm5" => Some(X86Reg(YMM5)),
"ymm6" => Some(X86Reg(YMM6)),
"ymm7" => Some(X86Reg(YMM7)),
"ymm8" => Some(X86Reg(YMM8)),
"ymm9" => Some(X86Reg(YMM9)),
"ymm10" => Some(X86Reg(YMM10)),
"ymm11" => Some(X86Reg(YMM11)),
"ymm12" => Some(X86Reg(YMM12)),
"ymm13" => Some(X86Reg(YMM13)),
"ymm14" => Some(X86Reg(YMM14)),
"ymm15" => Some(X86Reg(YMM15)),
"ymm16" => Some(X86Reg(YMM16)),
"ymm17" => Some(X86Reg(YMM17)),
"ymm18" => Some(X86Reg(YMM18)),
"ymm19" => Some(X86Reg(YMM19)),
"ymm20" => Some(X86Reg(YMM20)),
"ymm21" => Some(X86Reg(YMM21)),
"ymm22" => Some(X86Reg(YMM22)),
"ymm23" => Some(X86Reg(YMM23)),
"ymm24" => Some(X86Reg(YMM24)),
"ymm25" => Some(X86Reg(YMM25)),
"ymm26" => Some(X86Reg(YMM26)),
"ymm27" => Some(X86Reg(YMM27)),
"ymm28" => Some(X86Reg(YMM28)),
"ymm29" => Some(X86Reg(YMM29)),
"ymm30" => Some(X86Reg(YMM30)),
"ymm31" => Some(X86Reg(YMM31)),
"zmm0" => Some(X86Reg(ZMM0)),
"zmm1" => Some(X86Reg(ZMM1)),
"zmm2" => Some(X86Reg(ZMM2)),
"zmm3" => Some(X86Reg(ZMM3)),
"zmm4" => Some(X86Reg(ZMM4)),
"zmm5" => Some(X86Reg(ZMM5)),
"zmm6" => Some(X86Reg(ZMM6)),
"zmm7" => Some(X86Reg(ZMM7)),
"zmm8" => Some(X86Reg(ZMM8)),
"zmm9" => Some(X86Reg(ZMM9)),
"zmm10" => Some(X86Reg(ZMM10)),
"zmm11" => Some(X86Reg(ZMM11)),
"zmm12" => Some(X86Reg(ZMM12)),
"zmm13" => Some(X86Reg(ZMM13)),
"zmm14" => Some(X86Reg(ZMM14)),
"zmm15" => Some(X86Reg(ZMM15)),
"zmm16" => Some(X86Reg(ZMM16)),
"zmm17" => Some(X86Reg(ZMM17)),
"zmm18" => Some(X86Reg(ZMM18)),
"zmm19" => Some(X86Reg(ZMM19)),
"zmm20" => Some(X86Reg(ZMM20)),
"zmm21" => Some(X86Reg(ZMM21)),
"zmm22" => Some(X86Reg(ZMM22)),
"zmm23" => Some(X86Reg(ZMM23)),
"zmm24" => Some(X86Reg(ZMM24)),
"zmm25" => Some(X86Reg(ZMM25)),
"zmm26" => Some(X86Reg(ZMM26)),
"zmm27" => Some(X86Reg(ZMM27)),
"zmm28" => Some(X86Reg(ZMM28)),
"zmm29" => Some(X86Reg(ZMM29)),
"zmm30" => Some(X86Reg(ZMM30)),
"zmm31" => Some(X86Reg(ZMM31)),
"k0" => Some(X86Reg(K0)),
"k1" => Some(X86Reg(K1)),
"k2" => Some(X86Reg(K2)),
"k3" => Some(X86Reg(K3)),
"k4" => Some(X86Reg(K4)),
"k5" => Some(X86Reg(K5)),
"k6" => Some(X86Reg(K6)),
"k7" => Some(X86Reg(K7)),
"mm0" => Some(X86Reg(MM0)),
"mm1" => Some(X86Reg(MM1)),
"mm2" => Some(X86Reg(MM2)),
"mm3" => Some(X86Reg(MM3)),
"mm4" => Some(X86Reg(MM4)),
"mm5" => Some(X86Reg(MM5)),
"mm6" => Some(X86Reg(MM6)),
"mm7" => Some(X86Reg(MM7)),
"st0" => Some(X86Reg(ST0)),
"st1" => Some(X86Reg(ST1)),
"st2" => Some(X86Reg(ST2)),
"st3" => Some(X86Reg(ST3)),
"st4" => Some(X86Reg(ST4)),
"st5" => Some(X86Reg(ST5)),
"st6" => Some(X86Reg(ST6)),
"st7" => Some(X86Reg(ST7)),
"st" => Some(X86Reg(ST0)),
"st(0)" => Some(X86Reg(ST0)),
"cr0" => Some(X86Reg(CR0)),
"cr1" => Some(X86Reg(CR1)),
"cr2" => Some(X86Reg(CR2)),
"cr3" => Some(X86Reg(CR3)),
"cr4" => Some(X86Reg(CR4)),
"cr5" => Some(X86Reg(CR5)),
"cr6" => Some(X86Reg(CR6)),
"cr7" => Some(X86Reg(CR7)),
"cr8" => Some(X86Reg(CR8)),
"dr0" => Some(X86Reg(DR0)),
"dr1" => Some(X86Reg(DR1)),
"dr2" => Some(X86Reg(DR2)),
"dr3" => Some(X86Reg(DR3)),
"dr4" => Some(X86Reg(DR4)),
"dr5" => Some(X86Reg(DR5)),
"dr6" => Some(X86Reg(DR6)),
"dr7" => Some(X86Reg(DR7)),
"bnd0" => Some(X86Reg(BND0)),
"bnd1" => Some(X86Reg(BND1)),
"bnd2" => Some(X86Reg(BND2)),
"bnd3" => Some(X86Reg(BND3)),
"rflags" => Some(X86Reg(RFLAGS)),
"eflags" => Some(X86Reg(EFLAGS)),
"rip" => Some(X86Reg(RIP)),
"eip" => Some(X86Reg(EIP)),
_ => None,
}
}
pub fn parse_immediate(&self, text: &str) -> Option<i64> {
let s = text.trim_start_matches('$');
if s.is_empty() {
return None;
}
if s.starts_with("0x") || s.starts_with("0X") {
return i64::from_str_radix(&s[2..], 16).ok();
}
if s.starts_with("$") {
return i64::from_str_radix(&s[1..], 16).ok();
}
if s.starts_with("&H") || s.starts_with("&h") {
return i64::from_str_radix(&s[2..], 16).ok();
}
if s.starts_with("0b") || s.starts_with("0B") {
return i64::from_str_radix(&s[2..], 2).ok();
}
if s.starts_with("0o") || s.starts_with("0O") {
return i64::from_str_radix(&s[2..], 8).ok();
}
let neg = s.starts_with('-');
let clean = s.trim_start_matches('-').trim_start_matches('+');
if let Ok(v) = clean.parse::<i64>() {
Some(if neg { -v } else { v })
} else {
None
}
}
pub fn parse_memory_att(&self, text: &str) -> Option<MemRef> {
let s = text.trim();
let mut mem = MemRef::default();
let mut remaining = s;
if let Some(rest) = remaining.strip_prefix("%fs:") {
mem.segment = Some(X86Reg(FS));
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("%gs:") {
mem.segment = Some(X86Reg(GS));
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("%cs:") {
mem.segment = Some(X86Reg(CS));
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("%ds:") {
mem.segment = Some(X86Reg(DS));
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("%es:") {
mem.segment = Some(X86Reg(ES));
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix("%ss:") {
mem.segment = Some(X86Reg(SS));
remaining = rest;
}
if let Some(paren_pos) = remaining.find('(') {
let disp_part = &remaining[..paren_pos];
let after_disp = &remaining[paren_pos..];
if let Some(val) = self.parse_immediate(disp_part) {
mem.displacement = val;
} else if !disp_part.is_empty() {
mem.symbol = Some(disp_part.to_string());
}
self.parse_memory_parens(after_disp, &mut mem);
} else {
if let Some(val) = self.parse_immediate(remaining) {
mem.displacement = val;
} else if !remaining.is_empty() {
mem.symbol = Some(remaining.to_string());
}
}
if let Some(ref base) = mem.base {
let name = X86RegisterInfo::get_asm_name(base.0);
if name == "rip" {
mem.is_rip_relative = true;
}
}
Some(mem)
}
fn parse_memory_parens(&self, text: &str, mem: &mut MemRef) {
let s = text.trim().trim_start_matches('(').trim_end_matches(')');
if s.is_empty() {
return;
}
let parts: Vec<&str> = s.split(',').collect();
match parts.len() {
1 => {
let reg_name = parts[0].trim();
if let Some(reg) = self.parse_register(reg_name) {
mem.base = Some(reg);
}
}
2 => {
let base_name = parts[0].trim();
let index_name = parts[1].trim();
if let Some(reg) = self.parse_register(base_name) {
mem.base = Some(reg);
}
if let Some(reg) = self.parse_register(index_name) {
mem.index = Some(reg);
mem.scale = 1;
} else {
if let Ok(s) = index_name.parse::<u8>() {
mem.scale = s;
}
}
}
3 => {
for (i, part) in parts.iter().enumerate() {
let p = part.trim();
match i {
0 => {
if let Some(reg) = self.parse_register(p) {
mem.base = Some(reg);
}
}
1 => {
if let Some(reg) = self.parse_register(p) {
mem.index = Some(reg);
}
}
2 => {
if let Ok(s) = p.parse::<u8>() {
mem.scale = s;
}
}
_ => {}
}
}
}
_ => {}
}
}
pub fn parse_memory_intel(&self, text: &str) -> Option<MemRef> {
let s = text.trim();
let mut mem = MemRef::default();
let inner = if let Some(ptr_pos) = s.find("PTR") {
let before = &s[..ptr_pos].trim();
if let Some(pt) = MemPtrQualifier::from_str(before) {
mem.ptr_qualifier = Some(pt);
}
s[ptr_pos + 3..].trim()
} else if s.starts_with('[') {
s
} else {
s
};
let inner = inner.trim_start_matches('[').trim_end_matches(']');
let inner = if let Some(rest) = inner.strip_prefix("fs:") {
mem.segment = Some(X86Reg(FS));
rest
} else if let Some(rest) = inner.strip_prefix("gs:") {
mem.segment = Some(X86Reg(GS));
rest
} else {
inner
};
let mut remaining = inner;
let mut sign: i64 = 1;
while !remaining.is_empty() {
remaining = remaining.trim();
if remaining.is_empty() {
break;
}
if let Some(rest) = remaining.strip_prefix('+') {
sign = 1;
remaining = rest;
} else if let Some(rest) = remaining.strip_prefix('-') {
sign = -1;
remaining = rest;
}
let end = remaining
.find(|c| c == '+' || c == '-')
.unwrap_or(remaining.len());
let component = remaining[..end].trim();
remaining = &remaining[end..];
if component.is_empty() {
continue;
}
if let Some(reg) = self.parse_register(component) {
if mem.base.is_none() {
mem.base = Some(reg);
} else if mem.index.is_none() {
mem.index = Some(reg);
}
continue;
}
if let Some(star_pos) = component.find('*') {
let index_part = component[..star_pos].trim();
let scale_part = component[star_pos + 1..].trim();
if let Some(reg) = self.parse_register(index_part) {
mem.index = Some(reg);
}
if let Ok(s) = scale_part.parse::<u8>() {
mem.scale = s;
}
continue;
}
if let Some(val) = self.parse_immediate(component) {
mem.displacement += sign * val;
continue;
}
mem.symbol = Some(component.to_string());
}
if let Some(ref base) = mem.base {
let name = X86RegisterInfo::get_asm_name(base.0);
if name == "rip" {
mem.is_rip_relative = true;
}
}
Some(mem)
}
pub fn parse_operand_att(&self, text: &str) -> Option<AsmOperand> {
let s = text.trim();
if s.is_empty() {
return None;
}
if s.starts_with('%') {
let reg_name = s.trim_start_matches('%');
if let Some(reg) = self.parse_register(s) {
let name = X86RegisterInfo::get_asm_name(reg.0);
if name.starts_with('k') {
if let Ok(num) = name[1..].parse::<u8>() {
return Some(AsmOperand::KReg(num));
}
}
return Some(AsmOperand::Reg(reg));
}
if reg_name.starts_with("st(") && reg_name.ends_with(')') {
let inner = ®_name[3..reg_name.len() - 1];
if let Ok(_num) = inner.parse::<u8>() {
return Some(AsmOperand::Reg(X86Reg(ST0))); }
}
return None;
}
if s.starts_with('$') {
if let Some(val) = self.parse_immediate(s) {
return Some(AsmOperand::Imm(val));
}
return None;
}
if s.contains('(') {
if let Some(mem) = self.parse_memory_att(s) {
return Some(AsmOperand::Mem(mem));
}
}
if s.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '.' || c == '@')
{
return Some(AsmOperand::Label(s.to_string()));
}
if let Some(val) = self.parse_immediate(s) {
return Some(AsmOperand::Imm(val));
}
None
}
pub fn parse_operand_intel(&self, text: &str) -> Option<AsmOperand> {
let s = text.trim();
if s.is_empty() {
return None;
}
if let Some(reg) = self.parse_register(s) {
let name = X86RegisterInfo::get_asm_name(reg.0);
if name.starts_with('k') {
if let Ok(num) = name[1..].parse::<u8>() {
return Some(AsmOperand::KReg(num));
}
}
return Some(AsmOperand::Reg(reg));
}
if s.contains('[') || s.contains("PTR") || s.contains("ptr") {
if let Some(mem) = self.parse_memory_intel(s) {
return Some(AsmOperand::Mem(mem));
}
}
if s.to_uppercase().starts_with("OFFSET") {
let symbol = s[6..].trim().trim_start_matches("FLAT").trim();
return Some(AsmOperand::Label(symbol.to_string()));
}
if s.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '.' || c == '@')
{
return Some(AsmOperand::Label(s.to_string()));
}
if let Some(val) = self.parse_immediate(s) {
return Some(AsmOperand::Imm(val));
}
None
}
pub fn parse_operand_list(&self, text: &str) -> Vec<AsmOperand> {
let mut operands = Vec::new();
let mut current = String::new();
let mut chars = text.chars().peekable();
let mut in_string = false;
let mut string_char = '"';
while let Some(c) = chars.next() {
if in_string {
current.push(c);
if c == string_char {
in_string = false;
}
} else if c == '"' || c == '\'' {
in_string = true;
string_char = c;
current.push(c);
} else if c == ',' {
let op = match self.syntax {
AsmSyntaxMode::ATT => self.parse_operand_att(¤t),
AsmSyntaxMode::Intel => self.parse_operand_intel(¤t),
};
if let Some(o) = op {
operands.push(o);
}
current.clear();
} else {
current.push(c);
}
}
if !current.trim().is_empty() {
let op = match self.syntax {
AsmSyntaxMode::ATT => self.parse_operand_att(¤t),
AsmSyntaxMode::Intel => self.parse_operand_intel(¤t),
};
if let Some(o) = op {
operands.push(o);
}
}
operands
}
}
pub struct X86DirectiveProcessor {
pub current_section: String,
pub current_offset: u64,
pub symbols: std::collections::HashMap<String, SymbolDef>,
pub data: Vec<u8>,
pub alignment: u32,
pub equates: std::collections::HashMap<String, i64>,
pub macros: std::collections::HashMap<String, Vec<String>>,
pub cond_stack: Vec<bool>,
pub file_info: Option<(String, u32)>,
pub cfi_state: CfiState,
pub syntax: AsmSyntaxMode,
}
#[derive(Debug, Clone)]
pub struct SymbolDef {
pub name: String,
pub value: u64,
pub section: String,
pub is_global: bool,
pub is_weak: bool,
pub is_local: bool,
pub is_function: bool,
pub size: Option<u64>,
pub typ: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct CfiState {
pub cfa_reg: Option<X86Reg>,
pub cfa_offset: Option<i64>,
pub reg_offsets: std::collections::HashMap<X86Reg, i64>,
pub is_active: bool,
}
impl X86DirectiveProcessor {
pub fn new(syntax: AsmSyntaxMode) -> Self {
X86DirectiveProcessor {
current_section: String::from(".text"),
current_offset: 0,
symbols: std::collections::HashMap::new(),
data: Vec::new(),
alignment: 1,
equates: std::collections::HashMap::new(),
macros: std::collections::HashMap::new(),
cond_stack: Vec::new(),
file_info: None,
cfi_state: CfiState::default(),
syntax,
}
}
pub fn process(&mut self, dir: &AsmDirective) -> Result<Vec<u8>, String> {
match dir {
AsmDirective::Section { name, .. } => {
self.current_section = name.clone();
Ok(Vec::new())
}
AsmDirective::Text => {
self.current_section = String::from(".text");
Ok(Vec::new())
}
AsmDirective::Data => {
self.current_section = String::from(".data");
Ok(Vec::new())
}
AsmDirective::Bss => {
self.current_section = String::from(".bss");
Ok(Vec::new())
}
AsmDirective::Rodata => {
self.current_section = String::from(".rodata");
Ok(Vec::new())
}
AsmDirective::Byte(values) => {
let mut bytes = Vec::new();
for v in values {
bytes.push((*v & 0xFF) as u8);
}
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Short(values) => {
let mut bytes = Vec::new();
for v in values {
bytes.extend_from_slice(&((*v as u16).to_le_bytes()));
}
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Long(values) => {
let mut bytes = Vec::new();
for v in values {
bytes.extend_from_slice(&((*v as u32).to_le_bytes()));
}
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Quad(values) => {
let mut bytes = Vec::new();
for v in values {
bytes.extend_from_slice(&((*v as u64).to_le_bytes()));
}
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Octa(values) => {
let mut bytes = Vec::new();
for v in values {
bytes.extend_from_slice(&((*v as u64).to_le_bytes()));
bytes.extend_from_slice(&[0u8; 8]); }
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Ascii(s) | AsmDirective::Asciz(s) => {
let mut bytes: Vec<u8> = s.bytes().collect();
if matches!(dir, AsmDirective::Asciz(_)) {
bytes.push(0);
}
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::String(s) => {
let mut bytes: Vec<u8> = s.bytes().collect();
bytes.push(0);
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Fill { count, value, size } => {
let sz = size.unwrap_or(1) as usize;
let val_bytes = match sz {
1 => vec![(*value & 0xFF) as u8],
2 => ((*value as u16).to_le_bytes()).to_vec(),
4 => ((*value as u32).to_le_bytes()).to_vec(),
8 => ((*value as u64).to_le_bytes()).to_vec(),
_ => vec![(*value & 0xFF) as u8],
};
let mut bytes = Vec::new();
for _ in 0..*count {
bytes.extend_from_slice(&val_bytes);
}
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Space(n) | AsmDirective::Skip(n) | AsmDirective::Zero(n) => {
let count = *n as usize;
let bytes = vec![0u8; count];
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Align {
alignment,
fill,
max,
} => {
let align = *alignment as u64;
let fill_val = fill.unwrap_or(0);
let max_bytes = max.unwrap_or(*alignment);
let cur = self.current_offset;
let misalignment = cur % align;
if misalignment != 0 {
let padding = (align - misalignment) as usize;
let actual_pad = padding.min(max_bytes as usize);
let bytes = vec![fill_val; actual_pad];
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
return Ok(bytes);
}
Ok(Vec::new())
}
AsmDirective::P2Align {
alignment,
fill,
max,
} => {
let align = 1u64 << alignment;
let fill_val = fill.unwrap_or(0);
let max_bytes = max.unwrap_or((1u32 << alignment).max(1));
let cur = self.current_offset;
let misalignment = cur % align;
if misalignment != 0 {
let padding = (align - misalignment) as usize;
let actual_pad = padding.min(max_bytes as usize);
let bytes = vec![fill_val; actual_pad];
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
return Ok(bytes);
}
Ok(Vec::new())
}
AsmDirective::BAlign { alignment, fill } => {
let align = *alignment as u64;
let fill_val = fill.unwrap_or(0);
let cur = self.current_offset;
let padding = if cur % align == 0 {
0
} else {
align - (cur % align)
} as usize;
let bytes = vec![fill_val; padding];
self.data.extend(&bytes);
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Org(addr) => {
let target = *addr as u64;
if target < self.current_offset {
return Err(format!(
"cannot .org backwards ({} -> {})",
self.current_offset, target
));
}
let padding = (target - self.current_offset) as usize;
let bytes = vec![0u8; padding];
self.data.extend(&bytes);
self.current_offset = target;
Ok(bytes)
}
AsmDirective::Globl(name) | AsmDirective::Global(name) => {
self.symbols
.entry(name.clone())
.and_modify(|s| {
s.is_global = true;
})
.or_insert(SymbolDef {
name: name.clone(),
value: self.current_offset,
section: self.current_section.clone(),
is_global: true,
is_weak: false,
is_local: false,
is_function: false,
size: None,
typ: None,
});
Ok(Vec::new())
}
AsmDirective::Local(name) => {
self.symbols
.entry(name.clone())
.and_modify(|s| {
s.is_local = true;
s.is_global = false;
})
.or_insert(SymbolDef {
name: name.clone(),
value: self.current_offset,
section: self.current_section.clone(),
is_global: false,
is_weak: false,
is_local: true,
is_function: false,
size: None,
typ: None,
});
Ok(Vec::new())
}
AsmDirective::Weak(name) => {
self.symbols
.entry(name.clone())
.and_modify(|s| {
s.is_weak = true;
})
.or_insert(SymbolDef {
name: name.clone(),
value: self.current_offset,
section: self.current_section.clone(),
is_global: false,
is_weak: true,
is_local: false,
is_function: false,
size: None,
typ: None,
});
Ok(Vec::new())
}
AsmDirective::Hidden(_name) | AsmDirective::Protected(_name) => {
Ok(Vec::new())
}
AsmDirective::Type { name, typ: t } => {
self.symbols
.entry(name.clone())
.and_modify(|s| {
s.typ = Some(t.clone());
if t.contains("function") || t.contains("@function") {
s.is_function = true;
}
})
.or_insert(SymbolDef {
name: name.clone(),
value: self.current_offset,
section: self.current_section.clone(),
is_global: false,
is_weak: false,
is_local: false,
is_function: t.contains("function"),
size: None,
typ: Some(t.clone()),
});
Ok(Vec::new())
}
AsmDirective::Size { name, size: sz } => {
if let Some(sym) = self.symbols.get_mut(name) {
if let Ok(s) = sz.parse::<u64>() {
sym.size = Some(s);
}
}
Ok(Vec::new())
}
AsmDirective::Comm {
name,
size,
alignment,
} => {
self.symbols
.entry(name.clone())
.and_modify(|s| {
s.size = Some(*size as u64);
s.is_global = true;
})
.or_insert(SymbolDef {
name: name.clone(),
value: self.current_offset,
section: String::from(".bss"),
is_global: true,
is_weak: false,
is_local: false,
is_function: false,
size: Some(*size as u64),
typ: None,
});
let count = *size as usize;
let bytes = vec![0u8; count];
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::LComm {
name,
size,
alignment: _,
} => {
self.symbols
.entry(name.clone())
.and_modify(|s| {
s.size = Some(*size as u64);
s.is_local = true;
})
.or_insert(SymbolDef {
name: name.clone(),
value: self.current_offset,
section: String::from(".bss"),
is_global: false,
is_weak: false,
is_local: true,
is_function: false,
size: Some(*size as u64),
typ: None,
});
let count = *size as usize;
let bytes = vec![0u8; count];
self.current_offset += bytes.len() as u64;
Ok(bytes)
}
AsmDirective::Equ { name, value } | AsmDirective::Set { name, value } => {
if let Ok(v) = value.parse::<i64>() {
self.equates.insert(name.clone(), v);
}
Ok(Vec::new())
}
AsmDirective::Macro { name, body } => {
self.macros.insert(name.clone(), body.clone());
Ok(Vec::new())
}
AsmDirective::EndM | AsmDirective::EndR => Ok(Vec::new()),
AsmDirective::RepT { count, body } => {
let mut bytes = Vec::new();
for _ in 0..*count {
for line in body {
bytes.extend_from_slice(line.as_bytes());
}
}
Ok(bytes)
}
AsmDirective::IrP {
param: _,
values: _,
body,
} => {
let mut bytes = Vec::new();
for line in body {
bytes.extend_from_slice(line.as_bytes());
}
Ok(bytes)
}
AsmDirective::IrPC {
param: _,
chars: _,
body,
} => {
let mut bytes = Vec::new();
for line in body {
bytes.extend_from_slice(line.as_bytes());
}
Ok(bytes)
}
AsmDirective::If(cond) => {
let result = self.evaluate_condition(cond);
self.cond_stack.push(result);
Ok(Vec::new())
}
AsmDirective::Else => {
if let Some(top) = self.cond_stack.last_mut() {
*top = !*top;
}
Ok(Vec::new())
}
AsmDirective::EndIf => {
self.cond_stack.pop();
Ok(Vec::new())
}
AsmDirective::IfDef(name) => {
let result = self.symbols.contains_key(name) || self.equates.contains_key(name);
self.cond_stack.push(result);
Ok(Vec::new())
}
AsmDirective::IfNDef(name) => {
let result = !self.symbols.contains_key(name) && !self.equates.contains_key(name);
self.cond_stack.push(result);
Ok(Vec::new())
}
AsmDirective::CfiStartProc => {
self.cfi_state.is_active = true;
self.cfi_state = CfiState::default();
self.cfi_state.is_active = true;
Ok(Vec::new())
}
AsmDirective::CfiEndProc => {
self.cfi_state.is_active = false;
Ok(Vec::new())
}
AsmDirective::CfiDefCfa { reg, offset } => {
if self.cfi_state.is_active {
if let Some(r) =
X86OperandParser::new(self.syntax).parse_register(&format!("%{}", reg))
{
self.cfi_state.cfa_reg = Some(r);
self.cfi_state.cfa_offset = Some(*offset);
}
}
Ok(Vec::new())
}
AsmDirective::CfiOffset { reg, offset } => {
if self.cfi_state.is_active {
if let Some(r) =
X86OperandParser::new(self.syntax).parse_register(&format!("%{}", reg))
{
self.cfi_state.reg_offsets.insert(r, *offset);
}
}
Ok(Vec::new())
}
AsmDirective::File { name } => {
self.file_info = Some((name.clone(), 1));
Ok(Vec::new())
}
AsmDirective::Loc {
file,
line,
column: _,
} => {
self.file_info = Some((format!("file_{}", file), *line));
Ok(Vec::new())
}
AsmDirective::Line(n) => {
if let Some(ref mut fi) = self.file_info {
fi.1 = *n;
}
Ok(Vec::new())
}
AsmDirective::Ident(s) => {
let _ = s;
Ok(Vec::new())
}
AsmDirective::End => Ok(Vec::new()),
}
}
fn evaluate_condition(&self, cond: &str) -> bool {
let cond = cond.trim();
if cond == "1" || cond == "true" || cond == "TRUE" {
return true;
}
if cond == "0" || cond == "false" || cond == "FALSE" {
return false;
}
if cond.starts_with("defined(") && cond.ends_with(')') {
let sym = &cond[8..cond.len() - 1];
return self.symbols.contains_key(sym) || self.equates.contains_key(sym);
}
if let Some(&val) = self.equates.get(cond) {
return val != 0;
}
for op in &["==", "!=", "<=", ">=", "<", ">"] {
if let Some(pos) = cond.find(op) {
let left = cond[..pos].trim();
let right = cond[pos + op.len()..].trim();
let l_val = self.equates.get(left).copied().unwrap_or(0);
let r_val = self.equates.get(right).copied().unwrap_or(0);
return match *op {
"==" => l_val == r_val,
"!=" => l_val != r_val,
"<=" => l_val <= r_val,
">=" => l_val >= r_val,
"<" => l_val < r_val,
">" => l_val > r_val,
_ => false,
};
}
}
false
}
pub fn define_symbol(&mut self, name: &str) {
self.symbols
.entry(name.to_string())
.and_modify(|s| {
s.value = self.current_offset;
})
.or_insert(SymbolDef {
name: name.to_string(),
value: self.current_offset,
section: self.current_section.clone(),
is_global: false,
is_weak: false,
is_local: name.starts_with(".L"),
is_function: false,
size: None,
typ: None,
});
}
pub fn get_offset(&self) -> u64 {
self.current_offset
}
pub fn reset(&mut self) {
self.data.clear();
self.current_offset = 0;
self.current_section = String::from(".text");
self.symbols.clear();
self.equates.clear();
self.macros.clear();
self.cond_stack.clear();
self.cfi_state = CfiState::default();
self.alignment = 1;
self.file_info = None;
}
}
pub struct X86FixupHandler {
pub fixups: Vec<X86Fixup>,
pub emit_got_plt: bool,
pub pic: bool,
}
#[derive(Debug, Clone)]
pub struct X86Fixup {
pub offset: u64,
pub kind: X86FixupKind,
pub symbol: Option<String>,
pub addend: i64,
pub branch_size: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86FixupKind {
Rel8,
Rel32,
Abs64,
Abs32,
RipRel4,
GotPcRel,
Plt32,
GotOff,
TlsGd,
TlsLd,
GotTpOff,
TpOff,
DtpOff,
}
impl X86FixupKind {
pub fn size_bytes(&self) -> u32 {
match self {
X86FixupKind::Rel8 => 1,
X86FixupKind::Rel32
| X86FixupKind::RipRel4
| X86FixupKind::GotPcRel
| X86FixupKind::Plt32
| X86FixupKind::GotOff
| X86FixupKind::TlsGd
| X86FixupKind::TlsLd
| X86FixupKind::GotTpOff
| X86FixupKind::TpOff
| X86FixupKind::DtpOff
| X86FixupKind::Abs32 => 4,
X86FixupKind::Abs64 => 8,
}
}
pub fn is_pc_relative(&self) -> bool {
matches!(
self,
X86FixupKind::Rel8
| X86FixupKind::Rel32
| X86FixupKind::RipRel4
| X86FixupKind::GotPcRel
| X86FixupKind::Plt32
)
}
pub fn to_mc_fixup_kind(&self) -> MCFixupKind {
match self {
X86FixupKind::Rel8 => MCFixupKind::PcRel8,
X86FixupKind::Rel32 => MCFixupKind::PcRel32,
X86FixupKind::Abs32 => MCFixupKind::Abs32,
X86FixupKind::Abs64 => MCFixupKind::Abs64,
X86FixupKind::RipRel4 => MCFixupKind::RelocRipRel4Byte,
X86FixupKind::GotPcRel => MCFixupKind::RelocGotPcRel,
X86FixupKind::Plt32 => MCFixupKind::RelocPlt32,
X86FixupKind::GotOff => MCFixupKind::RelocGotOff,
X86FixupKind::TlsGd => MCFixupKind::RelocTlsGd,
X86FixupKind::TlsLd => MCFixupKind::RelocTlsLd,
X86FixupKind::GotTpOff => MCFixupKind::RelocGotTpOff,
X86FixupKind::TpOff => MCFixupKind::RelocTpOff32,
X86FixupKind::DtpOff => MCFixupKind::RelocDtpOff32,
}
}
}
impl X86FixupHandler {
pub fn new(pic: bool) -> Self {
X86FixupHandler {
fixups: Vec::new(),
emit_got_plt: pic,
pic,
}
}
pub fn add_branch_fixup(&mut self, offset: u64, target: Option<String>, is_short: bool) {
let kind = if is_short {
X86FixupKind::Rel8
} else {
X86FixupKind::Rel32
};
self.fixups.push(X86Fixup {
offset,
kind,
symbol: target,
addend: -4, branch_size: if is_short { 2 } else { 5 },
});
}
pub fn add_got_plt_fixup(&mut self, offset: u64, symbol: String) {
if self.emit_got_plt {
self.fixups.push(X86Fixup {
offset,
kind: X86FixupKind::GotPcRel,
symbol: Some(symbol),
addend: -4,
branch_size: 7, });
}
}
pub fn relax_branches(&self, data: &[u8]) -> Result<Vec<u8>, String> {
let mut result = data.to_vec();
let mut offset_shift: i64 = 0;
for fixup in self.fixups.iter() {
let adjusted_offset = (fixup.offset as i64 + offset_shift) as usize;
if adjusted_offset + fixup.branch_size as usize > result.len() {
continue;
}
match fixup.kind {
X86FixupKind::Rel8 => {
let target_dist = fixup.addend + fixup.branch_size as i64;
if target_dist < -128 || target_dist > 127 {
let opcode = result[adjusted_offset];
if opcode >= 0x70 && opcode <= 0x7F {
let new_opcode = 0x80 + (opcode - 0x70);
result[adjusted_offset] = 0x0F;
result.insert(adjusted_offset + 1, new_opcode);
for _ in 0..3 {
result.insert(adjusted_offset + 2, 0);
}
offset_shift += 4; }
}
}
_ => {}
}
}
Ok(result)
}
pub fn apply_relocations(
&self,
_data: &mut [u8],
_symbol_addresses: &std::collections::HashMap<String, u64>,
) -> Result<(), String> {
Ok(())
}
pub fn clear(&mut self) {
self.fixups.clear();
}
}
pub struct X86AsmParser {
pub syntax: AsmSyntaxMode,
pub mnemonic_parser: X86MnemonicParser,
pub operand_parser: X86OperandParser,
pub loc: SourceLocation,
}
impl X86AsmParser {
pub fn new(syntax: AsmSyntaxMode) -> Self {
X86AsmParser {
mnemonic_parser: X86MnemonicParser::new(syntax),
operand_parser: X86OperandParser::new(syntax),
syntax,
loc: SourceLocation { line: 0, column: 0 },
}
}
pub fn parse_line(&mut self, line: &str) -> Option<AsmLine> {
let trimmed = line.trim();
if trimmed.is_empty() {
return Some(AsmLine::Empty);
}
if trimmed.starts_with('#') || trimmed.starts_with("//") || trimmed.starts_with(';') {
return Some(AsmLine::Comment(trimmed.to_string()));
}
if let Some(label_pos) = trimmed.find(':') {
let label_part = &trimmed[..label_pos];
let rest = trimmed[label_pos + 1..].trim();
if !label_part.contains('"') && !label_part.contains('\'') && !label_part.contains(' ')
{
if rest.is_empty() || rest.starts_with('#') || rest.starts_with("//") {
return Some(AsmLine::Label(label_part.to_string()));
}
let _ = label_part;
let rest_parsed = self.parse_line(rest);
return Some(AsmLine::Label(label_part.to_string()));
}
}
if trimmed.starts_with('.') {
return self.parse_directive(trimmed);
}
self.parse_instruction(trimmed)
}
fn parse_directive(&self, line: &str) -> Option<AsmLine> {
let mut parts = line.splitn(2, |c: char| c.is_whitespace());
let directive = parts.next()?.to_lowercase();
let args = parts.next().unwrap_or("").trim().to_string();
let dir = match directive.as_str() {
".section" => {
let mut args_parts = args.splitn(2, ',');
let name = args_parts
.next()
.unwrap_or("")
.trim()
.trim_matches('"')
.to_string();
let flags = args_parts
.next()
.unwrap_or("")
.trim()
.split(',')
.map(|s| s.trim().trim_matches('"').to_string())
.collect();
AsmDirective::Section { name, flags }
}
".text" => AsmDirective::Text,
".data" => AsmDirective::Data,
".bss" => AsmDirective::Bss,
".rodata" => AsmDirective::Rodata,
".byte" => {
let values = self.parse_data_values(&args);
AsmDirective::Byte(values)
}
".short" | ".value" | ".2byte" => {
let values = self.parse_data_values(&args);
AsmDirective::Short(values)
}
".long" | ".int" | ".4byte" => {
let values = self.parse_data_values(&args);
AsmDirective::Long(values)
}
".quad" | ".8byte" => {
let values = self.parse_data_values(&args);
AsmDirective::Quad(values)
}
".octa" | ".16byte" => {
let values = self.parse_data_values(&args);
AsmDirective::Octa(values)
}
".ascii" => AsmDirective::Ascii(self.unquote(&args)),
".asciz" => AsmDirective::Asciz(self.unquote(&args)),
".string" => AsmDirective::String(self.unquote(&args)),
".fill" => {
let vals: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
let count = vals.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
let value = vals.get(1).and_then(|s| s.parse().ok()).unwrap_or(0);
let size = vals.get(2).and_then(|s| s.parse().ok());
AsmDirective::Fill { count, value, size }
}
".space" => AsmDirective::Space(args.parse().unwrap_or(0)),
".skip" => AsmDirective::Skip(args.parse().unwrap_or(0)),
".align" => {
let vals: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
AsmDirective::Align {
alignment: vals.get(0).and_then(|s| s.parse().ok()).unwrap_or(1),
fill: vals.get(1).and_then(|s| parse_fill_value(s)),
max: vals.get(2).and_then(|s| s.parse().ok()),
}
}
".p2align" => {
let vals: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
AsmDirective::P2Align {
alignment: vals.get(0).and_then(|s| s.parse().ok()).unwrap_or(1),
fill: vals.get(1).and_then(|s| parse_fill_value(s)),
max: vals.get(2).and_then(|s| s.parse().ok()),
}
}
".balign" => {
let vals: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
AsmDirective::BAlign {
alignment: vals.get(0).and_then(|s| s.parse().ok()).unwrap_or(1),
fill: vals.get(1).and_then(|s| parse_fill_value(s)),
}
}
".org" => AsmDirective::Org(args.parse().unwrap_or(0)),
".globl" | ".global" => AsmDirective::Global(args.trim().to_string()),
".local" => AsmDirective::Local(args.trim().to_string()),
".weak" => AsmDirective::Weak(args.trim().to_string()),
".hidden" => AsmDirective::Hidden(args.trim().to_string()),
".protected" => AsmDirective::Protected(args.trim().to_string()),
".type" => {
let mut parts = args.splitn(2, ',');
AsmDirective::Type {
name: parts.next().unwrap_or("").trim().to_string(),
typ: parts.next().unwrap_or("").trim().to_string(),
}
}
".size" => {
let mut parts = args.splitn(2, ',');
AsmDirective::Size {
name: parts.next().unwrap_or("").trim().to_string(),
size: parts.next().unwrap_or("").trim().to_string(),
}
}
".comm" => {
let vals: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
AsmDirective::Comm {
name: vals.get(0).unwrap_or(&"").to_string(),
size: vals.get(1).and_then(|s| s.parse().ok()).unwrap_or(0),
alignment: vals.get(2).and_then(|s| s.parse().ok()),
}
}
".lcomm" => {
let vals: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
AsmDirective::LComm {
name: vals.get(0).unwrap_or(&"").to_string(),
size: vals.get(1).and_then(|s| s.parse().ok()).unwrap_or(0),
alignment: vals.get(2).and_then(|s| s.parse().ok()),
}
}
".zero" => AsmDirective::Zero(args.parse().unwrap_or(0)),
".equ" => {
let mut parts = args.splitn(2, ',');
AsmDirective::Equ {
name: parts.next().unwrap_or("").trim().to_string(),
value: parts.next().unwrap_or("").trim().to_string(),
}
}
".set" => {
let mut parts = args.splitn(2, ',');
AsmDirective::Set {
name: parts.next().unwrap_or("").trim().to_string(),
value: parts.next().unwrap_or("").trim().to_string(),
}
}
".macro" => AsmDirective::Macro {
name: "unnamed".to_string(),
body: Vec::new(),
}, ".endm" => AsmDirective::EndM,
".rept" => AsmDirective::RepT {
count: args.parse().unwrap_or(0),
body: Vec::new(),
},
".endr" => AsmDirective::EndR,
".irp" => AsmDirective::IrP {
param: String::new(),
values: Vec::new(),
body: Vec::new(),
},
".irpc" => AsmDirective::IrPC {
param: String::new(),
chars: String::new(),
body: Vec::new(),
},
".if" => AsmDirective::If(args.trim().to_string()),
".else" => AsmDirective::Else,
".endif" => AsmDirective::EndIf,
".ifdef" => AsmDirective::IfDef(args.trim().to_string()),
".ifndef" => AsmDirective::IfNDef(args.trim().to_string()),
".cfi_startproc" => AsmDirective::CfiStartProc,
".cfi_endproc" => AsmDirective::CfiEndProc,
".cfi_def_cfa" => {
let vals: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
AsmDirective::CfiDefCfa {
reg: vals.get(0).unwrap_or(&"").to_string(),
offset: vals.get(1).and_then(|s| s.parse().ok()).unwrap_or(0),
}
}
".cfi_offset" => {
let vals: Vec<&str> = args.split(',').map(|s| s.trim()).collect();
AsmDirective::CfiOffset {
reg: vals.get(0).unwrap_or(&"").to_string(),
offset: vals.get(1).and_then(|s| s.parse().ok()).unwrap_or(0),
}
}
".file" => AsmDirective::File {
name: args.trim().trim_matches('"').to_string(),
},
".loc" => {
let vals: Vec<&str> = args.split_whitespace().collect();
AsmDirective::Loc {
file: vals.get(0).and_then(|s| s.parse().ok()).unwrap_or(1),
line: vals.get(1).and_then(|s| s.parse().ok()).unwrap_or(0),
column: vals.get(2).and_then(|s| s.parse().ok()),
}
}
".line" => AsmDirective::Line(args.parse().unwrap_or(0)),
".ident" => AsmDirective::Ident(self.unquote(&args)),
".end" => AsmDirective::End,
_ => return None,
};
Some(AsmLine::Directive(dir))
}
fn parse_data_values(&self, args: &str) -> Vec<i64> {
args.split(',')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(|s| {
if s.starts_with("0x") || s.starts_with("0X") {
i64::from_str_radix(&s[2..], 16).unwrap_or(0)
} else if s.starts_with("0b") || s.starts_with("0B") {
i64::from_str_radix(&s[2..], 2).unwrap_or(0)
} else {
s.parse().unwrap_or(0)
}
})
.collect()
}
fn unquote(&self, s: &str) -> String {
let s = s.trim();
if (s.starts_with('"') && s.ends_with('"')) || (s.starts_with('\'') && s.ends_with('\'')) {
s[1..s.len() - 1].to_string()
} else {
s.to_string()
}
}
fn parse_instruction(&self, line: &str) -> Option<AsmLine> {
let trimmed = line.trim();
let mut tokens = trimmed.splitn(2, |c: char| c.is_whitespace() || c == '\t');
let mnemonic = tokens.next()?.to_string();
let operands_str = tokens.next().unwrap_or("").trim();
let (mnemonic_base, suffixes) = if self.syntax == AsmSyntaxMode::ATT {
let mut base = mnemonic.clone();
let mut suffixes = Vec::new();
let known = &["b", "w", "l", "q", "s", "d", "ps", "pd", "ss", "sd", "t"];
let mut found = true;
while found {
found = false;
for sfx in known {
if base.ends_with(sfx) && base.len() > sfx.len() {
let before = &base[..base.len() - sfx.len()];
if !before.is_empty()
&& (before.ends_with(|c: char| c.is_alphabetic())
|| before == "cmp"
|| before == "mov"
|| before == "add"
|| before == "sub")
{
suffixes.push(sfx.to_string());
base = before.to_string();
found = true;
break;
}
}
}
}
suffixes.reverse();
(base, suffixes)
} else {
(mnemonic, Vec::new())
};
let prefixes = vec!["rep", "repe", "repne", "repz", "repnz", "lock"];
let mut prefix_list = Vec::new();
let mut final_mnemonic = mnemonic_base.clone();
for pfx in &prefixes {
if final_mnemonic == *pfx {
prefix_list.push(pfx.to_string());
if !operands_str.is_empty() {
let mut op_parts = operands_str.splitn(2, |c: char| c.is_whitespace());
final_mnemonic = op_parts.next().unwrap_or("").to_string();
let rest = op_parts.next().unwrap_or("");
return Some(AsmLine::Instruction {
mnemonic: format!("{} {}", prefix_list.join(" "), final_mnemonic),
operands: self.split_operand_string(rest),
suffixes: suffixes.clone(),
});
}
break;
}
}
Some(AsmLine::Instruction {
mnemonic: final_mnemonic,
operands: self.split_operand_string(operands_str),
suffixes,
})
}
fn split_operand_string(&self, s: &str) -> Vec<String> {
let mut operands = Vec::new();
let mut current = String::new();
let mut depth_paren = 0;
let mut depth_bracket = 0;
let mut in_string = false;
let mut string_char = '"';
for c in s.chars() {
match c {
'"' | '\'' if !in_string => {
in_string = true;
string_char = c;
current.push(c);
}
c if in_string && c == string_char => {
in_string = false;
current.push(c);
}
'(' if !in_string => {
depth_paren += 1;
current.push(c);
}
')' if !in_string => {
depth_paren -= 1;
current.push(c);
}
'[' if !in_string => {
depth_bracket += 1;
current.push(c);
}
']' if !in_string => {
depth_bracket -= 1;
current.push(c);
}
',' if !in_string && depth_paren == 0 && depth_bracket == 0 => {
if !current.trim().is_empty() {
operands.push(current.trim().to_string());
}
current.clear();
}
_ => {
current.push(c);
}
}
}
if !current.trim().is_empty() {
operands.push(current.trim().to_string());
}
operands
}
pub fn parse_full_instruction(&mut self, line: &str) -> Option<ParsedInstruction> {
let parsed = self.parse_line(line)?;
match parsed {
AsmLine::Instruction {
mnemonic,
operands,
suffixes,
} => {
let size_suffix = if let Some(s) = suffixes.first() {
SizeSuffix::from_att_suffix(s)
} else {
None
};
let opcode = match self.syntax {
AsmSyntaxMode::ATT => self
.mnemonic_parser
.parse_att(&mnemonic)
.map(|(_, _, op)| op),
AsmSyntaxMode::Intel => self
.mnemonic_parser
.parse_intel(&mnemonic)
.map(|(_, op)| op),
}?;
let parsed_operands: Vec<AsmOperand> = match self.syntax {
AsmSyntaxMode::ATT => operands
.iter()
.filter_map(|op| self.operand_parser.parse_operand_att(op))
.collect(),
AsmSyntaxMode::Intel => operands
.iter()
.filter_map(|op| self.operand_parser.parse_operand_intel(op))
.collect(),
};
let operands = if self.syntax == AsmSyntaxMode::ATT && parsed_operands.len() >= 2 {
let mut ops = parsed_operands;
if ops.len() == 2 {
ops.swap(0, 1);
}
if ops.len() == 3 {
let dst = ops.pop().unwrap();
ops.insert(0, dst);
}
ops
} else {
parsed_operands
};
Some(ParsedInstruction {
mnemonic: opcode.mnemonic().to_string(),
opcode,
operands,
size_suffix,
encoding_form: opcode.encoding_form(),
is_branch: opcode.is_jcc(),
is_call: false,
loc: self.loc,
})
}
_ => None,
}
}
}
fn parse_fill_value(s: &str) -> Option<u8> {
let s = s.trim();
if s.starts_with("0x") || s.starts_with("0X") {
u8::from_str_radix(&s[2..], 16).ok()
} else {
s.parse().ok()
}
}
#[derive(Debug, Clone)]
pub struct AssemblerConfig {
pub syntax: AsmSyntaxMode,
pub pic: bool,
pub cpu: String,
pub features: Vec<String>,
pub output_format: OutputFormat,
pub relaxation: bool,
pub debug_info: bool,
}
impl Default for AssemblerConfig {
fn default() -> Self {
AssemblerConfig {
syntax: AsmSyntaxMode::ATT,
pic: false,
cpu: String::from("x86-64"),
features: Vec::new(),
output_format: OutputFormat::ElF,
relaxation: true,
debug_info: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
Binary,
ElF,
MachO,
Coff,
}
#[derive(Debug)]
pub struct AssemblyResult {
pub data: Vec<u8>,
pub fixups: Vec<X86Fixup>,
pub symbols: std::collections::HashMap<String, SymbolDef>,
pub sections: Vec<SectionInfo>,
pub instruction_count: usize,
pub warnings: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct SectionInfo {
pub name: String,
pub offset: u64,
pub size: u64,
pub alignment: u32,
pub flags: Vec<String>,
}
pub struct X86MCFullAssembler {
config: AssemblerConfig,
parser: X86AsmParser,
directive_processor: X86DirectiveProcessor,
fixup_handler: X86FixupHandler,
full_info: X86FullInstrInfo,
instr_info: X86InstrInfo,
}
impl X86MCFullAssembler {
pub fn new(config: AssemblerConfig) -> Self {
let syntax = config.syntax;
X86MCFullAssembler {
parser: X86AsmParser::new(syntax),
directive_processor: X86DirectiveProcessor::new(syntax),
fixup_handler: X86FixupHandler::new(config.pic),
full_info: X86FullInstrInfo::new(),
instr_info: X86InstrInfo::new(),
config,
}
}
pub fn default_att() -> Self {
Self::new(AssemblerConfig::default())
}
pub fn default_intel() -> Self {
Self::new(AssemblerConfig {
syntax: AsmSyntaxMode::Intel,
..AssemblerConfig::default()
})
}
pub fn assemble(&mut self, source: &str) -> Result<AssemblyResult, String> {
let mut result = AssemblyResult {
data: Vec::new(),
fixups: Vec::new(),
symbols: std::collections::HashMap::new(),
sections: Vec::new(),
instruction_count: 0,
warnings: Vec::new(),
};
let lines: Vec<&str> = source.lines().collect();
let mut i = 0;
let mut current_section_offset = 0u64;
let mut section_data: Vec<u8> = Vec::new();
while i < lines.len() {
let line = lines[i];
self.parser.loc = SourceLocation {
line: (i + 1) as u32,
column: 0,
};
let parsed = match self.parser.parse_line(line) {
Some(p) => p,
None => {
result
.warnings
.push(format!("line {}: could not parse: {}", i + 1, line));
i += 1;
continue;
}
};
match parsed {
AsmLine::Empty | AsmLine::Comment(_) => {
i += 1;
continue;
}
AsmLine::Label(name) => {
self.directive_processor.define_symbol(&name);
if let Some(rest) = line.split(':').nth(1) {
let rest_trimmed = rest.trim();
if !rest_trimmed.is_empty()
&& !rest_trimmed.starts_with('#')
&& !rest_trimmed.starts_with("//")
{
if let Some(inst) = self.parser.parse_full_instruction(rest_trimmed) {
match self.encode_instruction(&inst) {
Ok(bytes) => {
section_data.extend(&bytes);
current_section_offset += bytes.len() as u64;
result.instruction_count += 1;
if inst.is_branch {
self.fixup_handler.add_branch_fixup(
current_section_offset - bytes.len() as u64,
None,
false,
);
}
}
Err(e) => {
result.warnings.push(format!(
"line {}: encode error: {}",
i + 1,
e
));
}
}
}
}
}
}
AsmLine::Directive(dir) => {
let old_section = self.directive_processor.current_section.clone();
match self.directive_processor.process(&dir) {
Ok(bytes) => {
section_data.extend(&bytes);
let new_section = self.directive_processor.current_section.clone();
if old_section != new_section {
if !section_data.is_empty() || old_section != new_section {
result.sections.push(SectionInfo {
name: old_section,
offset: current_section_offset - section_data.len() as u64,
size: section_data.len() as u64,
alignment: self.directive_processor.alignment,
flags: Vec::new(),
});
current_section_offset += section_data.len() as u64;
result.data.extend(§ion_data);
section_data.clear();
}
}
current_section_offset += bytes.len() as u64;
}
Err(e) => {
result
.warnings
.push(format!("line {}: directive error: {}", i + 1, e));
}
}
}
AsmLine::Instruction { .. } => {
if let Some(inst) = self.parser.parse_full_instruction(line) {
match self.encode_instruction(&inst) {
Ok(bytes) => {
section_data.extend(&bytes);
current_section_offset += bytes.len() as u64;
result.instruction_count += 1;
if inst.is_branch {
self.fixup_handler.add_branch_fixup(
current_section_offset - bytes.len() as u64,
None,
false,
);
}
}
Err(e) => {
result.warnings.push(format!(
"line {}: encode error: {}",
i + 1,
e
));
}
}
} else {
result.warnings.push(format!(
"line {}: unrecognized instruction: {}",
i + 1,
line
));
}
}
}
i += 1;
}
if !section_data.is_empty() {
result.sections.push(SectionInfo {
name: self.directive_processor.current_section.clone(),
offset: current_section_offset - section_data.len() as u64,
size: section_data.len() as u64,
alignment: self.directive_processor.alignment,
flags: Vec::new(),
});
}
result.data.extend(§ion_data);
result.fixups = self.fixup_handler.fixups.clone();
result.symbols = self.directive_processor.symbols.clone();
if self.config.relaxation && !result.fixups.is_empty() {
let relaxed = self.fixup_handler.relax_branches(&result.data)?;
result.data = relaxed;
}
Ok(result)
}
pub fn encode_instruction(&self, inst: &ParsedInstruction) -> Result<Vec<u8>, String> {
let encoding_opt = self.full_info.get(inst.opcode);
let mut bytes = Vec::new();
let form = encoding_opt
.map(|e| e.encoding_form)
.unwrap_or(EncodingForm::None);
match form {
EncodingForm::Legacy | EncodingForm::LegacyREX => {
if let Some(e) = encoding_opt {
self.encode_legacy(inst, e, &mut bytes)?;
}
}
EncodingForm::VEX | EncodingForm::VEX3Byte => {
if let Some(e) = encoding_opt {
self.encode_vex(inst, e, &mut bytes)?;
}
}
EncodingForm::EVEX => {
if let Some(e) = encoding_opt {
self.encode_evex(inst, e, &mut bytes)?;
}
}
EncodingForm::XOP => {
if let Some(e) = encoding_opt {
self.encode_xop(inst, e, &mut bytes)?;
}
}
EncodingForm::None => {
self.encode_implicit(inst, &mut bytes)?;
}
}
Ok(bytes)
}
fn encode_legacy(
&self,
inst: &ParsedInstruction,
_encoding: &InstrEncodingInfo,
bytes: &mut Vec<u8>,
) -> Result<(), String> {
let opcode = inst.opcode;
let base = opcode.base_opcode();
match opcode {
X86FullOpcode::MOVrr if inst.mnemonic == "nop" => {
bytes.push(0x90);
return Ok(());
}
_ if inst.mnemonic == "ret" => {
bytes.push(0xC3);
return Ok(());
}
_ if inst.mnemonic == "push" && inst.operands.len() == 1 => {
if let Some(AsmOperand::Reg(reg)) = inst.operands.first() {
let enc = self.get_reg_encoding(*reg);
if enc < 8 {
bytes.push(0x50 + enc);
return Ok(());
}
bytes.push(0x41); bytes.push(0x50 + (enc - 8));
return Ok(());
}
}
_ if inst.mnemonic == "pop" && inst.operands.len() == 1 => {
if let Some(AsmOperand::Reg(reg)) = inst.operands.first() {
let enc = self.get_reg_encoding(*reg);
if enc < 8 {
bytes.push(0x58 + enc);
return Ok(());
}
bytes.push(0x41); bytes.push(0x58 + (enc - 8));
return Ok(());
}
}
_ if inst.mnemonic == "mov" && inst.operands.len() == 2 => {
if let (AsmOperand::Reg(dst), AsmOperand::Reg(src)) =
(&inst.operands[0], &inst.operands[1])
{
let dst_enc = self.get_reg_encoding(*dst);
let src_enc = self.get_reg_encoding(*src);
let rex = if dst_enc >= 8 || src_enc >= 8 {
0x40 | 0x08 | 0x04 } else {
0
};
if rex != 0 {
bytes.push(rex);
}
bytes.push(0x89);
let modrm = 0xC0 | ((src_enc & 7) << 3) | (dst_enc & 7);
bytes.push(modrm);
return Ok(());
}
}
_ if ["add", "sub", "and", "or", "xor", "cmp"].contains(&inst.mnemonic.as_str())
&& inst.operands.len() == 2 =>
{
if let (AsmOperand::Reg(dst), AsmOperand::Reg(src)) =
(&inst.operands[0], &inst.operands[1])
{
let base_op = match inst.mnemonic.as_str() {
"add" => 0x01,
"sub" => 0x29,
"and" => 0x21,
"or" => 0x09,
"xor" => 0x31,
"cmp" => 0x39,
_ => 0x00,
};
let dst_enc = self.get_reg_encoding(*dst);
let src_enc = self.get_reg_encoding(*src);
let rex = if dst_enc >= 8 || src_enc >= 8 {
0x48 } else {
0
};
if rex != 0 {
bytes.push(rex);
}
bytes.push(base_op);
let modrm = 0xC0 | ((src_enc & 7) << 3) | (dst_enc & 7);
bytes.push(modrm);
return Ok(());
}
}
_ if opcode.is_jcc() => {
let jcc_base = match inst.mnemonic.as_str() {
"jo" => 0x70,
"jno" => 0x71,
"jb" | "jc" | "jnae" => 0x72,
"jae" | "jnb" | "jnc" => 0x73,
"je" | "jz" => 0x74,
"jne" | "jnz" => 0x75,
"jbe" | "jna" => 0x76,
"ja" | "jnbe" => 0x77,
"js" => 0x78,
"jns" => 0x79,
"jp" | "jpe" => 0x7A,
"jnp" | "jpo" => 0x7B,
"jl" | "jnge" => 0x7C,
"jge" | "jnl" => 0x7D,
"jle" | "jng" => 0x7E,
"jg" | "jnle" => 0x7F,
_ => 0x74, };
bytes.push(jcc_base);
bytes.push(0x00); return Ok(());
}
_ => {
if base <= 0xFF {
bytes.push(base as u8);
}
if opcode.requires_modrm() {
let modrm = if inst.operands.len() >= 2 {
if let (AsmOperand::Reg(dst), AsmOperand::Reg(src)) =
(&inst.operands[0], &inst.operands[1])
{
let dst_enc = self.get_reg_encoding(*dst) & 7;
let src_enc = self.get_reg_encoding(*src) & 7;
0xC0 | ((src_enc) << 3) | dst_enc
} else {
0xC0
}
} else if inst.operands.len() == 1 {
if let AsmOperand::Reg(reg) = &inst.operands[0] {
let enc = self.get_reg_encoding(*reg) & 7;
0xC0 | (enc << 3) | enc
} else {
0xC0
}
} else {
0xC0
};
bytes.push(modrm);
}
}
}
Ok(())
}
fn encode_vex(
&self,
_inst: &ParsedInstruction,
_encoding: &InstrEncodingInfo,
bytes: &mut Vec<u8>,
) -> Result<(), String> {
bytes.push(0xC5); bytes.push(0xF8); bytes.push(0xC0);
Ok(())
}
fn encode_evex(
&self,
_inst: &ParsedInstruction,
_encoding: &InstrEncodingInfo,
bytes: &mut Vec<u8>,
) -> Result<(), String> {
bytes.push(0x62);
bytes.push(0xF1); bytes.push(0x00); bytes.push(0x08); bytes.push(0x00); Ok(())
}
fn encode_xop(
&self,
_inst: &ParsedInstruction,
_encoding: &InstrEncodingInfo,
bytes: &mut Vec<u8>,
) -> Result<(), String> {
bytes.push(0x8F);
bytes.push(0xE8); bytes.push(0xC0); Ok(())
}
fn encode_implicit(&self, inst: &ParsedInstruction, bytes: &mut Vec<u8>) -> Result<(), String> {
match inst.mnemonic.as_str() {
"nop" => bytes.push(0x90),
"ret" => bytes.push(0xC3),
"syscall" => bytes.extend_from_slice(&[0x0F, 0x05]),
"sysret" => bytes.extend_from_slice(&[0x0F, 0x07]),
"cpuid" => bytes.extend_from_slice(&[0x0F, 0xA2]),
"pause" => bytes.extend_from_slice(&[0xF3, 0x90]),
"lfence" => bytes.extend_from_slice(&[0x0F, 0xAE, 0xE8]),
"mfence" => bytes.extend_from_slice(&[0x0F, 0xAE, 0xF0]),
"sfence" => bytes.extend_from_slice(&[0x0F, 0xAE, 0xF8]),
"clflush" => bytes.extend_from_slice(&[0x0F, 0xAE, 0x38]),
"rdtsc" => bytes.extend_from_slice(&[0x0F, 0x31]),
"rdtscp" => bytes.extend_from_slice(&[0x0F, 0x01, 0xF9]),
"leave" => bytes.push(0xC9),
"hlt" => bytes.push(0xF4),
"int3" => bytes.push(0xCC),
"ud2" => bytes.extend_from_slice(&[0x0F, 0x0B]),
"xlatb" => bytes.push(0xD7),
"cld" => bytes.push(0xFC),
"std" => bytes.push(0xFD),
"cli" => bytes.push(0xFA),
"sti" => bytes.push(0xFB),
"clc" => bytes.push(0xF8),
"stc" => bytes.push(0xF9),
"cmc" => bytes.push(0xF5),
"lahf" => bytes.push(0x9F),
"sahf" => bytes.push(0x9E),
"pushf" => bytes.push(0x9C),
"popf" => bytes.push(0x9D),
"pushfq" => bytes.push(0x9C),
"popfq" => bytes.push(0x9D),
"cbw" => bytes.extend_from_slice(&[0x66, 0x98]),
"cwde" => bytes.push(0x98),
"cdqe" => bytes.extend_from_slice(&[0x48, 0x98]),
"cwd" => bytes.extend_from_slice(&[0x66, 0x99]),
"cdq" => bytes.push(0x99),
"cqo" => bytes.extend_from_slice(&[0x48, 0x99]),
_ => {
return Err(format!("unknown implicit instruction: {}", inst.mnemonic));
}
}
Ok(())
}
fn get_reg_encoding(&self, reg: X86Reg) -> u8 {
let name = X86RegisterInfo::get_asm_name(reg.0);
match name {
"rax" | "eax" | "ax" | "al" => 0,
"rcx" | "ecx" | "cx" | "cl" => 1,
"rdx" | "edx" | "dx" | "dl" => 2,
"rbx" | "ebx" | "bx" | "bl" => 3,
"rsp" | "esp" | "sp" | "spl" | "ah" => 4,
"rbp" | "ebp" | "bp" | "bpl" | "ch" => 5,
"rsi" | "esi" | "si" | "sil" | "dh" => 6,
"rdi" | "edi" | "di" | "dil" | "bh" => 7,
"r8" | "r8d" | "r8w" | "r8b" => 8,
"r9" | "r9d" | "r9w" | "r9b" => 9,
"r10" | "r10d" | "r10w" | "r10b" => 10,
"r11" | "r11d" | "r11w" | "r11b" => 11,
"r12" | "r12d" | "r12w" | "r12b" => 12,
"r13" | "r13d" | "r13w" | "r13b" => 13,
"r14" | "r14d" | "r14w" | "r14b" => 14,
"r15" | "r15d" | "r15w" | "r15b" => 15,
"xmm0" | "ymm0" | "zmm0" | "mm0" => 0,
"xmm1" | "ymm1" | "zmm1" | "mm1" => 1,
"xmm2" | "ymm2" | "zmm2" | "mm2" => 2,
"xmm3" | "ymm3" | "zmm3" | "mm3" => 3,
"xmm4" | "ymm4" | "zmm4" | "mm4" => 4,
"xmm5" | "ymm5" | "zmm5" | "mm5" => 5,
"xmm6" | "ymm6" | "zmm6" | "mm6" => 6,
"xmm7" | "ymm7" | "zmm7" | "mm7" => 7,
"xmm8" | "ymm8" | "zmm8" => 8,
"xmm9" | "ymm9" | "zmm9" => 9,
"xmm10" | "ymm10" | "zmm10" => 10,
"xmm11" | "ymm11" | "zmm11" => 11,
"xmm12" | "ymm12" | "zmm12" => 12,
"xmm13" | "ymm13" | "zmm13" => 13,
"xmm14" | "ymm14" | "zmm14" => 14,
"xmm15" | "ymm15" | "zmm15" => 15,
_ => 0,
}
}
pub fn config(&self) -> &AssemblerConfig {
&self.config
}
pub fn set_syntax(&mut self, syntax: AsmSyntaxMode) {
self.config.syntax = syntax;
self.parser = X86AsmParser::new(syntax);
self.directive_processor.syntax = syntax;
}
pub fn reset(&mut self) {
self.directive_processor.reset();
self.fixup_handler.clear();
}
}
pub struct X86EncodingEngine;
impl X86EncodingEngine {
pub fn new() -> Self {
X86EncodingEngine
}
pub fn compute_rex(w: bool, r: bool, x: bool, b: bool) -> Option<u8> {
let mut rex = 0x40u8;
if w {
rex |= 0x08;
}
if r {
rex |= 0x04;
}
if x {
rex |= 0x02;
}
if b {
rex |= 0x01;
}
if rex == 0x40 {
None
} else {
Some(rex)
}
}
pub fn modrm(mod_bits: u8, reg: u8, rm: u8) -> u8 {
((mod_bits & 0x03) << 6) | ((reg & 0x07) << 3) | (rm & 0x07)
}
pub fn modrm_direct(reg: u8, rm: u8, extra: u8) -> u8 {
Self::modrm(3, reg, rm)
}
pub fn modrm_indirect(reg: u8, rm: u8, extra: u8) -> u8 {
Self::modrm(0, reg, rm)
}
pub fn modrm_disp8(reg: u8, rm: u8) -> u8 {
Self::modrm(1, reg, rm)
}
pub fn modrm_disp32(reg: u8, rm: u8) -> u8 {
Self::modrm(2, reg, rm)
}
pub fn compute_sib(scale: u8, index: u8, base: u8) -> u8 {
((scale & 0x03) << 6) | ((index & 0x07) << 3) | (base & 0x07)
}
pub fn scale_bits(scale: u8) -> u8 {
match scale {
1 => 0,
2 => 1,
4 => 2,
8 => 3,
_ => 0,
}
}
pub fn encode_vex_2byte(r: bool, vvvv: u8, l: bool, pp: u8) -> Vec<u8> {
let byte1 = 0xC5u8;
let byte2 = ((!r as u8) << 7) | ((!vvvv & 0x0F) << 3) | ((l as u8) << 2) | (pp & 0x03);
vec![byte1, byte2]
}
pub fn encode_vex_3byte(
r: bool,
x: bool,
b: bool,
mmmmm: u8,
w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> Vec<u8> {
let byte1 = 0xC4u8;
let byte2 = ((!r as u8) << 7) | ((!x as u8) << 6) | ((!b as u8) << 5) | (mmmmm & 0x1F);
let byte3 = ((w as u8) << 7) | ((!vvvv & 0x0F) << 3) | ((l as u8) << 2) | (pp & 0x03);
vec![byte1, byte2, byte3]
}
pub fn encode_evex_prefix(
r: bool,
x: bool,
b: bool,
mmmm: u8,
w: bool,
vvvv: u8,
vl: bool,
z: bool,
ll: u8,
bcast: bool,
pp: u8,
) -> Vec<u8> {
let byte1 = 0x62u8;
let byte2 = ((!r as u8) << 7)
| ((!x as u8) << 6)
| ((!b as u8) << 5)
| ((!r as u8 & 0x01) << 4)
| (mmmm & 0x0F);
let byte3 = ((w as u8) << 7)
| ((!vvvv & 0x0F) << 3)
| 0x04 | (pp & 0x03);
let byte4 = ((z as u8) << 7)
| ((ll & 0x03) << 5)
| ((bcast as u8) << 4)
| ((!vvvv >> 4) as u8 & 0x01) << 3
| 0x00; vec![byte1, byte2, byte3, byte4]
}
pub fn needs_rex_r(reg_encoding: u8) -> bool {
reg_encoding >= 8
}
pub fn needs_rex_b(reg_encoding: u8) -> bool {
reg_encoding >= 8
}
pub fn needs_rex_x(reg_encoding: u8) -> bool {
reg_encoding >= 8
}
pub fn build_rex(w: bool, dst_reg: u8, index_reg: u8, base_reg: u8) -> Option<u8> {
Self::compute_rex(
w,
Self::needs_rex_r(dst_reg),
Self::needs_rex_x(index_reg),
Self::needs_rex_b(base_reg),
)
}
pub fn build_memory_encoding(
base: Option<u8>,
index: Option<u8>,
scale: u8,
displacement: i64,
rip_relative: bool,
) -> (Vec<u8>, u8, u8) {
let mut extra = Vec::new();
let mut modrm = 0u8;
let sib = 0u8;
let base_reg = base.unwrap_or(0);
let index_reg = index.unwrap_or(4); let scale_bits = Self::scale_bits(scale);
if rip_relative {
modrm = Self::modrm(0, 0, 5);
extra.extend_from_slice(&(displacement as u32).to_le_bytes());
} else if base.is_none() && index.is_some() {
modrm = Self::modrm(0, 0, 4);
let sib_byte = Self::compute_sib(scale_bits, index_reg, 5);
extra.push(sib_byte);
extra.extend_from_slice(&(displacement as u32).to_le_bytes());
} else {
let (mod_bits, disp_bytes) = if displacement == 0 && base_reg != 5 {
(0, vec![])
} else if displacement >= -128 && displacement <= 127 {
(1, vec![displacement as u8])
} else {
(2, (displacement as u32).to_le_bytes().to_vec())
};
if base_reg == 4 || index_reg != 4 {
modrm = Self::modrm(mod_bits, 0, 4);
let sib_byte = Self::compute_sib(scale_bits, index_reg, base_reg);
extra.push(sib_byte);
} else {
modrm = Self::modrm(mod_bits, 0, base_reg);
}
extra.extend(disp_bytes);
}
(extra, modrm, sib)
}
pub fn encode_immediate(value: i64, size_hint: u8) -> Vec<u8> {
match size_hint {
1 => vec![value as u8],
2 => (value as u16).to_le_bytes().to_vec(),
4 => (value as u32).to_le_bytes().to_vec(),
8 => (value as u64).to_le_bytes().to_vec(),
_ => {
if value >= -128 && value <= 127 {
vec![value as u8]
} else if value >= -32768 && value <= 32767 {
(value as u16).to_le_bytes().to_vec()
} else if value >= -2147483648 && value <= 2147483647 {
(value as u32).to_le_bytes().to_vec()
} else {
(value as u64).to_le_bytes().to_vec()
}
}
}
}
pub fn emit_opcode(
bytes: &mut Vec<u8>,
mand_prefix: u8,
base_opcode: u8,
two_byte_escape: bool,
) {
if two_byte_escape {
bytes.push(0x0F);
if mand_prefix != 0 {
bytes.push(mand_prefix);
}
bytes.push(base_opcode);
} else {
if mand_prefix != 0 {
bytes.push(mand_prefix);
}
bytes.push(base_opcode);
}
}
pub fn mandatory_prefix(prefix: u8) -> Option<u8> {
match prefix {
0x66 | 0xF2 | 0xF3 => Some(prefix),
0 => None,
_ => None,
}
}
pub fn modrm_extension(opcode: u8) -> u8 {
(opcode >> 3) & 0x07
}
}
pub struct X86InstructionEncoder {
encoding: X86EncodingEngine,
}
impl X86InstructionEncoder {
pub fn new() -> Self {
X86InstructionEncoder {
encoding: X86EncodingEngine,
}
}
pub fn encode_mov_rr(dst: u8, src: u8, size: SizeSuffix) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(size == SizeSuffix::Quad, src, 0, dst);
if let Some(r) = rex {
bytes.push(r);
}
match size {
SizeSuffix::Byte => bytes.push(0x88),
_ => bytes.push(0x89),
}
let modrm = X86EncodingEngine::modrm_direct(src & 7, dst & 7, 0);
bytes.push(modrm);
bytes
}
pub fn encode_mov_ri(reg: u8, imm: i64, size: SizeSuffix) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(size == SizeSuffix::Quad, 0, 0, reg);
if let Some(r) = rex {
bytes.push(r);
}
match size {
SizeSuffix::Byte => {
bytes.push(0xB0 + (reg & 7));
bytes.push(imm as u8);
}
SizeSuffix::Word => {
bytes.push(0x66); bytes.push(0xB8 + (reg & 7));
bytes.extend_from_slice(&(imm as u16).to_le_bytes());
}
SizeSuffix::Long => {
bytes.push(0xB8 + (reg & 7));
bytes.extend_from_slice(&(imm as u32).to_le_bytes());
}
SizeSuffix::Quad => {
bytes.push(0xB8 + (reg & 7));
bytes.extend_from_slice(&(imm as u64).to_le_bytes());
}
_ => {
bytes.push(0xB8 + (reg & 7));
bytes.extend_from_slice(&(imm as u64).to_le_bytes());
}
}
bytes
}
pub fn encode_alu_rr(
opcode_base: u8,
dst: u8,
src: u8,
is_64bit: bool,
is_8bit: bool,
) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, src, 0, dst);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(opcode_base);
let modrm = X86EncodingEngine::modrm_direct(src & 7, dst & 7, 0);
bytes.push(modrm);
bytes
}
pub fn encode_push_reg(reg: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
if reg >= 8 {
bytes.push(0x41); }
if is_64bit && reg < 8 {
}
bytes.push(0x50 + (reg & 7));
bytes
}
pub fn encode_pop_reg(reg: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
if reg >= 8 {
bytes.push(0x41);
}
bytes.push(0x58 + (reg & 7));
bytes
}
pub fn encode_jcc_rel8(condition: u8, displacement: i8) -> Vec<u8> {
vec![0x70 + (condition & 0x0F), displacement as u8]
}
pub fn encode_jcc_rel32(condition: u8, displacement: i32) -> Vec<u8> {
let mut bytes = vec![0x0F, 0x80 + (condition & 0x0F)];
bytes.extend_from_slice(&displacement.to_le_bytes());
bytes
}
pub fn encode_alu_ri(opcode_modrm_ext: u8, reg: u8, imm: i64, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, 0, 0, reg);
if let Some(r) = rex {
bytes.push(r);
}
if imm >= -128 && imm <= 127 {
bytes.push(0x83);
bytes.push(X86EncodingEngine::modrm_direct(
opcode_modrm_ext,
reg & 7,
0,
));
bytes.push(imm as u8);
} else if is_64bit && (imm > 2147483647 || imm < -2147483648) {
bytes.clear();
bytes.extend(Self::encode_mov_ri(
reg,
imm,
if is_64bit {
SizeSuffix::Quad
} else {
SizeSuffix::Long
},
));
} else {
bytes.push(0x81);
bytes.push(X86EncodingEngine::modrm_direct(
opcode_modrm_ext,
reg & 7,
0,
));
bytes.extend_from_slice(&(imm as u32).to_le_bytes());
}
bytes
}
pub fn encode_shift_imm(opcode_modrm_ext: u8, reg: u8, count: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, 0, 0, reg);
if let Some(r) = rex {
bytes.push(r);
}
if count == 1 {
bytes.push(0xD1);
bytes.push(X86EncodingEngine::modrm_direct(
opcode_modrm_ext,
reg & 7,
0,
));
} else {
bytes.push(0xC1);
bytes.push(X86EncodingEngine::modrm_direct(
opcode_modrm_ext,
reg & 7,
0,
));
bytes.push(count);
}
bytes
}
pub fn encode_shift_cl(opcode_modrm_ext: u8, reg: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, 0, 0, reg);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(0xD3);
bytes.push(X86EncodingEngine::modrm_direct(
opcode_modrm_ext,
reg & 7,
0,
));
bytes
}
pub fn encode_inc_dec(is_inc: bool, reg: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, 0, 0, reg);
if let Some(r) = rex {
bytes.push(r);
}
let opcode = if is_inc { 0xFF } else { 0xFF };
let ext = if is_inc { 0 } else { 1 };
bytes.push(opcode);
bytes.push(X86EncodingEngine::modrm_direct(ext, reg & 7, 0));
bytes
}
pub fn encode_not_neg(is_not: bool, reg: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, 0, 0, reg);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(0xF7);
let ext = if is_not { 2 } else { 3 };
bytes.push(X86EncodingEngine::modrm_direct(ext, reg & 7, 0));
bytes
}
pub fn encode_mul_div(ext: u8, reg: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, 0, 0, reg);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(0xF7);
bytes.push(X86EncodingEngine::modrm_direct(ext, reg & 7, 0));
bytes
}
pub fn encode_test_rr(dst: u8, src: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, src, 0, dst);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(0x85);
bytes.push(X86EncodingEngine::modrm_direct(src & 7, dst & 7, 0));
bytes
}
pub fn encode_lea(
dst: u8,
base: u8,
index: Option<u8>,
scale: u8,
disp: i64,
is_64bit: bool,
) -> Vec<u8> {
let mut bytes = Vec::new();
let idx_reg = index.unwrap_or(4);
let rex = X86EncodingEngine::build_rex(is_64bit, dst, idx_reg, base);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(0x8D);
let (mut extra, modrm, _sib) =
X86EncodingEngine::build_memory_encoding(Some(base), index, scale, disp, false);
let mod_field = (modrm >> 6) & 0x03;
let rm_field = modrm & 0x07;
let fixed_modrm = X86EncodingEngine::modrm(mod_field, dst & 7, rm_field);
bytes.push(fixed_modrm);
bytes.append(&mut extra);
bytes
}
pub fn encode_xchg_ax(reg: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
if is_64bit {
bytes.push(0x48);
}
bytes.push(0x90 + (reg & 7));
bytes
}
pub fn encode_bswap(reg: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, 0, 0, reg);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(0x0F);
bytes.push(0xC8 + (reg & 7));
bytes
}
pub fn encode_call_jmp_reg(is_call: bool, reg: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, 0, 0, reg);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(0xFF);
let ext = if is_call { 2 } else { 4 };
bytes.push(X86EncodingEngine::modrm_direct(ext, reg & 7, 0));
bytes
}
pub fn encode_setcc(condition: u8, reg: u8) -> Vec<u8> {
let mut bytes = Vec::new();
if reg >= 8 {
bytes.push(0x41);
}
bytes.push(0x0F);
bytes.push(0x90 + (condition & 0x0F));
bytes.push(X86EncodingEngine::modrm_direct(0, reg & 7, 0));
bytes
}
pub fn encode_cmovcc(condition: u8, dst: u8, src: u8, is_64bit: bool) -> Vec<u8> {
let mut bytes = Vec::new();
let rex = X86EncodingEngine::build_rex(is_64bit, dst, 0, src);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(0x0F);
bytes.push(0x40 + (condition & 0x0F));
bytes.push(X86EncodingEngine::modrm_direct(dst & 7, src & 7, 0));
bytes
}
pub fn encode_movsx_movzx(
is_sx: bool,
dst: u8,
src: u8,
src_size: u8,
dst_size: u8,
) -> Vec<u8> {
let mut bytes = Vec::new();
let is_64bit = dst_size == 8;
let rex = X86EncodingEngine::build_rex(is_64bit, dst, 0, src);
if let Some(r) = rex {
bytes.push(r);
}
bytes.push(0x0F);
let base_op = if is_sx { 0xBE } else { 0xB6 };
let op = match src_size {
1 => base_op, 2 => base_op + 1, 4 if is_64bit => 0x63, _ => base_op,
};
if src_size == 4 && is_64bit && is_sx {
bytes[1] = 0x63;
} else {
bytes.push(op);
}
bytes.push(X86EncodingEngine::modrm_direct(dst & 7, src & 7, 0));
bytes
}
pub fn encode_sse_scalar(prefix: u8, opcode: u8, dst: u8, src: u8) -> Vec<u8> {
let mut bytes = Vec::new();
if prefix != 0 {
bytes.push(prefix); }
bytes.push(0x0F);
bytes.push(opcode);
bytes.push(X86EncodingEngine::modrm_direct(dst & 7, src & 7, 0));
bytes
}
pub fn encode_sse_packed(prefix: u8, opcode: u8, dst: u8, src: u8) -> Vec<u8> {
let mut bytes = Vec::new();
if prefix != 0 {
bytes.push(prefix); }
bytes.push(0x0F);
bytes.push(opcode);
bytes.push(X86EncodingEngine::modrm_direct(dst & 7, src & 7, 0));
bytes
}
pub fn encode_avx_binop(
prefix: u8,
opcode: u8,
dst: u8,
src1: u8,
src2: u8,
is_256bit: bool,
) -> Vec<u8> {
let mut bytes = Vec::new();
let vvvv = src2;
let r = X86EncodingEngine::needs_rex_r(dst) || X86EncodingEngine::needs_rex_r(src1);
let pp = match prefix {
0x66 => 1,
0xF3 => 2,
0xF2 => 3,
_ => 0,
};
let vex = X86EncodingEngine::encode_vex_2byte(r, vvvv & 7, is_256bit, pp);
bytes.extend(vex);
bytes.push(opcode);
let modrm = X86EncodingEngine::modrm_direct(dst & 7, src1 & 7, 0);
bytes.push(modrm);
bytes
}
pub fn encode_avx512_binop(
prefix: u8,
opcode: u8,
dst: u8,
src1: u8,
src2: u8,
mask: Option<u8>,
is_512bit: bool,
) -> Vec<u8> {
let mut bytes = Vec::new();
let pp = match prefix {
0x66 => 1,
0xF3 => 2,
0xF2 => 3,
_ => 0,
};
let ll = if is_512bit { 2 } else { 0 };
let vvvv = src2 & 0x1F;
let z = false; let bcast = false;
let evex = X86EncodingEngine::encode_evex_prefix(
false, false, false, 1, false, vvvv, is_512bit, z, ll, bcast, pp,
);
bytes.extend(evex);
bytes.push(opcode);
let modrm = X86EncodingEngine::modrm_direct(dst & 7, src1 & 7, 0);
bytes.push(modrm);
bytes
}
pub fn get_condition_number(mnemonic: &str) -> Option<u8> {
match mnemonic {
"o" | "jo" | "seto" | "cmovo" => Some(0),
"no" | "jno" | "setno" | "cmovno" => Some(1),
"b" | "jb" | "jc" | "jnae" | "setb" | "setc" | "setnae" | "cmovb" | "cmovc"
| "cmovnae" => Some(2),
"ae" | "jae" | "jnb" | "jnc" | "setae" | "setnb" | "setnc" | "cmovae" | "cmovnb"
| "cmovnc" => Some(3),
"e" | "z" | "je" | "jz" | "sete" | "setz" | "cmove" | "cmovz" => Some(4),
"ne" | "nz" | "jne" | "jnz" | "setne" | "setnz" | "cmovne" | "cmovnz" => Some(5),
"be" | "na" | "jbe" | "jna" | "setbe" | "setna" | "cmovbe" | "cmovna" => Some(6),
"a" | "nbe" | "ja" | "jnbe" | "seta" | "setnbe" | "cmova" | "cmovnbe" => Some(7),
"s" | "js" | "sets" | "cmovs" => Some(8),
"ns" | "jns" | "setns" | "cmovns" => Some(9),
"p" | "pe" | "jp" | "jpe" | "setp" | "setpe" | "cmovp" | "cmovpe" => Some(10),
"np" | "po" | "jnp" | "jpo" | "setnp" | "setpo" | "cmovnp" | "cmovpo" => Some(11),
"l" | "nge" | "jl" | "jnge" | "setl" | "setnge" | "cmovl" | "cmovnge" => Some(12),
"ge" | "nl" | "jge" | "jnl" | "setge" | "setnl" | "cmovge" | "cmovnl" => Some(13),
"le" | "ng" | "jle" | "jng" | "setle" | "setng" | "cmovle" | "cmovng" => Some(14),
"g" | "nle" | "jg" | "jnle" | "setg" | "setnle" | "cmovg" | "cmovnle" => Some(15),
_ => None,
}
}
pub fn get_alu_extension(mnemonic: &str) -> Option<u8> {
match mnemonic {
"add" => Some(0),
"or" => Some(1),
"adc" => Some(2),
"sbb" => Some(3),
"and" => Some(4),
"sub" => Some(5),
"xor" => Some(6),
"cmp" => Some(7),
_ => None,
}
}
pub fn get_shift_extension(mnemonic: &str) -> Option<u8> {
match mnemonic {
"rol" => Some(0),
"ror" => Some(1),
"rcl" => Some(2),
"rcr" => Some(3),
"shl" | "sal" => Some(4),
"shr" => Some(5),
"sar" => Some(7),
_ => None,
}
}
pub fn get_mul_div_extension(mnemonic: &str) -> Option<u8> {
match mnemonic {
"mul" => Some(4),
"imul" => Some(5),
"div" => Some(6),
"idiv" => Some(7),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lexer_identifiers() {
let mut lexer = X86AsmLexer::new("mov rax rbx");
let tokens = lexer.lex_all();
assert_eq!(tokens.len(), 4); assert!(matches!(tokens[0].0, X86AsmToken::Identifier(ref s) if s == "mov"));
assert!(matches!(tokens[1].0, X86AsmToken::Identifier(ref s) if s == "rax"));
assert!(matches!(tokens[2].0, X86AsmToken::Identifier(ref s) if s == "rbx"));
assert!(matches!(tokens[3].0, X86AsmToken::EndOfFile));
}
#[test]
fn test_lexer_numbers() {
let mut lexer = X86AsmLexer::new("42 0xFF 0b1010 -5");
let tokens = lexer.lex_all();
assert!(matches!(tokens[0].0, X86AsmToken::Integer(42)));
assert!(matches!(tokens[1].0, X86AsmToken::Integer(255)));
assert!(matches!(tokens[2].0, X86AsmToken::Integer(10)));
assert!(matches!(tokens[3].0, X86AsmToken::Integer(-5)));
}
#[test]
fn test_lexer_comments() {
let mut lexer = X86AsmLexer::new("mov rax # comment\nadd rbx");
let tokens = lexer.lex_all();
let identifiers: Vec<String> = tokens
.iter()
.filter_map(|(t, _, _)| match t {
X86AsmToken::Identifier(s) => Some(s.clone()),
_ => None,
})
.collect();
assert_eq!(identifiers, vec!["mov", "rax", "add", "rbx"]);
}
#[test]
fn test_lexer_at_t_syntax() {
let mut lexer = X86AsmLexer::new("movl %eax, $42");
let tokens = lexer.lex_all();
let kinds: Vec<&str> = tokens.iter().map(|(t, _, _)| t.kind_name()).collect();
assert!(kinds.contains(&"percent"));
assert!(kinds.contains(&"dollar"));
}
#[test]
fn test_lexer_string_literal() {
let mut lexer = X86AsmLexer::new("\"hello world\"");
let tokens = lexer.lex_all();
assert!(matches!(tokens[0].0, X86AsmToken::StringLiteral(ref s) if s == "hello world"));
}
#[test]
fn test_mnemonic_parser_count() {
let parser = X86MnemonicParser::new(AsmSyntaxMode::ATT);
let count = parser.mnemonic_count();
assert!(
count >= 800,
"Expected at least 800 mnemonics, got {}",
count
);
}
#[test]
fn test_mnemonic_parser_att_basic() {
let parser = X86MnemonicParser::new(AsmSyntaxMode::ATT);
let result = parser.parse_att("movl");
assert!(result.is_some(), "movl should be recognized");
let (name, suffix, _) = result.unwrap();
assert_eq!(name, "mov");
assert_eq!(suffix, Some(SizeSuffix::Long));
}
#[test]
fn test_mnemonic_parser_att_suffixes() {
let parser = X86MnemonicParser::new(AsmSyntaxMode::ATT);
assert!(parser.parse_att("movb").is_some());
assert!(parser.parse_att("movw").is_some());
assert!(parser.parse_att("movl").is_some());
assert!(parser.parse_att("movq").is_some());
assert!(parser.parse_att("addl").is_some());
assert!(parser.parse_att("subq").is_some());
}
#[test]
fn test_mnemonic_parser_jcc_aliases() {
let parser = X86MnemonicParser::new(AsmSyntaxMode::ATT);
assert!(parser.parse_att("jnz").is_some()); assert!(parser.parse_att("jz").is_some()); assert!(parser.parse_att("jnle").is_some()); }
#[test]
fn test_operand_parser_register_att() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::ATT);
assert!(parser.parse_operand_att("%rax").is_some());
assert!(parser.parse_operand_att("%eax").is_some());
assert!(parser.parse_operand_att("%ax").is_some());
assert!(parser.parse_operand_att("%al").is_some());
assert!(parser.parse_operand_att("%xmm0").is_some());
assert!(parser.parse_operand_att("%ymm0").is_some());
assert!(parser.parse_operand_att("%zmm0").is_some());
assert!(parser.parse_operand_att("%k1").is_some());
}
#[test]
fn test_operand_parser_immediate_att() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::ATT);
assert!(matches!(
parser.parse_operand_att("$42"),
Some(AsmOperand::Imm(42))
));
assert!(matches!(
parser.parse_operand_att("$0xFF"),
Some(AsmOperand::Imm(255))
));
}
#[test]
fn test_operand_parser_memory_att() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::ATT);
let op = parser.parse_operand_att("4(%rax)");
assert!(op.is_some());
if let Some(AsmOperand::Mem(mem)) = op {
assert_eq!(mem.displacement, 4);
assert!(mem.base.is_some());
assert_eq!(X86RegisterInfo::get_asm_name(mem.base.unwrap().0), "rax");
} else {
panic!("expected memory operand");
}
let op2 = parser.parse_operand_att("8(%rax, %rcx, 4)");
assert!(op2.is_some());
if let Some(AsmOperand::Mem(mem)) = op2 {
assert_eq!(mem.displacement, 8);
assert_eq!(mem.scale, 4);
}
let op3 = parser.parse_operand_att("symbol(%rip)");
assert!(op3.is_some());
if let Some(AsmOperand::Mem(mem)) = op3 {
assert!(mem.is_rip_relative);
assert_eq!(mem.symbol, Some("symbol".to_string()));
}
}
#[test]
fn test_operand_parser_register_intel() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::Intel);
assert!(parser.parse_operand_intel("rax").is_some());
assert!(parser.parse_operand_intel("eax").is_some());
assert!(parser.parse_operand_intel("xmm0").is_some());
}
#[test]
fn test_operand_parser_memory_intel() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::Intel);
let op = parser.parse_operand_intel("[rax + rcx*4 + 8]");
assert!(op.is_some());
if let Some(AsmOperand::Mem(mem)) = op {
assert_eq!(mem.displacement, 8);
assert_eq!(mem.scale, 4);
}
let op2 = parser.parse_operand_intel("DWORD PTR [rax]");
assert!(op2.is_some());
if let Some(AsmOperand::Mem(mem)) = op2 {
assert_eq!(mem.ptr_qualifier, Some(MemPtrQualifier::DWord));
}
}
#[test]
fn test_operand_parser_k_registers() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::ATT);
let op = parser.parse_operand_att("%k3");
assert!(matches!(op, Some(AsmOperand::KReg(3))));
}
#[test]
fn test_directive_section_switch() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
assert_eq!(proc.current_section, ".text");
proc.process(&AsmDirective::Data).unwrap();
assert_eq!(proc.current_section, ".data");
proc.process(&AsmDirective::Bss).unwrap();
assert_eq!(proc.current_section, ".bss");
proc.process(&AsmDirective::Text).unwrap();
assert_eq!(proc.current_section, ".text");
}
#[test]
fn test_directive_data_emission() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
let bytes = proc
.process(&AsmDirective::Byte(vec![0x41, 0x42, 0x43]))
.unwrap();
assert_eq!(bytes, vec![0x41, 0x42, 0x43]);
let bytes = proc.process(&AsmDirective::Short(vec![0x1234])).unwrap();
assert_eq!(bytes, vec![0x34, 0x12]);
let bytes = proc.process(&AsmDirective::Long(vec![0x12345678])).unwrap();
assert_eq!(bytes, vec![0x78, 0x56, 0x34, 0x12]);
let bytes = proc
.process(&AsmDirective::Quad(vec![0x1234567890ABCDEF]))
.unwrap();
assert!(bytes.len() == 8);
}
#[test]
fn test_directive_ascii() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
let bytes = proc
.process(&AsmDirective::Ascii("hello".to_string()))
.unwrap();
assert_eq!(bytes, b"hello");
let bytes = proc
.process(&AsmDirective::Asciz("hello".to_string()))
.unwrap();
assert_eq!(bytes, b"hello\0");
}
#[test]
fn test_directive_fill() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
let bytes = proc
.process(&AsmDirective::Fill {
count: 4,
value: 0x90,
size: Some(1),
})
.unwrap();
assert_eq!(bytes, vec![0x90, 0x90, 0x90, 0x90]);
}
#[test]
fn test_directive_alignment() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.current_offset = 3;
let bytes = proc
.process(&AsmDirective::Align {
alignment: 4,
fill: Some(0x90),
max: Some(4),
})
.unwrap();
assert_eq!(bytes.len(), 1); assert_eq!(proc.current_offset, 4);
}
#[test]
fn test_directive_p2align() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.current_offset = 3;
let bytes = proc
.process(&AsmDirective::P2Align {
alignment: 2, fill: Some(0x90),
max: Some(4),
})
.unwrap();
assert_eq!(bytes.len(), 1);
assert_eq!(proc.current_offset, 4);
}
#[test]
fn test_directive_org() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::Org(100)).unwrap();
assert_eq!(proc.current_offset, 100);
assert_eq!(proc.data.len(), 100);
}
#[test]
fn test_directive_symbols() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::Global("main".to_string()))
.unwrap();
assert!(proc.symbols.contains_key("main"));
assert!(proc.symbols["main"].is_global);
}
#[test]
fn test_directive_cfi() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::CfiStartProc).unwrap();
assert!(proc.cfi_state.is_active);
proc.process(&AsmDirective::CfiDefCfa {
reg: "rsp".to_string(),
offset: 8,
})
.unwrap();
assert!(proc.cfi_state.cfa_reg.is_some());
assert_eq!(proc.cfi_state.cfa_offset, Some(8));
}
#[test]
fn test_assemble_empty() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("").unwrap();
assert_eq!(result.data.len(), 0);
assert_eq!(result.instruction_count, 0);
}
#[test]
fn test_assemble_nop() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("nop").unwrap();
assert_eq!(result.data, vec![0x90]);
}
#[test]
fn test_assemble_ret() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("ret").unwrap();
assert_eq!(result.data, vec![0xC3]);
}
#[test]
fn test_assemble_push_pop() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("push %rax\npop %rbx").unwrap();
assert_eq!(result.data, vec![0x50, 0x5B]);
}
#[test]
fn test_assemble_mov_att() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movq %rax, %rbx").unwrap();
assert_eq!(result.data, vec![0x48, 0x89, 0xC3]);
}
#[test]
fn test_assemble_add() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("addq %rax, %rbx").unwrap();
assert_eq!(result.data.len(), 3);
}
#[test]
fn test_assemble_intel_syntax() {
let mut asm = X86MCFullAssembler::default_intel();
let result = asm.assemble("mov rax, rbx").unwrap();
assert!(!result.data.is_empty());
}
#[test]
fn test_assemble_syscall_implicits() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("syscall").unwrap();
assert_eq!(result.data, vec![0x0F, 0x05]);
let result = asm.assemble("cpuid").unwrap();
assert_eq!(result.data, vec![0x0F, 0xA2]);
let result = asm.assemble("mfence").unwrap();
assert_eq!(result.data, vec![0x0F, 0xAE, 0xF0]);
}
#[test]
fn test_assemble_directives() {
let mut asm = X86MCFullAssembler::default_att();
let source = ".text\n.byte 0x90, 0x90, 0x90\n.ascii \"hello\"\n";
let result = asm.assemble(source).unwrap();
assert!(result.instruction_count == 0);
assert!(result.data.len() >= 8);
}
#[test]
fn test_assemble_labels() {
let mut asm = X86MCFullAssembler::default_att();
let source = "start:\n movq %rax, %rbx\n.Llocal:\n ret\n";
let result = asm.assemble(source).unwrap();
assert!(result.symbols.contains_key("start"));
assert!(result.symbols.contains_key(".Llocal"));
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_assemble_with_section_switch() {
let mut asm = X86MCFullAssembler::default_att();
let source = ".text\nmovq %rax, %rbx\n.data\n.byte 0x41\n";
let result = asm.assemble(source).unwrap();
assert!(result.sections.len() >= 1);
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_assemble_alignment() {
let mut asm = X86MCFullAssembler::default_att();
let source = ".text\nnop\n.align 8\nmovq %rax, %rbx\n";
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_assemble_global_symbol() {
let mut asm = X86MCFullAssembler::default_att();
let source = ".globl main\nmain:\n movq %rax, %rbx\n ret\n";
let result = asm.assemble(source).unwrap();
let sym = result.symbols.get("main").unwrap();
assert!(sym.is_global);
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_assemble_intel_ptr() {
let mut asm = X86MCFullAssembler::default_intel();
let source = "mov rax, DWORD PTR [rbx]\n";
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_assemble_cfi_directives() {
let mut asm = X86MCFullAssembler::default_att();
let source = ".cfi_startproc\n.cfi_def_cfa rsp, 8\n.cfi_offset rbp, -16\n.cfi_endproc\n";
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 0);
}
#[test]
fn test_assemble_comment_handling() {
let mut asm = X86MCFullAssembler::default_att();
let source = "# This is a comment\nmovq %rax, %rbx # inline comment\nret\n";
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_assembler_config() {
let asm = X86MCFullAssembler::default_att();
assert_eq!(asm.config().syntax, AsmSyntaxMode::ATT);
let asm = X86MCFullAssembler::default_intel();
assert_eq!(asm.config().syntax, AsmSyntaxMode::Intel);
}
#[test]
fn test_assembler_syntax_switch() {
let mut asm = X86MCFullAssembler::default_att();
asm.set_syntax(AsmSyntaxMode::Intel);
assert_eq!(asm.config().syntax, AsmSyntaxMode::Intel);
asm.set_syntax(AsmSyntaxMode::ATT);
assert_eq!(asm.config().syntax, AsmSyntaxMode::ATT);
}
#[test]
fn test_roundtrip_nop_assembly() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("nop").unwrap();
assert_eq!(result.data, vec![0x90]);
let mut asm2 = X86MCFullAssembler::default_intel();
let result2 = asm2.assemble("nop").unwrap();
assert_eq!(result2.data, vec![0x90]);
}
#[test]
fn test_roundtrip_push_pop_mov() {
let mut asm = X86MCFullAssembler::default_att();
let source = "push %rax\npush %rbx\nmovq %rax, %rbx\npop %rbx\npop %rax\nret\n";
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 6);
assert!(!result.data.is_empty());
}
#[test]
fn test_all_mnemonics_reference() {
let mnemonic_list = ALL_MNEMONICS;
assert!(mnemonic_list.contains(&"mov"), "Should contain 'mov'");
assert!(mnemonic_list.contains(&"add"), "Should contain 'add'");
assert!(mnemonic_list.contains(&"vaddps"), "Should contain 'vaddps'");
assert!(
mnemonic_list.contains(&"vfmadd132pd"),
"Should contain FMA instructions"
);
assert!(
mnemonic_list.contains(&"kmovw"),
"Should contain AVX-512 mask instructions"
);
assert!(
mnemonic_list.contains(&"aesenc"),
"Should contain AES instructions"
);
assert!(
mnemonic_list.contains(&"sha1rnds4"),
"Should contain SHA instructions"
);
assert!(
mnemonic_list.len() >= 800,
"Should have at least 800 mnemonics"
);
}
#[test]
fn test_assembler_warnings() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("invalid_mnemonic_xyz").unwrap();
assert!(!result.warnings.is_empty());
}
#[test]
fn test_directive_equ_and_set() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::Equ {
name: "CONST".to_string(),
value: "42".to_string(),
})
.unwrap();
assert_eq!(proc.equates.get("CONST"), Some(&42));
}
#[test]
fn test_fixup_handler_creation() {
let handler = X86FixupHandler::new(true);
assert!(handler.pic);
assert!(handler.emit_got_plt);
}
#[test]
fn test_fixup_kind_sizes() {
assert_eq!(X86FixupKind::Rel8.size_bytes(), 1);
assert_eq!(X86FixupKind::Rel32.size_bytes(), 4);
assert_eq!(X86FixupKind::Abs64.size_bytes(), 8);
assert!(X86FixupKind::Rel8.is_pc_relative());
assert!(!X86FixupKind::Abs64.is_pc_relative());
}
#[test]
fn test_mem_ptr_qualifier() {
assert_eq!(MemPtrQualifier::Byte.size_bytes(), 1);
assert_eq!(MemPtrQualifier::Word.size_bytes(), 2);
assert_eq!(MemPtrQualifier::DWord.size_bytes(), 4);
assert_eq!(MemPtrQualifier::QWord.size_bytes(), 8);
assert_eq!(MemPtrQualifier::TByte.size_bytes(), 10);
assert_eq!(MemPtrQualifier::XmmWord.size_bytes(), 16);
assert_eq!(MemPtrQualifier::YmmWord.size_bytes(), 32);
assert_eq!(MemPtrQualifier::ZmmWord.size_bytes(), 64);
assert_eq!(
MemPtrQualifier::from_str("BYTE"),
Some(MemPtrQualifier::Byte)
);
assert_eq!(
MemPtrQualifier::from_str("DWORD"),
Some(MemPtrQualifier::DWord)
);
assert_eq!(MemPtrQualifier::from_str("INVALID"), None);
}
#[test]
fn test_size_suffix() {
assert_eq!(SizeSuffix::from_att_suffix("b"), Some(SizeSuffix::Byte));
assert_eq!(SizeSuffix::from_att_suffix("w"), Some(SizeSuffix::Word));
assert_eq!(SizeSuffix::from_att_suffix("l"), Some(SizeSuffix::Long));
assert_eq!(SizeSuffix::from_att_suffix("q"), Some(SizeSuffix::Quad));
assert_eq!(
SizeSuffix::from_att_suffix("ss"),
Some(SizeSuffix::ScalarSingle)
);
assert_eq!(
SizeSuffix::from_att_suffix("sd"),
Some(SizeSuffix::ScalarDouble)
);
assert_eq!(
SizeSuffix::from_att_suffix("ps"),
Some(SizeSuffix::PackedSingle)
);
assert_eq!(
SizeSuffix::from_att_suffix("pd"),
Some(SizeSuffix::PackedDouble)
);
}
#[test]
fn test_rex_prefix_generation() {
let encoder = X86EncodingEngine::new();
let rex = encoder.compute_rex(true, true, false, true);
assert_eq!(rex, Some(0x4B));
let rex = encoder.compute_rex(false, false, false, false);
assert_eq!(rex, None);
let rex = encoder.compute_rex(true, false, false, false);
assert_eq!(rex, Some(0x48));
}
#[test]
fn test_modrm_generation() {
let modrm = X86EncodingEngine::modrm_direct(0, 1, 2);
assert_eq!(modrm, 0xC0 | (1 << 3) | 2);
let modrm = X86EncodingEngine::modrm_indirect(0, 3, 0);
assert_eq!(modrm, (0 << 6) | (3 << 3) | 0);
let modrm = X86EncodingEngine::modrm_disp8(5, 2);
assert_eq!(modrm, (1 << 6) | (5 << 3) | 2);
}
#[test]
fn test_sib_generation() {
let sib = X86EncodingEngine::compute_sib(2, 1, 0);
assert_eq!(sib, (2 << 6) | (1 << 3) | 0);
let sib = X86EncodingEngine::compute_sib(0, 4, 5);
assert_eq!(sib, (0 << 6) | (4 << 3) | 5);
}
#[test]
fn test_vex_2byte_prefix() {
let vex = X86EncodingEngine::encode_vex_2byte(false, 0, false, 0);
assert_eq!(vex[0], 0xC5);
assert_eq!(vex[1], 0xF8);
}
#[test]
fn test_vex_3byte_prefix() {
let vex = X86EncodingEngine::encode_vex_3byte(false, false, false, 1, false, 0, false, 0);
assert_eq!(vex[0], 0xC4);
assert_eq!(vex[1], 0xE1);
assert_eq!(vex.len(), 3);
}
#[test]
fn test_evex_prefix_basic() {
let evex = X86EncodingEngine::encode_evex_prefix(
false, false, false, 1, false, 0, false, false, 0, false, 0,
);
assert_eq!(evex.len(), 4);
assert_eq!(evex[0], 0x62);
}
#[test]
fn test_encode_alu_immediate() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("addq $42, %rax").unwrap();
assert!(!result.data.is_empty());
assert!(result.data.len() >= 3);
}
#[test]
fn test_encode_shift_instructions() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("shlq $1, %rax").unwrap();
assert!(!result.data.is_empty());
}
#[test]
fn test_encode_mov_imm64() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movq $0x1234567890ABCDEF, %rax").unwrap();
assert_eq!(result.data.len(), 10);
assert_eq!(result.data[0], 0x48);
assert_eq!(result.data[1], 0xB8);
}
#[test]
fn test_encode_xor_zeroing() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("xorq %rax, %rax").unwrap();
assert_eq!(result.data.len(), 3);
assert_eq!(result.data[0], 0x48);
assert_eq!(result.data[1], 0x31);
assert_eq!(result.data[2], 0xC0);
}
#[test]
fn test_encode_lea() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("leaq 8(%rsp), %rax").unwrap();
assert!(!result.data.is_empty());
}
#[test]
fn test_encode_cmp() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("cmpq %rax, %rbx").unwrap();
assert_eq!(result.data[0], 0x48);
assert_eq!(result.data[1], 0x39);
assert_eq!(result.data[2], 0xC3);
}
#[test]
fn test_encode_test() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("testq %rax, %rax").unwrap();
assert!(!result.data.is_empty());
}
#[test]
fn test_encode_inc_dec() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("incq %rax\ndecq %rbx").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_not_neg() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("notq %rax\nnegq %rbx").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_mul_div() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("mulq %rbx\ndivq %rcx").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_jcc_short() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("je label\nlabel:\nnop").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_call_jmp() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("call *%rax\njmp *%rbx").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_setcc() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("sete %al\nsetne %bl").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_cmovcc() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("cmoveq %rax, %rbx").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_xchg() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("xchgq %rax, %rbx").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_bswap() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("bswap %rax").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_sse_mov() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movss %xmm0, %xmm1").unwrap();
assert_eq!(result.instruction_count, 1);
assert!(result.data.len() >= 3);
}
#[test]
fn test_encode_sse_arith() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec![
"addss %xmm0, %xmm1",
"subss %xmm0, %xmm1",
"mulss %xmm0, %xmm1",
"divss %xmm0, %xmm1",
];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
assert!(!result.data.is_empty());
}
}
#[test]
fn test_encode_sse_packed() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec![
"addps %xmm0, %xmm1",
"mulpd %xmm0, %xmm1",
"andps %xmm0, %xmm1",
"orps %xmm0, %xmm1",
"xorps %xmm0, %xmm1",
];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_encode_avx_basic() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("vaddps %xmm0, %xmm1, %xmm2").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_avx_fma() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec![
"vfmadd132ps %xmm0, %xmm1, %xmm2",
"vfmadd213ps %xmm0, %xmm1, %xmm2",
"vfmadd231ps %xmm0, %xmm1, %xmm2",
];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_encode_bmi() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec![
"andnq %rax, %rbx, %rcx",
"blsiq %rax, %rbx",
"blsmskq %rax, %rbx",
];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_encode_avx512_mask() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("kaddw %k1, %k2, %k3").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_segment_registers() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movq %fs:0, %rax").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_rip_relative() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movq symbol(%rip), %rax").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_control_registers() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movq %cr0, %rax").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_debug_registers() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movq %dr0, %rax").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_x87_fpu() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("fldl (%rax)\nfstpl (%rbx)").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_mmx() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movq %mm0, %mm1\npaddb %mm0, %mm1").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_string_ops() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec!["movsb", "movsw", "movsl", "movsq", "rep movsb"];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_encode_atomic() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("lock addq $1, (%rax)").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_aesni() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("aesenc %xmm0, %xmm1").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_sha() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("sha1rnds4 $0, %xmm0, %xmm1").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_crc32() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("crc32q %rax, %rbx").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_popcnt() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("popcntq %rax, %rbx").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_lzcnt_tzcnt() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("lzcntq %rax, %rbx\ntzcntq %rcx, %rdx")
.unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_movbe() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movbe (%rax), %rbx").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_prefetch() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec![
"prefetcht0 (%rax)",
"prefetcht1 (%rax)",
"prefetcht2 (%rax)",
"prefetchnta (%rax)",
"prefetchw (%rax)",
];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_encode_shifts_variable() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("shlq %cl, %rax\nshrq %cl, %rbx\nsarq %cl, %rcx")
.unwrap();
assert_eq!(result.instruction_count, 3);
}
#[test]
fn test_encode_double_shifts() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("shldq $4, %rax, %rbx\nshrdq $8, %rcx, %rdx")
.unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_bt_instructions() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("btq $3, %rax\nbtsq $5, %rbx\nbtrq $7, %rcx\nbtcq $9, %rdx")
.unwrap();
assert_eq!(result.instruction_count, 4);
}
#[test]
fn test_encode_imul_three_operand() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("imulq $42, %rax, %rbx").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_movsx_movzx() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movsbq %al, %rbx\nmovzbq %al, %rcx").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_movsxd() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movslq %eax, %rbx").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_cbw_cwde_cdqe() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("cbtw\ncwtl\ncltq").unwrap();
assert_eq!(result.instruction_count, 3);
}
#[test]
fn test_encode_cwd_cdq_cqo() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("cwtd\ncltd\ncqto").unwrap();
assert_eq!(result.instruction_count, 3);
}
#[test]
fn test_encode_adc_sbb() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("adcq %rax, %rbx\nsbbq %rcx, %rdx").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_enter_leave() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("enter $32, $0\nleave").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_loop_instructions() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("loop label\nloope label\nloopne label\nlabel:\nret")
.unwrap();
assert_eq!(result.instruction_count, 4);
}
#[test]
fn test_encode_conversion_sse() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec![
"cvtsi2ss %eax, %xmm0",
"cvtsi2sd %rax, %xmm0",
"cvtss2si %xmm0, %eax",
"cvtsd2si %xmm0, %rax",
"cvttss2si %xmm0, %eax",
"cvttsd2si %xmm0, %rax",
];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_encode_unpack_shuffle() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec![
"unpcklps %xmm0, %xmm1",
"unpckhps %xmm0, %xmm1",
"shufps $0x1B, %xmm0, %xmm1",
"pshufd $0x1B, %xmm0, %xmm1",
];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_encode_compare_sse() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("ucomiss %xmm0, %xmm1\nucomisd %xmm0, %xmm1")
.unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_sse41_extract_insert() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("pextrb $1, %xmm0, %eax\npinsrb $2, %eax, %xmm0")
.unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_pclmulqdq() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("pclmulqdq $0, %xmm0, %xmm1").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_adx() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("adcxq %rax, %rbx\nadoxq %rcx, %rdx").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_rdrand_rdseed() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("rdrand %rax\nrdseed %rbx").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_clflush_opt() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("clflush (%rax)\nclflushopt (%rbx)\nclwb (%rcx)")
.unwrap();
assert_eq!(result.instruction_count, 3);
}
#[test]
fn test_encode_movnt() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("movnti %rax, (%rbx)\nmovntdq %xmm0, (%rax)")
.unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_vmx() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec!["vmcall", "vmlaunch", "vmresume", "vmxoff"];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_encode_smep_smap() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("clac\nstac").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_swapgs() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("swapgs").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_monitor_mwait() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("monitor\nmwait").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_xsave_xrstor() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("xsave (%rax)\nxrstor (%rbx)").unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_serialize() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("serialize").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_endbr() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("endbr64").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_avx512_zmm_ops() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("vpaddd %zmm0, %zmm1, %zmm2").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_avx512_broadcast() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("vpbroadcastd %xmm0, %zmm1").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_avx512_compress_expand() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("vcompressps %zmm0, (%rax)\nvexpandps (%rbx), %zmm1")
.unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_encode_avx512_gather() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("vpgatherdd (%rax, %zmm0, 4), %zmm1").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_avx512_scatter() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("vpscatterdd %zmm0, (%rax, %zmm1, 4)").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_avx512_valign() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("valignd $2, %zmm0, %zmm1, %zmm2").unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_avx512_ternlog() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("vpternlogd $0x42, %zmm0, %zmm1, %zmm2")
.unwrap();
assert_eq!(result.instruction_count, 1);
}
#[test]
fn test_encode_avx512_fp16() {
let mut asm = X86MCFullAssembler::default_att();
let tests = vec![
"vaddph %zmm0, %zmm1, %zmm2",
"vmulph %zmm0, %zmm1, %zmm2",
"vfcmaddcph %zmm0, %zmm1, %zmm2",
];
for t in tests {
let result = asm.assemble(t).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_encode_tsx() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm
.assemble("xbegin label\nxend\nxabort $0xFF\nlabel:\nnop")
.unwrap();
assert_eq!(result.instruction_count, 4);
}
#[test]
fn test_assemble_multi_section_coff_style() {
let mut asm = X86MCFullAssembler::default_intel();
let source = r#"
.intel_syntax noprefix
.text
.globl main
main:
push rbp
mov rbp, rsp
mov eax, 42
pop rbp
ret
.data
my_data:
.quad 0x12345678
.byte 0x90
.rodata
my_string:
.asciz "hello"
"#;
let result = asm.assemble(source).unwrap();
assert!(result.instruction_count >= 5);
assert!(result.sections.len() >= 1);
}
#[test]
fn test_assemble_full_function_prolog_epilog() {
let mut asm = X86MCFullAssembler::default_att();
let source = r#"
.text
.globl my_func
my_func:
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
addq -16(%rbp), %rax
movq %rax, -24(%rbp)
movq -24(%rbp), %rax
leave
ret
"#;
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 11);
assert!(result.symbols.contains_key("my_func"));
assert!(result.symbols["my_func"].is_global);
}
#[test]
fn test_assemble_conditional_branches() {
let mut asm = X86MCFullAssembler::default_att();
let source = r#"
cmpq $0, %rax
je .Lzero
jg .Lpositive
jmp .Ldone
.Lzero:
movq $0, %rax
ret
.Lpositive:
movq $1, %rax
ret
.Ldone:
ret
"#;
let result = asm.assemble(source).unwrap();
assert!(result.instruction_count >= 8);
}
#[test]
fn test_assemble_sse_vector_add() {
let mut asm = X86MCFullAssembler::default_att();
let source = r#"
movaps (%rdi), %xmm0
movaps (%rsi), %xmm1
addps %xmm1, %xmm0
movaps %xmm0, (%rdx)
ret
"#;
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 4);
}
#[test]
fn test_assemble_rep_stosb() {
let mut asm = X86MCFullAssembler::default_att();
let source = "rep stosb\nret";
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_assemble_complex_equ() {
let mut asm = X86MCFullAssembler::default_att();
let source = r#"
.equ BUFFER_SIZE, 256
.equ ALIGNMENT, 16
.text
subq $BUFFER_SIZE, %rsp
ret
"#;
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 2); }
#[test]
fn test_assemble_data_section_directives() {
let mut asm = X86MCFullAssembler::default_att();
let source = r#"
.data
var1: .byte 0x41
var2: .short 0x1234
var3: .long 0x12345678
var4: .quad 0x1234567890ABCDEF
msg: .asciz "Hello, World!"
.align 8
pad: .zero 64
"#;
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 0);
assert!(result.sections.iter().any(|s| s.name == ".data"));
}
#[test]
fn test_mnemonic_parser_all_jumps() {
let parser = X86MnemonicParser::new(AsmSyntaxMode::ATT);
let jumps = [
"jo", "jno", "jb", "jae", "je", "jne", "jbe", "ja", "js", "jns", "jp", "jnp", "jl",
"jge", "jle", "jg", "jmp", "jcxz", "jecxz", "jrcxz",
];
for j in jumps.iter() {
assert!(parser.parse_att(j).is_some(), "Jump {} not recognized", j);
}
}
#[test]
fn test_mnemonic_parser_all_cmov() {
let parser = X86MnemonicParser::new(AsmSyntaxMode::ATT);
let cmovs = [
"cmovo", "cmovno", "cmovb", "cmovae", "cmove", "cmovne", "cmovbe", "cmova", "cmovs",
"cmovns", "cmovp", "cmovnp", "cmovl", "cmovge", "cmovle", "cmovg",
];
for c in cmovs.iter() {
assert!(parser.parse_att(c).is_some(), "CMOV {} not recognized", c);
}
}
#[test]
fn test_mnemonic_parser_all_setcc() {
let parser = X86MnemonicParser::new(AsmSyntaxMode::ATT);
let setccs = [
"seto", "setno", "setb", "setae", "sete", "setne", "setbe", "seta", "sets", "setns",
"setp", "setnp", "setl", "setge", "setle", "setg",
];
for s in setccs.iter() {
assert!(parser.parse_att(s).is_some(), "SETcc {} not recognized", s);
}
}
#[test]
fn test_asm_token_display_formatting() {
assert_eq!(format!("{}", X86AsmToken::Identifier("mov".into())), "mov");
assert_eq!(format!("{}", X86AsmToken::Integer(42)), "42");
assert_eq!(format!("{}", X86AsmToken::Comma), ",");
assert_eq!(format!("{}", X86AsmToken::EndOfFile), "<EOF>");
assert_eq!(format!("{}", X86AsmToken::Percent), "%");
assert_eq!(format!("{}", X86AsmToken::Dollar), "$");
assert_eq!(format!("{}", X86AsmToken::LBracket), "[");
assert_eq!(format!("{}", X86AsmToken::RBracket), "]");
}
#[test]
fn test_asm_token_kind_names() {
assert_eq!(
X86AsmToken::Identifier(String::new()).kind_name(),
"identifier"
);
assert_eq!(X86AsmToken::Integer(0).kind_name(), "integer");
assert_eq!(X86AsmToken::EndOfFile.kind_name(), "end-of-file");
}
#[test]
fn test_assembly_default_config() {
let config = AssemblerConfig::default();
assert_eq!(config.syntax, AsmSyntaxMode::ATT);
assert!(!config.pic);
assert_eq!(config.cpu, "x86-64");
assert!(config.relaxation);
assert!(!config.debug_info);
}
#[test]
fn test_assembly_pic_config() {
let config = AssemblerConfig {
pic: true,
..AssemblerConfig::default()
};
let handler = X86FixupHandler::new(config.pic);
assert!(handler.emit_got_plt);
assert!(handler.pic);
}
#[test]
fn test_register_encoding_high_regs() {
let asm = X86MCFullAssembler::default_att();
assert_eq!(asm.get_reg_encoding(X86Reg(R8)), 8);
assert_eq!(asm.get_reg_encoding(X86Reg(R9)), 9);
assert_eq!(asm.get_reg_encoding(X86Reg(R10)), 10);
assert_eq!(asm.get_reg_encoding(X86Reg(R15)), 15);
assert_eq!(asm.get_reg_encoding(X86Reg(RAX)), 0);
assert_eq!(asm.get_reg_encoding(X86Reg(RCX)), 1);
assert_eq!(asm.get_reg_encoding(X86Reg(RDX)), 2);
assert_eq!(asm.get_reg_encoding(X86Reg(RBX)), 3);
assert_eq!(asm.get_reg_encoding(X86Reg(RSP)), 4);
assert_eq!(asm.get_reg_encoding(X86Reg(RBP)), 5);
assert_eq!(asm.get_reg_encoding(X86Reg(RSI)), 6);
assert_eq!(asm.get_reg_encoding(X86Reg(RDI)), 7);
}
#[test]
fn test_register_encoding_xmm() {
let asm = X86MCFullAssembler::default_att();
assert_eq!(asm.get_reg_encoding(X86Reg(XMM0)), 0);
assert_eq!(asm.get_reg_encoding(X86Reg(XMM15)), 15);
assert_eq!(asm.get_reg_encoding(X86Reg(XMM16)), 16);
assert_eq!(asm.get_reg_encoding(X86Reg(XMM31)), 31);
}
#[test]
fn test_size_suffix_sizes() {
assert_eq!(SizeSuffix::Byte.byte_size(), 1);
assert_eq!(SizeSuffix::Word.byte_size(), 2);
assert_eq!(SizeSuffix::Long.byte_size(), 4);
assert_eq!(SizeSuffix::Quad.byte_size(), 8);
assert_eq!(SizeSuffix::Single.byte_size(), 4);
assert_eq!(SizeSuffix::Double.byte_size(), 8);
assert_eq!(SizeSuffix::PackedSingle.byte_size(), 16);
assert_eq!(SizeSuffix::PackedDouble.byte_size(), 16);
assert_eq!(SizeSuffix::ScalarSingle.byte_size(), 4);
assert_eq!(SizeSuffix::ScalarDouble.byte_size(), 8);
assert_eq!(SizeSuffix::TByte.byte_size(), 10);
}
#[test]
fn test_rounding_mode_parsing() {
assert_eq!(
RoundingMode::from_modifier("{rn-sae}"),
Some(RoundingMode::RNE)
);
assert_eq!(
RoundingMode::from_modifier("{rd-sae}"),
Some(RoundingMode::RD)
);
assert_eq!(
RoundingMode::from_modifier("{ru-sae}"),
Some(RoundingMode::RU)
);
assert_eq!(
RoundingMode::from_modifier("{rz-sae}"),
Some(RoundingMode::RZ)
);
assert_eq!(
RoundingMode::from_modifier("{sae}"),
Some(RoundingMode::SAE)
);
}
#[test]
fn test_branch_relaxation_short_to_near() {
let handler = X86FixupHandler::new(false);
let data = vec![0x74, 0x00]; let relaxed = handler.relax_branches(&data).unwrap();
assert!(!relaxed.is_empty());
}
#[test]
fn test_fixup_handler_got_plt() {
let mut handler = X86FixupHandler::new(true);
handler.add_got_plt_fixup(0, "printf".to_string());
assert_eq!(handler.fixups.len(), 1);
assert_eq!(handler.fixups[0].kind, X86FixupKind::GotPcRel);
assert_eq!(handler.fixups[0].symbol, Some("printf".to_string()));
}
#[test]
fn test_fixup_handler_clear() {
let mut handler = X86FixupHandler::new(false);
handler.add_branch_fixup(0, Some("target".to_string()), true);
assert_eq!(handler.fixups.len(), 1);
handler.clear();
assert_eq!(handler.fixups.len(), 0);
}
#[test]
fn test_directive_align_noop() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.current_offset = 8;
let bytes = proc
.process(&AsmDirective::Align {
alignment: 4,
fill: Some(0x90),
max: Some(4),
})
.unwrap();
assert_eq!(bytes.len(), 0); }
#[test]
fn test_directive_org_backwards_error() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.current_offset = 100;
let result = proc.process(&AsmDirective::Org(50));
assert!(result.is_err());
}
#[test]
fn test_directive_type_function() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::Type {
name: "myfunc".to_string(),
typ: "@function".to_string(),
})
.unwrap();
let sym = proc.symbols.get("myfunc").unwrap();
assert!(sym.is_function);
assert_eq!(sym.typ, Some("@function".to_string()));
}
#[test]
fn test_directive_size() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.define_symbol("myfunc");
proc.process(&AsmDirective::Size {
name: "myfunc".to_string(),
size: "64".to_string(),
})
.unwrap();
assert_eq!(proc.symbols["myfunc"].size, Some(64));
}
#[test]
fn test_directive_weak_hidden_protected() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::Weak("sym".to_string()))
.unwrap();
assert!(proc.symbols["sym"].is_weak);
proc.process(&AsmDirective::Hidden("sym".to_string()))
.unwrap();
proc.process(&AsmDirective::Protected("sym".to_string()))
.unwrap();
}
#[test]
fn test_directive_comm_lcomm() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::Comm {
name: "global_var".to_string(),
size: 1024,
alignment: Some(16),
})
.unwrap();
assert!(proc.symbols["global_var"].is_global);
assert_eq!(proc.symbols["global_var"].size, Some(1024));
proc.process(&AsmDirective::LComm {
name: "local_var".to_string(),
size: 256,
alignment: Some(8),
})
.unwrap();
assert!(proc.symbols["local_var"].is_local);
}
#[test]
fn test_directive_macro_definition() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::Macro {
name: "push_all".to_string(),
body: vec!["push %rax".to_string(), "push %rbx".to_string()],
})
.unwrap();
assert!(proc.macros.contains_key("push_all"));
assert_eq!(proc.macros["push_all"].len(), 2);
}
#[test]
fn test_directive_conditional() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::If("1".to_string())).unwrap();
assert_eq!(proc.cond_stack.len(), 1);
assert!(proc.cond_stack[0]);
proc.process(&AsmDirective::Else).unwrap();
assert!(!proc.cond_stack[0]);
proc.process(&AsmDirective::EndIf).unwrap();
assert_eq!(proc.cond_stack.len(), 0);
}
#[test]
fn test_directive_ifdef_ifndef() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.define_symbol("MY_SYMBOL");
proc.process(&AsmDirective::IfDef("MY_SYMBOL".to_string()))
.unwrap();
assert!(proc.cond_stack[0]);
proc.process(&AsmDirective::EndIf).unwrap();
proc.process(&AsmDirective::IfNDef("UNDEFINED".to_string()))
.unwrap();
assert!(proc.cond_stack[0]);
proc.process(&AsmDirective::EndIf).unwrap();
}
#[test]
fn test_directive_file_loc_line() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::File {
name: "test.s".to_string(),
})
.unwrap();
assert_eq!(proc.file_info, Some(("test.s".to_string(), 1)));
proc.process(&AsmDirective::Loc {
file: 1,
line: 42,
column: Some(10),
})
.unwrap();
assert!(proc.file_info.is_some());
proc.process(&AsmDirective::Line(99)).unwrap();
assert_eq!(proc.file_info.unwrap().1, 99);
}
#[test]
fn test_directive_ident_end() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::Ident("GCC: (GNU) 12.2.0".to_string()))
.unwrap();
proc.process(&AsmDirective::End).unwrap();
}
#[test]
fn test_directive_process_all_data_types() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
let result = proc.process(&AsmDirective::Byte(vec![1, 2, 3])).unwrap();
assert_eq!(result, vec![1, 2, 3]);
let result = proc.process(&AsmDirective::Short(vec![0x1234])).unwrap();
assert_eq!(result, vec![0x34, 0x12]);
let result = proc.process(&AsmDirective::Long(vec![0xDEADBEEF])).unwrap();
assert_eq!(result, vec![0xEF, 0xBE, 0xAD, 0xDE]);
let result = proc.process(&AsmDirective::Octa(vec![0x42])).unwrap();
assert_eq!(result.len(), 16);
}
#[test]
fn test_parser_split_operand_nested() {
let mut parser = X86AsmParser::new(AsmSyntaxMode::ATT);
let ops = parser.split_operand_string("(%rax, %rcx, 4), %rbx");
assert_eq!(ops.len(), 2);
assert_eq!(ops[0], "(%rax, %rcx, 4)");
assert_eq!(ops[1], "%rbx");
let mut parser = X86AsmParser::new(AsmSyntaxMode::Intel);
let ops = parser.split_operand_string("[rax + rcx*4], rbx");
assert_eq!(ops.len(), 2);
}
#[test]
fn test_lexer_at_sign_handling() {
let mut lexer = X86AsmLexer::new("@function");
let tokens = lexer.lex_all();
assert!(matches!(tokens[0].0, X86AsmToken::Identifier(ref s) if s == "@function"));
}
#[test]
fn test_lexer_dot_labels() {
let mut lexer = X86AsmLexer::new(".L1: mov rax, rbx");
let tokens = lexer.lex_all();
let ids: Vec<_> = tokens
.iter()
.filter_map(|(t, _, _)| match t {
X86AsmToken::Identifier(s) => Some(s.as_str()),
_ => None,
})
.collect();
assert!(ids.contains(&".L1"));
}
#[test]
fn test_lexer_block_comment() {
let mut lexer = X86AsmLexer::new("mov /* this is a\n block comment */ rax");
let tokens = lexer.lex_all();
let ids: Vec<_> = tokens
.iter()
.filter_map(|(t, _, _)| match t {
X86AsmToken::Identifier(s) => Some(s.as_str()),
_ => None,
})
.collect();
assert_eq!(ids, vec!["mov", "rax"]);
}
#[test]
fn test_lexer_char_literal() {
let mut lexer = X86AsmLexer::new("'a'");
let tokens = lexer.lex_all();
assert!(matches!(tokens[0].0, X86AsmToken::CharLiteral('a')));
}
#[test]
fn test_lexer_escape_sequences() {
let mut lexer = X86AsmLexer::new("\"hello\\nworld\"");
let tokens = lexer.lex_all();
assert!(matches!(&tokens[0].0, X86AsmToken::StringLiteral(ref s) if s == "hello\nworld"));
}
#[test]
fn test_operand_parser_segment_override_att() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::ATT);
let op = parser.parse_operand_att("%fs:0(%rax)");
assert!(op.is_some());
if let Some(AsmOperand::Mem(mem)) = op {
assert_eq!(mem.segment, Some(X86Reg(FS)));
assert_eq!(mem.displacement, 0);
assert_eq!(mem.base, Some(X86Reg(RAX)));
}
let op = parser.parse_operand_att("%gs:8(%rbx)");
if let Some(AsmOperand::Mem(mem)) = op {
assert_eq!(mem.segment, Some(X86Reg(GS)));
assert_eq!(mem.displacement, 8);
}
}
#[test]
fn test_operand_parser_segment_override_intel() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::Intel);
let op = parser.parse_operand_intel("fs:[rax]");
if let Some(AsmOperand::Mem(mem)) = op {
assert_eq!(mem.segment, Some(X86Reg(FS)));
}
}
#[test]
fn test_operand_parser_rip_relative_intel() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::Intel);
let op = parser.parse_operand_intel("[rip + symbol]");
if let Some(AsmOperand::Mem(mem)) = op {
assert!(mem.is_rip_relative);
}
}
#[test]
fn test_operand_parser_sib_full_att() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::ATT);
let op = parser.parse_operand_att("16(%rax, %rcx, 8)");
if let Some(AsmOperand::Mem(mem)) = op {
assert_eq!(mem.displacement, 16);
assert_eq!(mem.base, Some(X86Reg(RAX)));
assert_eq!(mem.index, Some(X86Reg(RCX)));
assert_eq!(mem.scale, 8);
}
}
#[test]
fn test_operand_parser_symbol_no_rip() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::ATT);
let op = parser.parse_operand_att("my_label");
assert!(matches!(op, Some(AsmOperand::Label(ref s)) if s == "my_label"));
}
#[test]
fn test_parse_full_instruction_att_basic() {
let mut parser = X86AsmParser::new(AsmSyntaxMode::ATT);
let inst = parser.parse_full_instruction("movq %rax, %rbx");
assert!(inst.is_some());
let inst = inst.unwrap();
assert_eq!(inst.mnemonic, "mov");
assert_eq!(inst.operands.len(), 2);
}
#[test]
fn test_parse_full_instruction_intel_basic() {
let mut parser = X86AsmParser::new(AsmSyntaxMode::Intel);
let inst = parser.parse_full_instruction("mov rax, rbx");
assert!(inst.is_some());
}
#[test]
fn test_parse_directive_line() {
let mut parser = X86AsmParser::new(AsmSyntaxMode::ATT);
let result = parser.parse_line(".text");
assert!(matches!(
result,
Some(AsmLine::Directive(AsmDirective::Text))
));
let result = parser.parse_line(".globl main");
assert!(
matches!(result, Some(AsmLine::Directive(AsmDirective::Global(ref s))) if s == "main")
);
let result = parser.parse_line(".byte 0x90, 0xCC");
assert!(matches!(
result,
Some(AsmLine::Directive(AsmDirective::Byte(_)))
));
}
#[test]
fn test_parse_label_definitions() {
let mut parser = X86AsmParser::new(AsmSyntaxMode::ATT);
assert!(
matches!(parser.parse_line("my_label:"), Some(AsmLine::Label(ref s)) if s == "my_label")
);
assert!(matches!(parser.parse_line(".L1:"), Some(AsmLine::Label(ref s)) if s == ".L1"));
assert!(matches!(parser.parse_line("1:"), Some(AsmLine::Label(ref s)) if s == "1"));
}
#[test]
fn test_parse_comment_lines() {
let mut parser = X86AsmParser::new(AsmSyntaxMode::ATT);
assert!(matches!(
parser.parse_line("# comment"),
Some(AsmLine::Comment(_))
));
assert!(matches!(
parser.parse_line("// comment"),
Some(AsmLine::Comment(_))
));
assert!(matches!(
parser.parse_line("; comment"),
Some(AsmLine::Comment(_))
));
}
#[test]
fn test_parse_empty_lines() {
let mut parser = X86AsmParser::new(AsmSyntaxMode::ATT);
assert!(matches!(parser.parse_line(""), Some(AsmLine::Empty)));
assert!(matches!(parser.parse_line(" "), Some(AsmLine::Empty)));
}
#[test]
fn test_parse_section_directive() {
let mut parser = X86AsmParser::new(AsmSyntaxMode::ATT);
let result = parser.parse_line(".section .my_section, \"ax\", @progbits");
assert!(matches!(
result,
Some(AsmLine::Directive(AsmDirective::Section { .. }))
));
}
#[test]
fn test_memref_default() {
let mem = MemRef::default();
assert!(mem.segment.is_none());
assert!(mem.base.is_none());
assert!(mem.index.is_none());
assert_eq!(mem.scale, 0);
assert_eq!(mem.displacement, 0);
assert!(mem.symbol.is_none());
assert!(!mem.is_rip_relative);
}
#[test]
fn test_cfi_state_default() {
let state = CfiState::default();
assert!(!state.is_active);
assert!(state.cfa_reg.is_none());
assert!(state.cfa_offset.is_none());
assert!(state.reg_offsets.is_empty());
}
#[test]
fn test_directive_processor_reset() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::Byte(vec![1, 2, 3])).unwrap();
proc.define_symbol("test");
assert!(!proc.data.is_empty());
proc.reset();
assert!(proc.data.is_empty());
assert_eq!(proc.current_offset, 0);
assert!(proc.symbols.is_empty());
}
#[test]
fn test_assembler_reset() {
let mut asm = X86MCFullAssembler::default_att();
let _ = asm.assemble("nop").unwrap();
asm.reset();
let result = asm.assemble("ret").unwrap();
assert_eq!(result.data, vec![0xC3]);
}
#[test]
fn test_pattern_fill_values() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
let bytes = proc
.process(&AsmDirective::Fill {
count: 3,
value: 0xDEAD,
size: Some(2),
})
.unwrap();
assert_eq!(bytes, vec![0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE]);
}
#[test]
fn test_fixup_kind_conversion() {
assert_eq!(X86FixupKind::Rel32.to_mc_fixup_kind(), MCFixupKind::PcRel32);
assert_eq!(X86FixupKind::Abs64.to_mc_fixup_kind(), MCFixupKind::Abs64);
assert_eq!(
X86FixupKind::GotPcRel.to_mc_fixup_kind(),
MCFixupKind::RelocGotPcRel
);
assert_eq!(
X86FixupKind::Plt32.to_mc_fixup_kind(),
MCFixupKind::RelocPlt32
);
}
#[test]
fn test_x86_fixup_new_and_size() {
let fixup = X86Fixup {
offset: 42,
kind: X86FixupKind::Rel32,
symbol: Some("target".into()),
addend: -4,
branch_size: 5,
};
assert_eq!(fixup.offset, 42);
assert_eq!(fixup.kind.size_bytes(), 4);
assert!(fixup.kind.is_pc_relative());
}
#[test]
fn test_all_output_formats() {
assert_eq!(OutputFormat::Binary as u32, 0);
assert_eq!(OutputFormat::ElF as u32, 1);
assert_eq!(OutputFormat::MachO as u32, 2);
assert_eq!(OutputFormat::Coff as u32, 3);
}
#[test]
fn test_full_register_coverage() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::ATT);
for name in &[
"%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
] {
assert!(parser.parse_operand_att(name).is_some(), "Failed: {}", name);
}
for name in &["%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"] {
assert!(parser.parse_operand_att(name).is_some(), "Failed: {}", name);
}
for name in &["%cs", "%ds", "%ss", "%es", "%fs", "%gs"] {
assert!(parser.parse_operand_att(name).is_some(), "Failed: {}", name);
}
}
#[test]
fn test_encoder_mov_rr() {
let bytes = X86InstructionEncoder::encode_mov_rr(0, 1, SizeSuffix::Quad);
assert_eq!(bytes, vec![0x48, 0x89, 0xC8]);
}
#[test]
fn test_encoder_mov_ri_byte() {
let bytes = X86InstructionEncoder::encode_mov_ri(0, 0x42, SizeSuffix::Byte);
assert_eq!(bytes, vec![0xB0, 0x42]);
}
#[test]
fn test_encoder_mov_ri_quad() {
let bytes = X86InstructionEncoder::encode_mov_ri(0, 0x42, SizeSuffix::Quad);
assert_eq!(bytes[0], 0x48);
assert_eq!(bytes[1], 0xB8);
assert_eq!(bytes.len(), 10);
}
#[test]
fn test_encoder_alu_rr_add() {
let bytes = X86InstructionEncoder::encode_alu_rr(0x01, 0, 1, true, false);
assert_eq!(bytes, vec![0x48, 0x01, 0xC8]);
}
#[test]
fn test_encoder_push_pop() {
let push_rax = X86InstructionEncoder::encode_push_reg(0, true);
assert_eq!(push_rax, vec![0x50]);
let pop_rbx = X86InstructionEncoder::encode_pop_reg(3, true);
assert_eq!(pop_rbx, vec![0x5B]);
}
#[test]
fn test_encoder_push_high_reg() {
let push_r8 = X86InstructionEncoder::encode_push_reg(8, true);
assert_eq!(push_r8[0], 0x41);
assert_eq!(push_r8[1], 0x50);
}
#[test]
fn test_encoder_jcc_rel8() {
let bytes = X86InstructionEncoder::encode_jcc_rel8(4, 0); assert_eq!(bytes, vec![0x74, 0x00]);
}
#[test]
fn test_encoder_jcc_rel32() {
let bytes = X86InstructionEncoder::encode_jcc_rel32(4, 0);
assert_eq!(bytes[0], 0x0F);
assert_eq!(bytes[1], 0x84);
assert_eq!(bytes[2..6], [0, 0, 0, 0]);
}
#[test]
fn test_encoder_alu_ri() {
let bytes = X86InstructionEncoder::encode_alu_ri(0, 0, 42, true);
assert_eq!(bytes, vec![0x48, 0x83, 0xC0, 0x2A]);
}
#[test]
fn test_encoder_shift_imm() {
let bytes = X86InstructionEncoder::encode_shift_imm(4, 0, 1, true);
assert_eq!(bytes, vec![0x48, 0xD1, 0xE0]);
let bytes = X86InstructionEncoder::encode_shift_imm(4, 0, 4, true);
assert_eq!(bytes, vec![0x48, 0xC1, 0xE0, 0x04]);
}
#[test]
fn test_encoder_shift_cl() {
let bytes = X86InstructionEncoder::encode_shift_cl(4, 0, true);
assert_eq!(bytes, vec![0x48, 0xD3, 0xE0]);
}
#[test]
fn test_encoder_inc_dec() {
let inc_rax = X86InstructionEncoder::encode_inc_dec(true, 0, true);
assert_eq!(inc_rax[0], 0x48);
assert_eq!(inc_rax[1], 0xFF);
assert_eq!(inc_rax[2], 0xC0);
let dec_rbx = X86InstructionEncoder::encode_inc_dec(false, 3, true);
assert_eq!(dec_rbx[0], 0x48);
assert_eq!(dec_rbx[1], 0xFF);
assert_eq!(dec_rbx[2], 0xCB);
}
#[test]
fn test_encoder_not_neg() {
let not_rax = X86InstructionEncoder::encode_not_neg(true, 0, true);
assert_eq!(not_rax, vec![0x48, 0xF7, 0xD0]);
let neg_rbx = X86InstructionEncoder::encode_not_neg(false, 3, true);
assert_eq!(neg_rbx, vec![0x48, 0xF7, 0xDB]);
}
#[test]
fn test_encoder_mul_div() {
let mul_rbx = X86InstructionEncoder::encode_mul_div(4, 3, true);
assert_eq!(mul_rbx, vec![0x48, 0xF7, 0xE3]);
let div_rcx = X86InstructionEncoder::encode_mul_div(6, 1, true);
assert_eq!(div_rcx, vec![0x48, 0xF7, 0xF1]);
}
#[test]
fn test_encoder_test_rr() {
let bytes = X86InstructionEncoder::encode_test_rr(0, 0, true);
assert_eq!(bytes, vec![0x48, 0x85, 0xC0]);
}
#[test]
fn test_encoder_lea() {
let bytes = X86InstructionEncoder::encode_lea(0, 4, None, 1, 8, true);
assert_eq!(bytes[0], 0x48);
assert_eq!(bytes[1], 0x8D);
}
#[test]
fn test_encoder_xchg_ax() {
let bytes = X86InstructionEncoder::encode_xchg_ax(3, true);
assert_eq!(bytes, vec![0x48, 0x93]);
}
#[test]
fn test_encoder_bswap() {
let bytes = X86InstructionEncoder::encode_bswap(0, true);
assert_eq!(bytes, vec![0x48, 0x0F, 0xC8]);
}
#[test]
fn test_encoder_call_jmp_reg() {
let call_rax = X86InstructionEncoder::encode_call_jmp_reg(true, 0, true);
assert_eq!(call_rax, vec![0xFF, 0xD0]);
let jmp_rbx = X86InstructionEncoder::encode_call_jmp_reg(false, 3, true);
assert_eq!(jmp_rbx, vec![0xFF, 0xE3]);
}
#[test]
fn test_encoder_setcc() {
let bytes = X86InstructionEncoder::encode_setcc(4, 0);
assert_eq!(bytes, vec![0x0F, 0x94, 0xC0]);
}
#[test]
fn test_encoder_cmovcc() {
let bytes = X86InstructionEncoder::encode_cmovcc(4, 0, 1, true);
assert_eq!(bytes, vec![0x48, 0x0F, 0x44, 0xC1]);
}
#[test]
fn test_encoder_movsx() {
let bytes = X86InstructionEncoder::encode_movsx_movzx(true, 0, 0, 1, 8);
assert_eq!(bytes, vec![0x48, 0x0F, 0xBE, 0xC0]);
}
#[test]
fn test_encoder_movsxd() {
let bytes = X86InstructionEncoder::encode_movsx_movzx(true, 0, 0, 4, 8);
assert_eq!(bytes, vec![0x48, 0x63, 0xC0]);
}
#[test]
fn test_encoder_sse_scalar() {
let bytes = X86InstructionEncoder::encode_sse_scalar(0xF3, 0x58, 1, 0);
assert_eq!(bytes, vec![0xF3, 0x0F, 0x58, 0xC8]);
}
#[test]
fn test_encoder_sse_packed() {
let bytes = X86InstructionEncoder::encode_sse_packed(0, 0x58, 1, 0);
assert_eq!(bytes, vec![0x0F, 0x58, 0xC8]);
let bytes = X86InstructionEncoder::encode_sse_packed(0x66, 0x58, 1, 0);
assert_eq!(bytes, vec![0x66, 0x0F, 0x58, 0xC8]);
}
#[test]
fn test_encoder_avx_binop() {
let bytes = X86InstructionEncoder::encode_avx_binop(0, 0x58, 2, 1, 0, false);
assert_eq!(bytes[0], 0xC5);
}
#[test]
fn test_encoder_avx512_binop() {
let bytes = X86InstructionEncoder::encode_avx512_binop(0x66, 0xFE, 2, 1, 0, None, true);
assert_eq!(bytes[0], 0x62);
}
#[test]
fn test_condition_number_comprehensive() {
let conditions = vec![
("jo", 0),
("jno", 1),
("jb", 2),
("jae", 3),
("je", 4),
("jne", 5),
("jbe", 6),
("ja", 7),
("js", 8),
("jns", 9),
("jp", 10),
("jnp", 11),
("jl", 12),
("jge", 13),
("jle", 14),
("jg", 15),
];
for (name, expected) in conditions {
let actual = X86InstructionEncoder::get_condition_number(name);
assert_eq!(
actual,
Some(expected),
"Condition {} should be {}",
name,
expected
);
}
}
#[test]
fn test_alu_extension_comprehensive() {
let alu_ops = vec![
("add", 0),
("or", 1),
("adc", 2),
("sbb", 3),
("and", 4),
("sub", 5),
("xor", 6),
("cmp", 7),
];
for (name, expected) in alu_ops {
let actual = X86InstructionEncoder::get_alu_extension(name);
assert_eq!(
actual,
Some(expected),
"ALU {} should be {}",
name,
expected
);
}
}
#[test]
fn test_shift_extension_comprehensive() {
let shift_ops = vec![
("rol", 0),
("ror", 1),
("rcl", 2),
("rcr", 3),
("shl", 4),
("sal", 4),
("shr", 5),
("sar", 7),
];
for (name, expected) in shift_ops {
let actual = X86InstructionEncoder::get_shift_extension(name);
assert_eq!(
actual,
Some(expected),
"Shift {} should be {}",
name,
expected
);
}
}
#[test]
fn test_mul_div_extension_comprehensive() {
let ops = vec![("mul", 4), ("imul", 5), ("div", 6), ("idiv", 7)];
for (name, expected) in ops {
let actual = X86InstructionEncoder::get_mul_div_extension(name);
assert_eq!(actual, Some(expected));
}
}
#[test]
fn test_modrm_all_modes() {
assert_eq!(X86EncodingEngine::modrm(0, 1, 2), 0x0A);
assert_eq!(X86EncodingEngine::modrm(1, 2, 3), 0x53);
assert_eq!(X86EncodingEngine::modrm(2, 3, 4), 0x9C);
assert_eq!(X86EncodingEngine::modrm(3, 4, 5), 0xDD);
}
#[test]
fn test_sib_all_scales() {
assert_eq!(X86EncodingEngine::compute_sib(0, 1, 0), 0x08);
assert_eq!(X86EncodingEngine::compute_sib(1, 2, 1), 0x51);
assert_eq!(X86EncodingEngine::compute_sib(2, 3, 2), 0x9A);
assert_eq!(X86EncodingEngine::compute_sib(3, 0, 3), 0xC3);
}
#[test]
fn test_rex_all_bits() {
assert_eq!(
X86EncodingEngine::compute_rex(true, false, false, false),
Some(0x48)
);
assert_eq!(
X86EncodingEngine::compute_rex(false, true, false, false),
Some(0x44)
);
assert_eq!(
X86EncodingEngine::compute_rex(false, false, true, false),
Some(0x42)
);
assert_eq!(
X86EncodingEngine::compute_rex(false, false, false, true),
Some(0x41)
);
assert_eq!(
X86EncodingEngine::compute_rex(true, true, true, true),
Some(0x4F)
);
assert_eq!(
X86EncodingEngine::compute_rex(false, false, false, false),
None
);
}
#[test]
fn test_needs_rex_helpers() {
assert!(!X86EncodingEngine::needs_rex_r(0));
assert!(X86EncodingEngine::needs_rex_r(8));
assert!(X86EncodingEngine::needs_rex_r(15));
assert!(!X86EncodingEngine::needs_rex_b(7));
assert!(X86EncodingEngine::needs_rex_b(8));
}
#[test]
fn test_scale_bits() {
assert_eq!(X86EncodingEngine::scale_bits(1), 0);
assert_eq!(X86EncodingEngine::scale_bits(2), 1);
assert_eq!(X86EncodingEngine::scale_bits(4), 2);
assert_eq!(X86EncodingEngine::scale_bits(8), 3);
assert_eq!(X86EncodingEngine::scale_bits(16), 0);
}
#[test]
fn test_encode_immediate_sizes() {
assert_eq!(X86EncodingEngine::encode_immediate(5, 1), vec![5]);
assert_eq!(
X86EncodingEngine::encode_immediate(0x1234, 2),
vec![0x34, 0x12]
);
assert_eq!(
X86EncodingEngine::encode_immediate(0x12345678, 4),
vec![0x78, 0x56, 0x34, 0x12]
);
assert_eq!(
X86EncodingEngine::encode_immediate(0x1234567890ABCDEFi64, 8).len(),
8
);
}
#[test]
fn test_emit_opcode_two_byte_escape() {
let mut bytes = Vec::new();
X86EncodingEngine::emit_opcode(&mut bytes, 0, 0x38, true);
assert_eq!(bytes, vec![0x0F, 0x38]);
}
#[test]
fn test_mandatory_prefix_helper() {
assert_eq!(X86EncodingEngine::mandatory_prefix(0x66), Some(0x66));
assert_eq!(X86EncodingEngine::mandatory_prefix(0xF2), Some(0xF2));
assert_eq!(X86EncodingEngine::mandatory_prefix(0xF3), Some(0xF3));
assert_eq!(X86EncodingEngine::mandatory_prefix(0), None);
}
#[test]
fn test_encoding_engine_vex_2byte_variants() {
let vex = X86EncodingEngine::encode_vex_2byte(false, 0u8, false, 0u8);
assert_eq!(vex, vec![0xC5, 0xF8]);
let vex = X86EncodingEngine::encode_vex_2byte(false, 0, true, 0);
assert_eq!(vex, vec![0xC5, 0x7C]);
let vex = X86EncodingEngine::encode_vex_2byte(false, 0, false, 1);
assert_eq!(vex, vec![0xC5, 0xF9]);
let vex = X86EncodingEngine::encode_vex_2byte(false, 0, false, 2);
assert_eq!(vex, vec![0xC5, 0xFA]);
let vex = X86EncodingEngine::encode_vex_2byte(false, 0, false, 3);
assert_eq!(vex, vec![0xC5, 0xFB]);
}
#[test]
fn test_build_rex_comprehensive() {
assert_eq!(X86EncodingEngine::build_rex(true, 0, 0, 0), Some(0x48));
assert_eq!(X86EncodingEngine::build_rex(false, 8, 0, 0), Some(0x44));
assert_eq!(X86EncodingEngine::build_rex(false, 0, 8, 0), Some(0x42));
assert_eq!(X86EncodingEngine::build_rex(false, 0, 0, 8), Some(0x41));
assert_eq!(X86EncodingEngine::build_rex(false, 0, 0, 0), None);
}
#[test]
fn test_encode_immediate_negative() {
assert_eq!(X86EncodingEngine::encode_immediate(-1, 1), vec![0xFF]);
assert_eq!(X86EncodingEngine::encode_immediate(-128, 1), vec![0x80]);
assert_eq!(
X86EncodingEngine::encode_immediate(-32768, 2),
vec![0x00, 0x80]
);
}
#[test]
fn test_assemble_many_instructions() {
let mut asm = X86MCFullAssembler::default_att();
let mut source = String::new();
for _ in 0..100 {
source.push_str("nop\n");
}
source.push_str("ret\n");
let result = asm.assemble(&source).unwrap();
assert_eq!(result.instruction_count, 101);
}
#[test]
fn test_assemble_mixed_syntax_features() {
let mut asm = X86MCFullAssembler::default_att();
let source = r#"
.text
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movq $42, -8(%rbp)
movq -8(%rbp), %rax
addq $1, %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
leave
ret
"#;
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 10);
}
#[test]
fn test_assemble_align_between_functions() {
let mut asm = X86MCFullAssembler::default_att();
let source = r#"
.text
.align 16
.globl func1
func1:
movq $1, %rax
ret
.align 16
.globl func2
func2:
movq $2, %rax
ret
"#;
let result = asm.assemble(source).unwrap();
assert_eq!(result.instruction_count, 4);
}
#[test]
fn test_memory_att_all_forms() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::ATT);
assert!(parser.parse_operand_att("(%rax)").is_some());
assert!(parser.parse_operand_att("8(%rbp)").is_some());
assert!(parser.parse_operand_att("(%rax, %rcx)").is_some());
assert!(parser.parse_operand_att("16(%rbx, %rsi, 8)").is_some());
assert!(parser.parse_operand_att("array(%rsi)").is_some());
}
#[test]
fn test_memory_intel_all_forms() {
let mut parser = X86OperandParser::new(AsmSyntaxMode::Intel);
assert!(parser.parse_operand_intel("[rax]").is_some());
assert!(parser.parse_operand_intel("[rax + rcx]").is_some());
assert!(parser.parse_operand_intel("[rax + rcx*4]").is_some());
assert!(parser.parse_operand_intel("[rax + rcx*8 + 16]").is_some());
assert!(parser.parse_operand_intel("[rax + rcx*2 - 8]").is_some());
assert!(parser.parse_operand_intel("BYTE PTR [rax]").is_some());
assert!(parser.parse_operand_intel("DWORD PTR [rax]").is_some());
assert!(parser.parse_operand_intel("QWORD PTR [rax]").is_some());
}
#[test]
fn test_all_mnemonics_no_duplicates() {
let mut sorted: Vec<&str> = ALL_MNEMONICS.to_vec();
sorted.sort();
sorted.dedup();
assert_eq!(sorted.len(), ALL_MNEMONICS.len());
}
#[test]
fn test_symbol_lifecycle() {
let mut asm = X86MCFullAssembler::default_att();
let source = r#"
.globl start
.type start, @function
start:
movq $42, %rax
ret
.size start, .-start
"#;
let result = asm.assemble(source).unwrap();
let sym = result.symbols.get("start").unwrap();
assert!(sym.is_global);
assert!(sym.is_function);
assert!(sym.size.is_some());
}
#[test]
fn test_directive_skip_and_space() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
let result = proc.process(&AsmDirective::Space(64)).unwrap();
assert_eq!(result.len(), 64);
let result = proc.process(&AsmDirective::Skip(32)).unwrap();
assert_eq!(result.len(), 32);
}
#[test]
fn test_cfi_full_sequence() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
proc.process(&AsmDirective::CfiStartProc).unwrap();
assert!(proc.cfi_state.is_active);
proc.process(&AsmDirective::CfiDefCfa {
reg: "rsp".to_string(),
offset: 8,
})
.unwrap();
assert!(proc.cfi_state.cfa_reg.is_some());
proc.process(&AsmDirective::CfiOffset {
reg: "rip".to_string(),
offset: -8,
})
.unwrap();
assert!(!proc.cfi_state.reg_offsets.is_empty());
proc.process(&AsmDirective::CfiEndProc).unwrap();
assert!(!proc.cfi_state.is_active);
}
#[test]
fn test_lexer_line_column_tracking() {
let mut lexer = X86AsmLexer::new("mov rax\nadd rbx");
let tokens = lexer.lex_all();
assert_eq!(tokens[0].1, 1);
assert_eq!(tokens[1].1, 1);
let newline_idx = tokens
.iter()
.position(|(t, _, _)| matches!(t, X86AsmToken::EndOfLine));
if let Some(idx) = newline_idx {
if idx + 1 < tokens.len() {
assert_eq!(tokens[idx + 1].1, 2);
}
}
}
#[test]
fn test_lexer_all_single_char_tokens() {
let mut lexer = X86AsmLexer::new(", : [ ] ( ) + - * / @ ! ~ & | ^");
let tokens = lexer.lex_all();
let kinds: Vec<_> = tokens.iter().map(|(t, _, _)| t.kind_name()).collect();
for expected in &[
"comma",
"colon",
"lbracket",
"rbracket",
"lparen",
"rparen",
"plus",
"minus",
"star",
"slash",
"at",
"exclaim",
"tilde",
"ampersand",
"pipe",
"caret",
] {
assert!(kinds.contains(expected), "Missing token: {}", expected);
}
}
#[test]
fn test_parsed_instruction_fields() {
let inst = ParsedInstruction {
mnemonic: "mov".to_string(),
opcode: X86FullOpcode::MOVrr,
operands: Vec::new(),
size_suffix: Some(SizeSuffix::Quad),
encoding_form: EncodingForm::LegacyREX,
is_branch: false,
is_call: false,
loc: SourceLocation { line: 1, column: 0 },
};
assert_eq!(inst.mnemonic, "mov");
assert_eq!(inst.opcode, X86FullOpcode::MOVrr);
assert!(!inst.is_branch);
}
#[test]
fn test_opcode_table_lookup() {
let entry = OpcodeEncodingEntry::lookup("nop");
assert!(entry.is_some());
let entry = entry.unwrap();
assert_eq!(entry.opcode_bytes, &[0x90]);
assert!(!entry.has_modrm);
}
#[test]
fn test_opcode_table_mov() {
let entry = OpcodeEncodingEntry::lookup("mov");
assert!(entry.is_some());
assert!(entry.unwrap().has_modrm);
}
#[test]
fn test_opcode_table_comprehensive() {
for entry in OPCODE_ENCODING_TABLE.iter() {
assert!(!entry.mnemonic.is_empty());
assert!(!entry.opcode_bytes.is_empty());
assert!(entry.operand_count <= 3);
}
}
#[test]
fn test_opcode_table_all_cond_jumps() {
let jcc = [
"jo", "jno", "jb", "jae", "je", "jne", "jbe", "ja", "js", "jns", "jp", "jnp", "jl",
"jge", "jle", "jg",
];
for j in jcc.iter() {
let entry = OpcodeEncodingEntry::lookup(j);
assert!(entry.is_some(), "Missing jcc: {}", j);
assert!(entry.unwrap().is_branch);
}
}
#[test]
fn test_opcode_table_all_setcc() {
let setcc = [
"seto", "setno", "setb", "setae", "sete", "setne", "setbe", "seta", "sets", "setns",
"setp", "setnp", "setl", "setge", "setle", "setg",
];
for s in setcc.iter() {
let entry = OpcodeEncodingEntry::lookup(s);
assert!(entry.is_some(), "Missing setcc: {}", s);
}
}
#[test]
fn test_opcode_table_all_cmovcc() {
let cmov = [
"cmovo", "cmovno", "cmovb", "cmovae", "cmove", "cmovne", "cmovbe", "cmova", "cmovs",
"cmovns", "cmovp", "cmovnp", "cmovl", "cmovge", "cmovle", "cmovg",
];
for c in cmov.iter() {
let entry = OpcodeEncodingEntry::lookup(c);
assert!(entry.is_some(), "Missing cmov: {}", c);
}
}
#[test]
fn test_opcode_table_lookup_all() {
let entries = OpcodeEncodingEntry::lookup_all("call");
assert!(entries.len() >= 2); }
#[test]
fn test_relaxation_needed() {
assert!(X86RelaxationEngine::needs_relaxation(-129));
assert!(X86RelaxationEngine::needs_relaxation(128));
assert!(!X86RelaxationEngine::needs_relaxation(-128));
assert!(!X86RelaxationEngine::needs_relaxation(127));
assert!(!X86RelaxationEngine::needs_relaxation(0));
}
#[test]
fn test_relaxation_overhead() {
assert_eq!(X86RelaxationEngine::relaxation_overhead(), 4);
}
#[test]
fn test_relax_short_jcc() {
let relaxed = X86RelaxationEngine::relax_short_jcc(0x74); assert_eq!(relaxed[0], 0x0F);
assert_eq!(relaxed[1], 0x84);
assert_eq!(relaxed.len(), 6);
}
#[test]
fn test_relax_all_jcc_short() {
for cond in 0..16u8 {
let opcode = 0x70 + cond;
let relaxed = X86RelaxationEngine::relax_short_jcc(opcode);
assert_eq!(relaxed[0], 0x0F);
assert_eq!(relaxed[1], 0x80 + cond);
}
}
#[test]
fn test_relax_short_jmp() {
let relaxed = X86RelaxationEngine::relax_short_jmp();
assert_eq!(relaxed[0], 0xE9);
assert_eq!(relaxed.len(), 5);
}
#[test]
fn test_relax_loop_instructions() {
let relaxed = X86RelaxationEngine::relax_loop(0xE2); assert!(relaxed.len() >= 6);
let relaxed = X86RelaxationEngine::relax_loop(0xE1); assert!(relaxed.len() >= 6);
let relaxed = X86RelaxationEngine::relax_loop(0xE0); assert!(relaxed.len() >= 6);
}
#[test]
fn test_apply_displacement() {
let mut data = vec![0x0F, 0x84, 0x00, 0x00, 0x00, 0x00];
X86RelaxationEngine::apply_displacement(&mut data, 2, 0x12345678);
assert_eq!(data[2..6], [0x78, 0x56, 0x34, 0x12]);
}
#[test]
fn test_apply_short_displacement() {
let mut data = vec![0x74, 0x00];
X86RelaxationEngine::apply_short_displacement(&mut data, 1, 42);
assert_eq!(data[1], 42);
X86RelaxationEngine::apply_short_displacement(&mut data, 1, -1);
assert_eq!(data[1], 0xFF);
}
#[test]
fn test_streamer_basic() {
let config = AssemblerConfig::default();
let mut streamer = X86AsmStreamer::new(config);
streamer.emit_label("start").unwrap();
streamer.emit_instruction("nop", &[]).unwrap();
streamer.emit_instruction("ret", &[]).unwrap();
let result = streamer.finalize().unwrap();
assert_eq!(result.instruction_count, 2);
}
#[test]
fn test_streamer_directives() {
let config = AssemblerConfig::default();
let mut streamer = X86AsmStreamer::new(config);
streamer.emit_directive(".text", "").unwrap();
streamer.emit_directive(".byte", "0x90, 0xCC").unwrap();
let result = streamer.finalize().unwrap();
assert!(result.data.len() >= 2);
}
#[test]
fn test_streamer_new_label() {
let config = AssemblerConfig::default();
let mut streamer = X86AsmStreamer::new(config);
let label1 = streamer.new_label();
let label2 = streamer.new_label();
assert!(label1 != label2);
assert!(label1.contains("Ltmp"));
assert!(label2.contains("Ltmp"));
}
#[test]
fn test_verify_all_nop_variants() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("nop").unwrap();
assert_eq!(result.data, vec![0x90]);
}
#[test]
fn test_verify_push_all_gprs() {
let mut asm = X86MCFullAssembler::default_att();
let regs = [
"%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
];
for reg in regs.iter() {
let source = format!(
"pushq {}
",
reg
);
let result = asm.assemble(&source).unwrap();
assert_eq!(result.instruction_count, 1);
assert!(!result.data.is_empty());
}
}
#[test]
fn test_verify_pop_all_gprs() {
let mut asm = X86MCFullAssembler::default_att();
let regs = [
"%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
];
for reg in regs.iter() {
let source = format!(
"popq {}
",
reg
);
let result = asm.assemble(&source).unwrap();
assert_eq!(result.instruction_count, 1);
}
}
#[test]
fn test_verify_xor_self_zeroing() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("xorl %eax, %eax").unwrap();
assert_eq!(result.data.len(), 2); assert_eq!(result.data, vec![0x31, 0xC0]);
}
#[test]
fn test_verify_mov_imm32_to_reg() {
let mut asm = X86MCFullAssembler::default_att();
let result = asm.assemble("movl 2, %eax").unwrap();
assert_eq!(result.data[0], 0xB8);
assert_eq!(result.data[1..5], [42, 0, 0, 0]);
}
#[test]
fn test_mnemonic_completeness_check() {
let parser = X86MnemonicParser::new(AsmSyntaxMode::ATT);
let critical_ops = [
"mov", "add", "sub", "mul", "div", "and", "or", "xor", "shl", "shr", "sar", "rol",
"ror", "jmp", "call", "ret", "push", "pop", "cmp", "test", "lea", "xchg", "nop", "inc",
"dec", "not", "neg", "movsx", "movzx", "movsxd", "imul", "idiv", "adc", "sbb", "bt",
"bts", "btr", "btc", "bsf", "bsr", "cmpxchg", "xadd", "cbw", "cwde", "cdqe", "cwd",
"cdq", "cqo", "syscall", "sysret", "cpuid", "lfence", "mfence", "sfence", "rdtsc",
"rdtscp", "enter", "leave", "lahf", "sahf",
];
for op in critical_ops.iter() {
assert!(
parser.parse_att(op).is_some(),
"Critical opcode not recognized: {}",
op
);
}
}
#[test]
fn test_verify_jcc_all_16_conditions() {
let mut asm = X86MCFullAssembler::default_att();
let jcc_names = [
"jo", "jno", "jb", "jae", "je", "jne", "jbe", "ja", "js", "jns", "jp", "jnp", "jl",
"jge", "jle", "jg",
];
for jcc in jcc_names.iter() {
let source = format!(
"{} target
target:
nop
",
jcc
);
let result = asm.assemble(&source).unwrap();
assert!(result.instruction_count >= 2, "Failed for {}", jcc);
}
}
#[test]
fn test_verify_imul_three_forms() {
let mut asm = X86MCFullAssembler::default_att();
let r = asm.assemble("imulq %rbx").unwrap();
assert_eq!(r.instruction_count, 1);
let r = asm.assemble("imulq %rbx, %rax").unwrap();
assert_eq!(r.instruction_count, 1);
let r = asm.assemble("imulq 2, %rbx, %rax").unwrap();
assert_eq!(r.instruction_count, 1);
}
#[test]
fn test_assemble_500_nops() {
let mut asm = X86MCFullAssembler::default_att();
let mut source = String::new();
for _ in 0..500 {
source.push_str(
"nop
",
);
}
let result = asm.assemble(&source).unwrap();
assert_eq!(result.instruction_count, 500);
assert_eq!(result.data.len(), 500);
assert!(result.data.iter().all(|&b| b == 0x90));
}
#[test]
fn test_assemble_complex_control_flow() {
let mut asm = X86MCFullAssembler::default_att();
let source = r#"
.text
.globl fibonacci
fibonacci:
cmpl , %edi
jle .Lbase
pushq %rbx
movl %edi, %ebx
subl , %edi
call fibonacci
movl %eax, %r8d
leal -2(%rbx), %edi
call fibonacci
addl %r8d, %eax
popq %rbx
ret
.Lbase:
movl , %eax
ret
"#;
let result = asm.assemble(source).unwrap();
assert!(result.instruction_count >= 10);
}
#[test]
fn test_directive_ascii_roundtrip() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
let bytes = proc
.process(&AsmDirective::Ascii("test".to_string()))
.unwrap();
assert_eq!(bytes, b"test");
let bytes = proc
.process(&AsmDirective::Asciz("test".to_string()))
.unwrap();
assert_eq!(bytes, b"test\0");
}
#[test]
fn test_directive_string_roundtrip() {
let mut proc = X86DirectiveProcessor::new(AsmSyntaxMode::ATT);
let bytes = proc
.process(&AsmDirective::String("hello".to_string()))
.unwrap();
assert_eq!(bytes, b"hello\0");
}
}