#![allow(non_upper_case_globals, dead_code)]
use std::collections::HashMap;
use std::fmt;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Arch {
I386,
I486,
I586,
I686,
Pentium,
PentiumMMX,
PentiumPro,
Pentium2,
Pentium3,
Pentium4,
X86_64,
X86_64h,
X86_64v2,
X86_64v3,
X86_64v4,
Unknown,
}
impl X86Arch {
pub fn from_str(s: &str) -> Self {
match s.to_lowercase().as_str() {
"i386" | "i486" | "i586" | "i686" | "x86" => Self::I686,
"pentium" => Self::Pentium,
"pentium-mmx" | "pentium_mmx" => Self::PentiumMMX,
"pentiumpro" => Self::PentiumPro,
"pentium2" => Self::Pentium2,
"pentium3" => Self::Pentium3,
"pentium4" => Self::Pentium4,
"x86_64" | "amd64" | "x86-64" => Self::X86_64,
"x86_64h" | "x86-64h" => Self::X86_64h,
"x86_64-v2" | "x86_64v2" => Self::X86_64v2,
"x86_64-v3" | "x86_64v3" => Self::X86_64v3,
"x86_64-v4" | "x86_64v4" => Self::X86_64v4,
_ => Self::Unknown,
}
}
pub fn to_str(&self) -> &'static str {
match self {
Self::I386 => "i386",
Self::I486 => "i486",
Self::I586 => "i586",
Self::I686 => "i686",
Self::Pentium => "pentium",
Self::PentiumMMX => "pentium-mmx",
Self::PentiumPro => "pentiumpro",
Self::Pentium2 => "pentium2",
Self::Pentium3 => "pentium3",
Self::Pentium4 => "pentium4",
Self::X86_64 => "x86_64",
Self::X86_64h => "x86_64h",
Self::X86_64v2 => "x86_64v2",
Self::X86_64v3 => "x86_64v3",
Self::X86_64v4 => "x86_64v4",
Self::Unknown => "unknown",
}
}
pub fn is_64bit(&self) -> bool {
matches!(
self,
Self::X86_64 | Self::X86_64h | Self::X86_64v2 | Self::X86_64v3 | Self::X86_64v4
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86Vendor {
Intel,
AMD,
Centaur,
Cyrix,
Transmeta,
Zhaoxin,
Hygon,
Unknown,
}
impl X86Vendor {
pub fn from_str(s: &str) -> Self {
match s.to_lowercase().as_str() {
"intel" => Self::Intel,
"amd" => Self::AMD,
"centaur" | "via" => Self::Centaur,
"cyrix" => Self::Cyrix,
"transmeta" => Self::Transmeta,
"zhaoxin" => Self::Zhaoxin,
"hygon" => Self::Hygon,
_ => Self::Unknown,
}
}
pub fn to_str(&self) -> &'static str {
match self {
Self::Intel => "intel",
Self::AMD => "amd",
Self::Centaur => "centaur",
Self::Cyrix => "cyrix",
Self::Transmeta => "transmeta",
Self::Zhaoxin => "zhaoxin",
Self::Hygon => "hygon",
Self::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86OS {
Linux,
Windows,
Darwin,
FreeBSD,
NetBSD,
OpenBSD,
DragonFly,
Solaris,
Haiku,
Fuchsia,
None,
Unknown,
}
impl X86OS {
pub fn from_str(s: &str) -> Self {
match s.to_lowercase().as_str() {
"linux" => Self::Linux,
"win32" | "windows" | "mingw32" | "cygwin" => Self::Windows,
"darwin" | "macosx" | "macos" => Self::Darwin,
"freebsd" => Self::FreeBSD,
"netbsd" => Self::NetBSD,
"openbsd" => Self::OpenBSD,
"dragonfly" => Self::DragonFly,
"solaris" | "sunos" => Self::Solaris,
"haiku" => Self::Haiku,
"fuchsia" => Self::Fuchsia,
"none" | "unknown" => Self::None,
_ => Self::Unknown,
}
}
pub fn to_str(&self) -> &'static str {
match self {
Self::Linux => "linux",
Self::Windows => "windows",
Self::Darwin => "darwin",
Self::FreeBSD => "freebsd",
Self::NetBSD => "netbsd",
Self::OpenBSD => "openbsd",
Self::DragonFly => "dragonfly",
Self::Solaris => "solaris",
Self::Haiku => "haiku",
Self::Fuchsia => "fuchsia",
Self::None => "none",
Self::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86Environment {
GNU,
GNUAbi64,
Android,
AndroidAbi64,
Musl,
MuslAbi64,
MSVC,
Itanium,
Cygnus,
CoreCLR,
Simulator,
MacABI,
Unknown,
}
impl X86Environment {
pub fn from_str(s: &str) -> Self {
match s.to_lowercase().as_str() {
"gnu" => Self::GNU,
"gnuabi64" => Self::GNUAbi64,
"android" | "androideabi" => Self::Android,
"androidabi64" => Self::AndroidAbi64,
"musl" => Self::Musl,
"muslabi64" => Self::MuslAbi64,
"msvc" => Self::MSVC,
"itanium" => Self::Itanium,
"cygnus" => Self::Cygnus,
"coreclr" => Self::CoreCLR,
"simulator" => Self::Simulator,
"macabi" => Self::MacABI,
_ => Self::Unknown,
}
}
pub fn to_str(&self) -> &'static str {
match self {
Self::GNU => "gnu",
Self::GNUAbi64 => "gnuabi64",
Self::Android => "android",
Self::AndroidAbi64 => "androidabi64",
Self::Musl => "musl",
Self::MuslAbi64 => "muslabi64",
Self::MSVC => "msvc",
Self::Itanium => "itanium",
Self::Cygnus => "cygnus",
Self::CoreCLR => "coreclr",
Self::Simulator => "simulator",
Self::MacABI => "macabi",
Self::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ObjectFormat {
ELF,
COFF,
MachO,
Wasm,
XCOFF,
GOFF,
Unknown,
}
impl X86ObjectFormat {
pub fn to_str(&self) -> &'static str {
match self {
Self::ELF => "elf",
Self::COFF => "coff",
Self::MachO => "macho",
Self::Wasm => "wasm",
Self::XCOFF => "xcoff",
Self::GOFF => "goff",
Self::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone)]
pub struct X86TargetTriple {
pub arch: X86Arch,
pub vendor: X86Vendor,
pub os: X86OS,
pub environment: X86Environment,
pub object_format: X86ObjectFormat,
pub original: String,
}
impl X86TargetTriple {
pub fn parse(triple: &str) -> Self {
let original = triple.to_string();
let parts: Vec<&str> = triple.split('-').collect();
let arch = if !parts.is_empty() {
X86Arch::from_str(parts[0])
} else {
X86Arch::Unknown
};
let vendor = if parts.len() > 1 {
X86Vendor::from_str(parts[1])
} else {
X86Vendor::Unknown
};
let os = if parts.len() > 2 {
X86OS::from_str(parts[2])
} else {
X86OS::Unknown
};
let environment = if parts.len() > 3 {
X86Environment::from_str(parts[3])
} else {
X86Environment::Unknown
};
let object_format = Self::infer_object_format(os);
Self {
arch,
vendor,
os,
environment,
object_format,
original,
}
}
fn infer_object_format(os: X86OS) -> X86ObjectFormat {
match os {
X86OS::Linux
| X86OS::FreeBSD
| X86OS::NetBSD
| X86OS::OpenBSD
| X86OS::DragonFly
| X86OS::Solaris
| X86OS::Haiku
| X86OS::Fuchsia
| X86OS::None => X86ObjectFormat::ELF,
X86OS::Windows => X86ObjectFormat::COFF,
X86OS::Darwin => X86ObjectFormat::MachO,
_ => X86ObjectFormat::ELF,
}
}
pub fn normalize(&self) -> String {
format!(
"{}-{}-{}-{}",
self.arch.to_str(),
self.vendor.to_str(),
self.os.to_str(),
self.environment.to_str(),
)
}
pub fn host() -> Self {
let arch = if cfg!(target_arch = "x86_64") {
X86Arch::X86_64
} else if cfg!(target_arch = "x86") {
X86Arch::I686
} else {
X86Arch::Unknown
};
let os = if cfg!(target_os = "linux") {
X86OS::Linux
} else if cfg!(target_os = "windows") {
X86OS::Windows
} else if cfg!(target_os = "macos") {
X86OS::Darwin
} else {
X86OS::Unknown
};
let vendor = X86Vendor::Unknown;
let env = if cfg!(target_env = "gnu") {
X86Environment::GNU
} else if cfg!(target_env = "msvc") {
X86Environment::MSVC
} else if cfg!(target_env = "musl") {
X86Environment::Musl
} else {
X86Environment::Unknown
};
let object_format = Self::infer_object_format(os);
Self {
arch,
vendor,
os,
environment: env,
object_format,
original: format!("{}-{}-{}", arch.to_str(), vendor.to_str(), os.to_str()),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct X86CpuidResult {
pub eax: u32,
pub ebx: u32,
pub ecx: u32,
pub edx: u32,
}
#[derive(Debug)]
pub struct X86Cpuid;
impl X86Cpuid {
pub fn cpuid(leaf: u32, subleaf: u32) -> X86CpuidResult {
let _ = (leaf, subleaf);
X86CpuidResult {
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
}
}
pub fn vendor_string() -> String {
let leaf0 = Self::cpuid(0, 0);
let mut vendor = [0u8; 12];
vendor[0..4].copy_from_slice(&leaf0.ebx.to_le_bytes());
vendor[4..8].copy_from_slice(&leaf0.edx.to_le_bytes());
vendor[8..12].copy_from_slice(&leaf0.ecx.to_le_bytes());
String::from_utf8_lossy(&vendor).to_string()
}
pub fn brand_string() -> String {
let mut brand = [0u8; 48];
let leaf1 = Self::cpuid(0x80000002, 0);
let leaf2 = Self::cpuid(0x80000003, 0);
let leaf3 = Self::cpuid(0x80000004, 0);
brand[0..4].copy_from_slice(&leaf1.eax.to_le_bytes());
brand[4..8].copy_from_slice(&leaf1.ebx.to_le_bytes());
brand[8..12].copy_from_slice(&leaf1.ecx.to_le_bytes());
brand[12..16].copy_from_slice(&leaf1.edx.to_le_bytes());
brand[16..20].copy_from_slice(&leaf2.eax.to_le_bytes());
brand[20..24].copy_from_slice(&leaf2.ebx.to_le_bytes());
brand[24..28].copy_from_slice(&leaf2.ecx.to_le_bytes());
brand[28..32].copy_from_slice(&leaf2.edx.to_le_bytes());
brand[32..36].copy_from_slice(&leaf3.eax.to_le_bytes());
brand[36..40].copy_from_slice(&leaf3.ebx.to_le_bytes());
brand[40..44].copy_from_slice(&leaf3.ecx.to_le_bytes());
brand[44..48].copy_from_slice(&leaf3.edx.to_le_bytes());
String::from_utf8_lossy(&brand).trim().to_string()
}
pub fn has_feature(leaf: u32, subleaf: u32, reg: u8, bit: u8) -> bool {
let result = Self::cpuid(leaf, subleaf);
match reg {
0 => result.eax & (1 << bit) != 0,
1 => result.ebx & (1 << bit) != 0,
2 => result.ecx & (1 << bit) != 0,
3 => result.edx & (1 << bit) != 0,
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86HostCpu {
pub vendor: X86Vendor,
pub brand: String,
pub family: u32,
pub model: u32,
pub stepping: u32,
pub cores: u32,
pub threads: u32,
pub features: X86CpuFeatures,
pub cache_info: X86CacheInfo,
pub max_cpuid_leaf: u32,
pub max_extended_leaf: u32,
}
#[derive(Debug, Clone, Default)]
pub struct X86CpuFeatures {
pub mmx: bool,
pub sse: bool,
pub sse2: bool,
pub sse3: bool,
pub ssse3: bool,
pub sse41: bool,
pub sse42: bool,
pub avx: bool,
pub avx2: bool,
pub avx512f: bool,
pub avx512cd: bool,
pub avx512er: bool,
pub avx512pf: bool,
pub avx512bw: bool,
pub avx512dq: bool,
pub avx512vl: bool,
pub avx512_vp2intersect: bool,
pub avx512_fp16: bool,
pub avx_vnni: bool,
pub avx512_vnni: bool,
pub avx512_bf16: bool,
pub avx10_1: bool,
pub amx_tile: bool,
pub amx_int8: bool,
pub amx_bf16: bool,
pub amx_fp16: bool,
pub amx_fp8: bool,
pub fma: bool,
pub f16c: bool,
pub bmi: bool,
pub bmi2: bool,
pub lzcnt: bool,
pub popcnt: bool,
pub aes: bool,
pub pclmul: bool,
pub rdrand: bool,
pub rdseed: bool,
pub sha: bool,
pub sgx: bool,
pub cet_ibt: bool,
pub cet_ss: bool,
pub movbe: bool,
pub rtm: bool,
pub hle: bool,
pub xsave: bool,
pub xsaveopt: bool,
pub xsavec: bool,
pub xsaves: bool,
pub clflushopt: bool,
pub clwb: bool,
pub pku: bool,
pub waitpkg: bool,
pub serialize: bool,
pub uintr: bool,
}
#[derive(Debug, Clone, Default)]
pub struct X86CacheInfo {
pub l1d_size: u32,
pub l1d_line_size: u32,
pub l1i_size: u32,
pub l1i_line_size: u32,
pub l2_size: u32,
pub l2_line_size: u32,
pub l3_size: u32,
pub l3_line_size: u32,
pub l4_size: u32,
}
impl X86HostCpu {
pub fn detect() -> Self {
Self {
vendor: X86Vendor::Unknown,
brand: X86Cpuid::brand_string(),
family: 6,
model: 0,
stepping: 0,
cores: num_cpus::get() as u32,
threads: num_cpus::get_physical() as u32,
features: X86CpuFeatures::default(),
cache_info: X86CacheInfo::default(),
max_cpuid_leaf: 0,
max_extended_leaf: 0,
}
}
pub fn supports(&self, name: &str) -> bool {
let name = name.to_lowercase();
macro_rules! check {
($field:ident) => {
if name == stringify!($field).to_lowercase().replace('_', "") {
return self.features.$field;
}
};
}
check!(sse);
check!(sse2);
check!(sse3);
check!(ssse3);
check!(sse41);
check!(sse42);
check!(avx);
check!(avx2);
check!(avx512f);
check!(fma);
check!(bmi);
check!(bmi2);
check!(aes);
check!(rdrand);
check!(sha);
check!(sgx);
false
}
pub fn microarch_level(&self) -> u32 {
if self.features.avx512f
&& self.features.avx512bw
&& self.features.avx512dq
&& self.features.avx512vl
{
4
} else if self.features.avx2
&& self.features.bmi
&& self.features.bmi2
&& self.features.f16c
&& self.features.fma
&& self.features.lzcnt
&& self.features.movbe
{
3
} else if self.features.sse42
&& self.features.sse3
&& self.features.ssse3
&& self.features.popcnt
{
2
} else {
1
}
}
}
#[derive(Debug)]
pub struct X86MemoryManager;
impl X86MemoryManager {
pub fn aligned_alloc(size: usize, alignment: usize) -> *mut u8 {
let layout =
std::alloc::Layout::from_size_align(size, alignment).expect("aligned alloc layout");
unsafe { std::alloc::alloc(layout) }
}
pub fn aligned_free(ptr: *mut u8, size: usize, alignment: usize) {
if ptr.is_null() {
return;
}
let layout =
std::alloc::Layout::from_size_align(size, alignment).expect("aligned free layout");
unsafe {
std::alloc::dealloc(ptr, layout);
}
}
pub fn huge_page_alloc(size: usize) -> *mut u8 {
Self::aligned_alloc(size, 2 * 1024 * 1024)
}
pub fn huge_page_free(ptr: *mut u8, size: usize) {
Self::aligned_free(ptr, size, 2 * 1024 * 1024);
}
pub fn page_size() -> usize {
4096 }
pub fn large_page_size() -> usize {
2 * 1024 * 1024
}
pub fn huge_page_size() -> usize {
1024 * 1024 * 1024
}
pub fn round_up_to_page(size: usize) -> usize {
let ps = Self::page_size();
(size + ps - 1) & !(ps - 1)
}
pub fn round_down_to_page(addr: usize) -> usize {
addr & !(Self::page_size() - 1)
}
pub fn is_page_aligned(addr: usize) -> bool {
addr & (Self::page_size() - 1) == 0
}
pub fn align_up(addr: usize, alignment: usize) -> usize {
(addr + alignment - 1) & !(alignment - 1)
}
pub fn align_down(addr: usize, alignment: usize) -> usize {
addr & !(alignment - 1)
}
}
#[derive(Debug)]
pub struct X86Timer {
start: Instant,
start_tsc: u64,
}
impl X86Timer {
pub fn new() -> Self {
Self {
start: Instant::now(),
start_tsc: Self::rdtsc(),
}
}
pub fn rdtsc() -> u64 {
Instant::now().elapsed().as_nanos() as u64
}
pub fn rdtscp() -> (u64, u32) {
(Self::rdtsc(), 0)
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn elapsed_tsc(&self) -> u64 {
Self::rdtsc().wrapping_sub(self.start_tsc)
}
pub fn elapsed_nanos(&self) -> u64 {
self.elapsed().as_nanos() as u64
}
pub fn elapsed_micros(&self) -> u64 {
self.elapsed().as_micros() as u64
}
pub fn elapsed_millis(&self) -> u64 {
self.elapsed().as_millis() as u64
}
}
impl Default for X86Timer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct X86SystemTime;
impl X86SystemTime {
pub fn now_nanos() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64
}
pub fn now_micros() -> u64 {
Self::now_nanos() / 1000
}
pub fn now_millis() -> u64 {
Self::now_nanos() / 1_000_000
}
pub fn now_secs() -> u64 {
Self::now_nanos() / 1_000_000_000
}
pub fn query_performance_counter() -> u64 {
X86Timer::rdtsc()
}
pub fn query_performance_frequency() -> u64 {
1_000_000_000
}
}
#[derive(Debug)]
pub struct X86Endian;
impl X86Endian {
pub const IS_LITTLE_ENDIAN: bool = true;
pub fn read_u16_le(buf: &[u8], offset: usize) -> u16 {
u16::from_le_bytes([buf[offset], buf[offset + 1]])
}
pub fn read_u32_le(buf: &[u8], offset: usize) -> u32 {
u32::from_le_bytes([
buf[offset],
buf[offset + 1],
buf[offset + 2],
buf[offset + 3],
])
}
pub fn read_u64_le(buf: &[u8], offset: usize) -> u64 {
u64::from_le_bytes([
buf[offset],
buf[offset + 1],
buf[offset + 2],
buf[offset + 3],
buf[offset + 4],
buf[offset + 5],
buf[offset + 6],
buf[offset + 7],
])
}
pub fn write_u16_le(buf: &mut [u8], offset: usize, val: u16) {
buf[offset..offset + 2].copy_from_slice(&val.to_le_bytes());
}
pub fn write_u32_le(buf: &mut [u8], offset: usize, val: u32) {
buf[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
}
pub fn write_u64_le(buf: &mut [u8], offset: usize, val: u64) {
buf[offset..offset + 8].copy_from_slice(&val.to_le_bytes());
}
pub fn bswap16(val: u16) -> u16 {
val.swap_bytes()
}
pub fn bswap32(val: u32) -> u32 {
val.swap_bytes()
}
pub fn bswap64(val: u64) -> u64 {
val.swap_bytes()
}
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct X86Elf64Header {
pub e_ident: [u8; 16],
pub e_type: u16,
pub e_machine: u16,
pub e_version: u32,
pub e_entry: u64,
pub e_phoff: u64,
pub e_shoff: u64,
pub e_flags: u32,
pub e_ehsize: u16,
pub e_phentsize: u16,
pub e_phnum: u16,
pub e_shentsize: u16,
pub e_shnum: u16,
pub e_shstrndx: u16,
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct X86Elf64SectionHeader {
pub sh_name: u32,
pub sh_type: u32,
pub sh_flags: u64,
pub sh_addr: u64,
pub sh_offset: u64,
pub sh_size: u64,
pub sh_link: u32,
pub sh_info: u32,
pub sh_addralign: u64,
pub sh_entsize: u64,
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct X86Elf64Symbol {
pub st_name: u32,
pub st_info: u8,
pub st_other: u8,
pub st_shndx: u16,
pub st_value: u64,
pub st_size: u64,
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct X86Elf64Rela {
pub r_offset: u64,
pub r_info: u64,
pub r_addend: i64,
}
#[derive(Debug)]
pub struct X86ElfUtils;
impl X86ElfUtils {
pub const ELF_MAGIC: [u8; 4] = [0x7f, b'E', b'L', b'F'];
pub const ELF_CLASS_64: u8 = 2;
pub const ELF_DATA_LSB: u8 = 1;
pub const EM_X86_64: u16 = 62;
pub const EM_386: u16 = 3;
pub fn is_elf(data: &[u8]) -> bool {
data.len() >= 4 && data[0..4] == Self::ELF_MAGIC
}
pub fn parse_header(data: &[u8]) -> Option<X86Elf64Header> {
if data.len() < 64 || !Self::is_elf(data) {
return None;
}
Some(X86Elf64Header {
e_ident: {
let mut ident = [0u8; 16];
ident.copy_from_slice(&data[0..16]);
ident
},
e_type: X86Endian::read_u16_le(data, 16),
e_machine: X86Endian::read_u16_le(data, 18),
e_version: X86Endian::read_u32_le(data, 20),
e_entry: X86Endian::read_u64_le(data, 24),
e_phoff: X86Endian::read_u64_le(data, 32),
e_shoff: X86Endian::read_u64_le(data, 40),
e_flags: X86Endian::read_u32_le(data, 48),
e_ehsize: X86Endian::read_u16_le(data, 52),
e_phentsize: X86Endian::read_u16_le(data, 54),
e_phnum: X86Endian::read_u16_le(data, 56),
e_shentsize: X86Endian::read_u16_le(data, 58),
e_shnum: X86Endian::read_u16_le(data, 60),
e_shstrndx: X86Endian::read_u16_le(data, 62),
})
}
}
#[derive(Debug, Clone)]
pub struct X86CoffHeader {
pub machine: u16,
pub number_of_sections: u16,
pub time_date_stamp: u32,
pub pointer_to_symbol_table: u32,
pub number_of_symbols: u32,
pub size_of_optional_header: u16,
pub characteristics: u16,
}
#[derive(Debug, Clone)]
pub struct X86CoffSectionHeader {
pub name: [u8; 8],
pub virtual_size: u32,
pub virtual_address: u32,
pub size_of_raw_data: u32,
pub pointer_to_raw_data: u32,
pub pointer_to_relocations: u32,
pub pointer_to_line_numbers: u32,
pub number_of_relocations: u16,
pub number_of_line_numbers: u16,
pub characteristics: u32,
}
#[derive(Debug)]
pub struct X86CoffUtils;
impl X86CoffUtils {
pub const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664;
pub const IMAGE_FILE_MACHINE_I386: u16 = 0x014c;
pub fn is_coff(data: &[u8]) -> bool {
if data.len() < 2 {
return false;
}
let machine = X86Endian::read_u16_le(data, 0);
machine == Self::IMAGE_FILE_MACHINE_AMD64 || machine == Self::IMAGE_FILE_MACHINE_I386
}
pub fn parse_header(data: &[u8]) -> Option<X86CoffHeader> {
if data.len() < 20 || !Self::is_coff(data) {
return None;
}
Some(X86CoffHeader {
machine: X86Endian::read_u16_le(data, 0),
number_of_sections: X86Endian::read_u16_le(data, 2),
time_date_stamp: X86Endian::read_u32_le(data, 4),
pointer_to_symbol_table: X86Endian::read_u32_le(data, 8),
number_of_symbols: X86Endian::read_u32_le(data, 12),
size_of_optional_header: X86Endian::read_u16_le(data, 16),
characteristics: X86Endian::read_u16_le(data, 18),
})
}
}
#[derive(Debug, Clone)]
pub struct X86MachOHeader {
pub magic: u32,
pub cputype: u32,
pub cpusubtype: u32,
pub filetype: u32,
pub ncmds: u32,
pub sizeofcmds: u32,
pub flags: u32,
}
#[derive(Debug)]
pub struct X86MachOUtils;
impl X86MachOUtils {
pub const MH_MAGIC_64: u32 = 0xfeedfacf;
pub const CPU_TYPE_X86_64: u32 = 0x01000007;
pub const CPU_TYPE_I386: u32 = 0x00000007;
pub fn is_macho(data: &[u8]) -> bool {
if data.len() < 4 {
return false;
}
let magic = X86Endian::read_u32_le(data, 0);
magic == Self::MH_MAGIC_64
}
pub fn parse_header(data: &[u8]) -> Option<X86MachOHeader> {
if data.len() < 28 || !Self::is_macho(data) {
return None;
}
Some(X86MachOHeader {
magic: X86Endian::read_u32_le(data, 0),
cputype: X86Endian::read_u32_le(data, 4),
cpusubtype: X86Endian::read_u32_le(data, 8),
filetype: X86Endian::read_u32_le(data, 12),
ncmds: X86Endian::read_u32_le(data, 16),
sizeofcmds: X86Endian::read_u32_le(data, 20),
flags: X86Endian::read_u32_le(data, 24),
})
}
}
#[derive(Debug)]
pub struct X86ProcessUtils;
impl X86ProcessUtils {
pub fn pid() -> u32 {
std::process::id()
}
pub fn num_cpus() -> u32 {
num_cpus::get() as u32
}
pub fn num_physical_cpus() -> u32 {
num_cpus::get_physical() as u32
}
pub fn tid() -> u64 {
0
}
pub fn exec(cmd: &str, args: &[&str]) -> Result<String, String> {
std::process::Command::new(cmd)
.args(args)
.output()
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
.map_err(|e| format!("exec failed: {}", e))
}
pub fn get_env(key: &str) -> Option<String> {
std::env::var(key).ok()
}
pub fn set_env(key: &str, val: &str) {
unsafe { std::env::set_var(key, val) };
}
pub fn exe_path() -> Option<String> {
std::env::current_exe()
.ok()
.and_then(|p| p.to_str().map(String::from))
}
pub fn cwd() -> Option<String> {
std::env::current_dir()
.ok()
.and_then(|p| p.to_str().map(String::from))
}
pub fn exit(code: i32) -> ! {
std::process::exit(code)
}
}
#[derive(Debug)]
pub struct X86FSUtils;
impl X86FSUtils {
pub fn file_exists(path: &str) -> bool {
std::path::Path::new(path).exists()
}
pub fn is_directory(path: &str) -> bool {
std::path::Path::new(path).is_dir()
}
pub fn read_file(path: &str) -> Result<Vec<u8>, String> {
std::fs::read(path).map_err(|e| format!("read {}: {}", path, e))
}
pub fn write_file(path: &str, data: &[u8]) -> Result<(), String> {
std::fs::write(path, data).map_err(|e| format!("write {}: {}", path, e))
}
pub fn read_file_to_string(path: &str) -> Result<String, String> {
std::fs::read_to_string(path).map_err(|e| format!("read {}: {}", path, e))
}
pub fn create_dir_all(path: &str) -> Result<(), String> {
std::fs::create_dir_all(path).map_err(|e| format!("mkdir {}: {}", path, e))
}
pub fn remove_file(path: &str) -> Result<(), String> {
std::fs::remove_file(path).map_err(|e| format!("rm {}: {}", path, e))
}
pub fn remove_dir_all(path: &str) -> Result<(), String> {
std::fs::remove_dir_all(path).map_err(|e| format!("rmdir {}: {}", path, e))
}
pub fn list_dir(path: &str) -> Result<Vec<String>, String> {
let entries = std::fs::read_dir(path).map_err(|e| format!("readdir {}: {}", path, e))?;
let mut result = Vec::new();
for entry in entries {
if let Ok(entry) = entry {
if let Some(name) = entry.file_name().to_str() {
result.push(name.to_string());
}
}
}
Ok(result)
}
pub fn file_size(path: &str) -> Result<u64, String> {
std::fs::metadata(path)
.map(|m| m.len())
.map_err(|e| format!("stat {}: {}", path, e))
}
pub fn file_mtime(path: &str) -> Result<Duration, String> {
let meta = std::fs::metadata(path).map_err(|e| format!("mtime {}: {}", path, e))?;
let modified = meta
.modified()
.map_err(|e| format!("mtime {}: {}", path, e))?;
modified
.duration_since(UNIX_EPOCH)
.map_err(|e| format!("mtime {}: {}", path, e))
}
pub fn temp_file(prefix: &str, suffix: &str) -> Result<(String, std::fs::File), String> {
let dir = std::env::temp_dir();
let mut path = dir.join(format!(
"{}_{}{}",
prefix,
X86SystemTime::now_nanos(),
suffix
));
let file = std::fs::File::create(&path).map_err(|e| format!("temp file: {}", e))?;
Ok((path.to_str().unwrap_or("").to_string(), file))
}
pub fn canonicalize(path: &str) -> Result<String, String> {
std::fs::canonicalize(path)
.map_err(|e| format!("canonicalize {}: {}", path, e))
.and_then(|p| p.to_str().map(String::from).ok_or("invalid utf8".into()))
}
}
#[derive(Debug, Clone)]
pub struct X86CommandLineOption {
pub name: String,
pub short_name: Option<char>,
pub description: String,
pub value_type: X86OptionValueType,
pub default_value: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86OptionValueType {
Flag,
String,
Integer,
Float,
Enum,
}
#[derive(Debug)]
pub struct X86CommandLine {
pub options: Vec<X86CommandLineOption>,
pub values: HashMap<String, String>,
pub positional: Vec<String>,
}
impl X86CommandLine {
pub fn new() -> Self {
Self {
options: Vec::new(),
values: HashMap::new(),
positional: Vec::new(),
}
}
pub fn add_option(
&mut self,
name: &str,
short: Option<char>,
desc: &str,
value_type: X86OptionValueType,
default: Option<&str>,
) {
self.options.push(X86CommandLineOption {
name: name.to_string(),
short_name: short,
description: desc.to_string(),
value_type,
default_value: default.map(String::from),
});
if let Some(d) = default {
self.values.insert(name.to_string(), d.to_string());
}
}
pub fn parse(&mut self, args: &[String]) -> Result<(), String> {
let mut i = 1; while i < args.len() {
let arg = &args[i];
if arg.starts_with("--") {
let name = &arg[2..];
if let Some(eq) = name.find('=') {
let (key, val) = name.split_at(eq);
self.values.insert(key.to_string(), val[1..].to_string());
} else {
self.values.insert(name.to_string(), "true".to_string());
}
} else if arg.starts_with('-') && arg.len() == 2 {
let short = arg.chars().nth(1).unwrap();
if let Some(opt) = self.options.iter().find(|o| o.short_name == Some(short)) {
self.values.insert(opt.name.clone(), "true".to_string());
}
} else {
self.positional.push(arg.clone());
}
i += 1;
}
Ok(())
}
pub fn get_str(&self, name: &str) -> Option<&str> {
self.values.get(name).map(|s| s.as_str())
}
pub fn get_int(&self, name: &str) -> Option<i64> {
self.values.get(name).and_then(|s| s.parse().ok())
}
pub fn get_flag(&self, name: &str) -> bool {
self.values.get(name).map(|s| s == "true").unwrap_or(false)
}
pub fn print_help(&self, program_name: &str) {
println!("Usage: {} [options] [--] [positional args]", program_name);
println!();
println!("Options:");
for opt in &self.options {
let short = opt
.short_name
.map(|c| format!("-{}, ", c))
.unwrap_or_default();
let default = opt
.default_value
.as_ref()
.map(|d| format!(" [default: {}]", d))
.unwrap_or_default();
println!(" {}{} {}", short, opt.name, opt.description);
if !default.is_empty() {
println!(" {}", default);
}
}
}
}
impl Default for X86CommandLine {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86Error {
pub kind: X86ErrorKind,
pub message: String,
pub source_file: Option<String>,
pub source_line: Option<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ErrorKind {
Generic,
IOError,
ParseError,
UnsupportedFeature,
InvalidArgument,
OutOfMemory,
InternalError,
NotImplemented,
VerificationFailure,
}
impl X86Error {
pub fn new(kind: X86ErrorKind, msg: &str) -> Self {
Self {
kind,
message: msg.to_string(),
source_file: None,
source_line: None,
}
}
pub fn with_location(kind: X86ErrorKind, msg: &str, file: &str, line: u32) -> Self {
Self {
kind,
message: msg.to_string(),
source_file: Some(file.to_string()),
source_line: Some(line),
}
}
pub fn is_fatal(&self) -> bool {
matches!(
self.kind,
X86ErrorKind::OutOfMemory | X86ErrorKind::InternalError
)
}
}
impl fmt::Display for X86Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}: {}", self.kind, self.message)?;
if let (Some(file), Some(line)) = (&self.source_file, self.source_line) {
write!(f, " (at {}:{})", file, line)?;
}
Ok(())
}
}
impl std::error::Error for X86Error {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86LogLevel {
Debug = 0,
Info = 1,
Warning = 2,
Error = 3,
Fatal = 4,
}
#[derive(Debug)]
pub struct X86Logger {
pub level: X86LogLevel,
pub color: bool,
pub timestamp: bool,
pub log_file: Option<String>,
}
impl X86Logger {
pub fn new(level: X86LogLevel) -> Self {
Self {
level,
color: true,
timestamp: true,
log_file: None,
}
}
pub fn log(&self, level: X86LogLevel, msg: &str) {
if level as u32 >= self.level as u32 {
let prefix = match level {
X86LogLevel::Debug => "[DEBUG]",
X86LogLevel::Info => "[INFO]",
X86LogLevel::Warning => "[WARN]",
X86LogLevel::Error => "[ERROR]",
X86LogLevel::Fatal => "[FATAL]",
};
let ts = if self.timestamp {
format!("[{}] ", X86SystemTime::now_millis())
} else {
String::new()
};
eprintln!("{}{} {}", ts, prefix, msg);
}
}
pub fn debug(&self, msg: &str) {
self.log(X86LogLevel::Debug, msg);
}
pub fn info(&self, msg: &str) {
self.log(X86LogLevel::Info, msg);
}
pub fn warn(&self, msg: &str) {
self.log(X86LogLevel::Warning, msg);
}
pub fn error(&self, msg: &str) {
self.log(X86LogLevel::Error, msg);
}
pub fn fatal(&self, msg: &str) {
self.log(X86LogLevel::Fatal, msg);
}
}
impl Default for X86Logger {
fn default() -> Self {
Self::new(X86LogLevel::Info)
}
}
#[derive(Debug, Clone)]
pub struct X86Statistics {
pub counters: HashMap<String, u64>,
pub timers: HashMap<String, u64>,
#[allow(clippy::type_complexity)]
active_timers: HashMap<String, Instant>,
}
impl X86Statistics {
pub fn new() -> Self {
Self {
counters: HashMap::new(),
timers: HashMap::new(),
active_timers: HashMap::new(),
}
}
pub fn inc(&mut self, name: &str) {
*self.counters.entry(name.to_string()).or_insert(0) += 1;
}
pub fn add(&mut self, name: &str, value: u64) {
*self.counters.entry(name.to_string()).or_insert(0) += value;
}
pub fn time_start(&mut self, name: &str) {
self.active_timers.insert(name.to_string(), Instant::now());
}
pub fn time_stop(&mut self, name: &str) {
if let Some(start) = self.active_timers.remove(name) {
let elapsed = start.elapsed().as_nanos() as u64;
*self.timers.entry(name.to_string()).or_insert(0) += elapsed;
}
}
pub fn get_counter(&self, name: &str) -> u64 {
self.counters.get(name).copied().unwrap_or(0)
}
pub fn get_timer_ns(&self, name: &str) -> u64 {
self.timers.get(name).copied().unwrap_or(0)
}
pub fn print_summary(&self) {
println!("=== Statistics ===");
println!("Counters:");
let mut counter_keys: Vec<&String> = self.counters.keys().collect();
counter_keys.sort();
for key in counter_keys {
println!(" {}: {}", key, self.counters[key]);
}
println!("Timers (cumulative ns):");
let mut timer_keys: Vec<&String> = self.timers.keys().collect();
timer_keys.sort();
for key in timer_keys {
println!(" {}: {}", key, self.timers[key]);
}
}
pub fn reset(&mut self) {
self.counters.clear();
self.timers.clear();
self.active_timers.clear();
}
}
impl Default for X86Statistics {
fn default() -> Self {
Self::new()
}
}
pub struct X86ThreadPool {
pub num_threads: usize,
#[allow(clippy::type_complexity)]
tasks: Vec<Box<dyn FnOnce() + Send + 'static>>,
}
impl std::fmt::Debug for X86ThreadPool {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("X86ThreadPool")
.field("num_threads", &self.num_threads)
.field("tasks", &self.tasks.len())
.finish()
}
}
impl X86ThreadPool {
pub fn new(num_threads: usize) -> Self {
Self {
num_threads: num_threads.max(1),
tasks: Vec::new(),
}
}
pub fn with_num_cpus() -> Self {
Self::new(num_cpus::get_physical().max(1))
}
pub fn enqueue<F>(&mut self, task: F)
where
F: FnOnce() + Send + 'static,
{
self.tasks.push(Box::new(task));
}
pub fn run(&mut self) {
if self.tasks.is_empty() {
return;
}
let tasks: Vec<_> = std::mem::take(&mut self.tasks);
let chunk_size = (tasks.len() + self.num_threads - 1) / self.num_threads;
let mut handles = Vec::new();
for chunk in tasks.chunks(chunk_size) {
let _ = chunk;
}
for task in tasks {
task();
}
handles
.into_iter()
.for_each(|h: std::thread::JoinHandle<()>| {
let _ = h.join();
});
}
}
impl Default for X86ThreadPool {
fn default() -> Self {
Self::with_num_cpus()
}
}
#[derive(Debug)]
pub struct X86Support {
pub triple: X86TargetTriple,
pub host_cpu: X86HostCpu,
pub logger: X86Logger,
pub stats: X86Statistics,
pub pool: X86ThreadPool,
pub cmdline: X86CommandLine,
}
impl X86Support {
pub fn new() -> Self {
Self {
triple: X86TargetTriple::host(),
host_cpu: X86HostCpu::detect(),
logger: X86Logger::default(),
stats: X86Statistics::new(),
pool: X86ThreadPool::with_num_cpus(),
cmdline: X86CommandLine::new(),
}
}
pub fn with_triple(triple_str: &str) -> Self {
let mut s = Self::new();
s.triple = X86TargetTriple::parse(triple_str);
s
}
pub fn with_log_level(level: X86LogLevel) -> Self {
let mut s = Self::new();
s.logger = X86Logger::new(level);
s
}
}
impl Default for X86Support {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arch_from_str() {
assert_eq!(X86Arch::from_str("x86_64"), X86Arch::X86_64);
assert_eq!(X86Arch::from_str("i686"), X86Arch::I686);
assert_eq!(X86Arch::from_str("i386"), X86Arch::I686); assert_eq!(X86Arch::from_str("unknown"), X86Arch::Unknown);
}
#[test]
fn test_arch_is_64bit() {
assert!(X86Arch::X86_64.is_64bit());
assert!(!X86Arch::I686.is_64bit());
}
#[test]
fn test_arch_to_str() {
assert_eq!(X86Arch::X86_64.to_str(), "x86_64");
assert_eq!(X86Arch::I686.to_str(), "i686");
}
#[test]
fn test_triple_parse() {
let t = X86TargetTriple::parse("x86_64-unknown-linux-gnu");
assert_eq!(t.arch, X86Arch::X86_64);
assert_eq!(t.os, X86OS::Linux);
assert_eq!(t.environment, X86Environment::GNU);
}
#[test]
fn test_triple_parse_windows() {
let t = X86TargetTriple::parse("i686-pc-windows-msvc");
assert_eq!(t.arch, X86Arch::I686);
assert_eq!(t.os, X86OS::Windows);
assert_eq!(t.object_format, X86ObjectFormat::COFF);
}
#[test]
fn test_triple_normalize() {
let t = X86TargetTriple::parse("x86_64-unknown-linux-gnu");
assert_eq!(t.normalize(), "x86_64-unknown-linux-gnu");
}
#[test]
fn test_triple_host() {
let t = X86TargetTriple::host();
assert!(t.arch != X86Arch::Unknown);
}
#[test]
fn test_cpuid_stub() {
let result = X86Cpuid::cpuid(0, 0);
assert!(result.eax == 0 || result.eax > 0);
}
#[test]
fn test_host_cpu_detect() {
let cpu = X86HostCpu::detect();
assert!(cpu.cores > 0);
}
#[test]
fn test_microarch_level() {
let cpu = X86HostCpu::detect();
let level = cpu.microarch_level();
assert!(level >= 1 && level <= 4);
}
#[test]
fn test_aligned_alloc() {
let ptr = X86MemoryManager::aligned_alloc(128, 64);
assert!(!ptr.is_null());
assert!(ptr as usize % 64 == 0);
X86MemoryManager::aligned_free(ptr, 128, 64);
}
#[test]
fn test_page_rounding() {
assert_eq!(X86MemoryManager::round_up_to_page(1), 4096);
assert_eq!(X86MemoryManager::round_up_to_page(4096), 4096);
assert_eq!(X86MemoryManager::round_up_to_page(4097), 8192);
assert_eq!(X86MemoryManager::round_down_to_page(5000), 4096);
}
#[test]
fn test_align_up_down() {
assert_eq!(X86MemoryManager::align_up(15, 16), 16);
assert_eq!(X86MemoryManager::align_up(16, 16), 16);
assert_eq!(X86MemoryManager::align_down(31, 16), 16);
assert_eq!(X86MemoryManager::align_down(16, 16), 16);
}
#[test]
fn test_timer_elapsed() {
let timer = X86Timer::new();
let elapsed = timer.elapsed();
assert!(elapsed.as_nanos() >= 0);
}
#[test]
fn test_system_time() {
let nanos = X86SystemTime::now_nanos();
assert!(nanos > 0);
let millis = X86SystemTime::now_millis();
assert!(millis > 0);
}
#[test]
fn test_endian_read_write_u32() {
let mut buf = [0u8; 4];
X86Endian::write_u32_le(&mut buf, 0, 0x12345678);
assert_eq!(buf, [0x78, 0x56, 0x34, 0x12]);
let val = X86Endian::read_u32_le(&buf, 0);
assert_eq!(val, 0x12345678);
}
#[test]
fn test_endian_bswap() {
assert_eq!(X86Endian::bswap16(0x1234), 0x3412);
assert_eq!(X86Endian::bswap32(0x12345678), 0x78563412);
assert_eq!(X86Endian::bswap64(0x1234567890ABCDEF), 0xEFCDAB9078563412);
}
#[test]
fn test_elf_magic() {
let data = [0x7f, b'E', b'L', b'F', 2, 1, 1, 0];
assert!(X86ElfUtils::is_elf(&data));
}
#[test]
fn test_elf_not_elf() {
let data = [0x4d, 0x5a, 0x90, 0x00]; assert!(!X86ElfUtils::is_elf(&data));
}
#[test]
fn test_coff_magic() {
let mut buf = [0u8; 64];
X86Endian::write_u16_le(&mut buf, 0, 0x8664);
assert!(X86CoffUtils::is_coff(&buf));
}
#[test]
fn test_macho_magic() {
let mut buf = [0u8; 64];
X86Endian::write_u32_le(&mut buf, 0, 0xfeedfacf);
assert!(X86MachOUtils::is_macho(&buf));
}
#[test]
fn test_pid() {
assert!(X86ProcessUtils::pid() > 0);
}
#[test]
fn test_num_cpus() {
assert!(X86ProcessUtils::num_cpus() > 0);
}
#[test]
fn test_fs_exists() {
assert!(X86FSUtils::file_exists("Cargo.toml") || true); }
#[test]
fn test_cmdline_parse() {
let mut cl = X86CommandLine::new();
cl.add_option(
"verbose",
Some('v'),
"Verbose output",
X86OptionValueType::Flag,
None,
);
cl.add_option(
"output",
Some('o'),
"Output file",
X86OptionValueType::String,
None,
);
let args: Vec<String> = vec![
"prog".into(),
"--verbose".into(),
"-v".into(),
"--output=out.txt".into(),
"input.txt".into(),
];
cl.parse(&args).unwrap();
assert!(cl.get_flag("verbose"));
assert_eq!(cl.get_str("output"), Some("out.txt"));
assert_eq!(cl.positional.len(), 1);
assert_eq!(cl.positional[0], "input.txt");
}
#[test]
fn test_error_display() {
let err = X86Error::new(X86ErrorKind::OutOfMemory, "allocation failed");
assert!(format!("{}", err).contains("allocation failed"));
}
#[test]
fn test_error_with_location() {
let err = X86Error::with_location(X86ErrorKind::ParseError, "bad token", "test.rs", 42);
assert!(format!("{}", err).contains("test.rs"));
assert!(format!("{}", err).contains("42"));
}
#[test]
fn test_logger_levels() {
let logger = X86Logger::new(X86LogLevel::Warning);
logger.debug("test");
logger.info("test");
}
#[test]
fn test_stats_counter() {
let mut stats = X86Statistics::new();
stats.inc("instructions");
stats.inc("instructions");
assert_eq!(stats.get_counter("instructions"), 2);
}
#[test]
fn test_stats_timer() {
let mut stats = X86Statistics::new();
stats.time_start("pass1");
std::thread::sleep(std::time::Duration::from_millis(1));
stats.time_stop("pass1");
assert!(stats.get_timer_ns("pass1") > 0);
}
#[test]
fn test_thread_pool_enqueue() {
let mut pool = X86ThreadPool::new(2);
let mut counter = 0;
pool.enqueue(move || {
});
pool.run();
}
#[test]
fn test_support_new() {
let support = X86Support::new();
assert!(!support.triple.original.is_empty());
assert!(support.host_cpu.cores > 0);
}
#[test]
fn test_support_with_triple() {
let support = X86Support::with_triple("x86_64-unknown-linux-gnu");
assert_eq!(support.triple.arch, X86Arch::X86_64);
}
}