mirror of
https://github.com/Noratrieb/portability.git
synced 2026-01-14 15:55:04 +01:00
rel
This commit is contained in:
parent
402efaee61
commit
303eb47de5
6 changed files with 222 additions and 38 deletions
23
Cargo.lock
generated
23
Cargo.lock
generated
|
|
@ -88,6 +88,27 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naked-function"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b8d5fca6ab1e6215b010aefd3b9ac5aae369dae0faea3a7f34f296cc9f719ac"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"naked-function-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naked-function-macro"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b4123e70df5fe0bb370cff166ae453b9c5324a2cfc932c0f7e55498147a0475"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
|
|
@ -122,8 +143,10 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memmap2",
|
||||
"naked-function",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"windows",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ edition = "2021"
|
|||
[dependencies]
|
||||
bitflags = { version = "2.8.0", features = ["bytemuck"] }
|
||||
bytemuck = { version = "1.21.0", features = ["derive"] }
|
||||
cfg-if = "1.0.0"
|
||||
memmap2 = "0.9.5"
|
||||
naked-function = "0.1.5"
|
||||
tracing = { version = "0.1.41", features = ["attributes"] }
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
|
||||
|
|
|
|||
111
src/emulated.rs
111
src/emulated.rs
|
|
@ -1,3 +1,28 @@
|
|||
mod base_defs {
|
||||
pub(crate) type HANDLE = usize;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct LPCWSTR(pub *const u16);
|
||||
impl std::fmt::Debug for LPCWSTR {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
unsafe {
|
||||
let mut p = self.0;
|
||||
let mut v = p.read();
|
||||
while v != 0 {
|
||||
let c = std::char::decode_utf16([v])
|
||||
.map(Result::unwrap)
|
||||
.collect::<String>();
|
||||
f.write_str(&c)?;
|
||||
p = p.add(1);
|
||||
v = p.read();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_emulation_entry {
|
||||
($($name:ident,)*) => {
|
||||
pub(crate) fn supports_dll(dll_name: &str) -> bool {
|
||||
|
|
@ -32,7 +57,8 @@ define_emulation_entry!(
|
|||
macro_rules! emulate {
|
||||
($dllname:literal, mod $modname:ident {
|
||||
$(
|
||||
fn $name:ident($($args:tt)*) $(-> $ret:ty)? {
|
||||
$(#[$attr:meta])*
|
||||
fn $name:ident($($argname:ident: $argty:ty),* $(,)?) $(-> $ret:ty)? {
|
||||
$($body:tt)*
|
||||
}
|
||||
)*
|
||||
|
|
@ -40,6 +66,9 @@ macro_rules! emulate {
|
|||
mod $modname {
|
||||
pub(super) const DLL_NAME: &str = $dllname;
|
||||
pub(super) fn emulate(dll_name: &str, function_name: &str) -> Option<usize> {
|
||||
#[allow(unused_imports)]
|
||||
use crate::emulated::base_defs::*;
|
||||
|
||||
if dll_name.to_lowercase() != $dllname {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -47,7 +76,8 @@ macro_rules! emulate {
|
|||
$(
|
||||
if function_name == stringify!($name) {
|
||||
unsafe {
|
||||
return Some(std::mem::transmute($name as extern "system" fn()));
|
||||
// NOTE: The ABI string is a lie.....
|
||||
return Some(std::mem::transmute($name::trampoline as unsafe extern "C" fn($($argty),*)));
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
|
@ -56,10 +86,42 @@ macro_rules! emulate {
|
|||
}
|
||||
|
||||
$(
|
||||
// TODO: Windows API adapter...
|
||||
#[allow(non_snake_case)]
|
||||
extern "system" fn $name($($args)*) $(-> $ret)? {
|
||||
$($body)*
|
||||
mod $name {
|
||||
#[allow(unused_imports)]
|
||||
use crate::emulated::base_defs::*;
|
||||
|
||||
// https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(windows)] {
|
||||
pub(super) unsafe extern "C" fn trampoline($($argname: $argty)*) {
|
||||
unsafe { inner($($argname),*) }
|
||||
}
|
||||
} else if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] {
|
||||
#[naked_function::naked]
|
||||
#[export_name = concat!("portability_callconv_trampoline_", stringify!($modname), "__", stringify!($name))]
|
||||
pub(super) unsafe extern "C" fn trampoline($($argname: $argty),*) {
|
||||
// Map Windows arguments to System V arguments.
|
||||
// For now we only support 4 integer arguments.
|
||||
asm!(
|
||||
"mov rdi, rcx", // arg 1
|
||||
"mov rsi, rdx", // arg 2
|
||||
"mov rdx, r8", // arg 3
|
||||
"mov rcx, r9", // arg 4
|
||||
"jmp {}",
|
||||
sym inner,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
compile_error!("unsupported architecture or operating system");
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$attr])*
|
||||
unsafe extern "C" fn inner($($argname: $argty),*) $(-> $ret)? {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { $($body)* }
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
|
@ -331,14 +393,22 @@ emulate!(
|
|||
fn GetCurrentProcess() {
|
||||
todo!("GetCurrentProcess")
|
||||
}
|
||||
fn GetCurrentProcessId() {
|
||||
todo!("GetCurrentProcessId")
|
||||
/// <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessid>
|
||||
fn GetCurrentProcessId() -> u32 {
|
||||
std::process::id()
|
||||
}
|
||||
fn GetCurrentThread() {
|
||||
todo!("GetCurrentThread")
|
||||
}
|
||||
fn GetCurrentThreadId() {
|
||||
todo!("GetCurrentThreadId")
|
||||
// <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid>
|
||||
fn GetCurrentThreadId() -> u32 {
|
||||
use std::sync::atomic;
|
||||
static THREAD_ID_COUNTER: atomic::AtomicU32 = atomic::AtomicU32::new(0);
|
||||
std::thread_local! {
|
||||
static THREAD_ID: u32 = THREAD_ID_COUNTER.fetch_add(1, atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
THREAD_ID.with(|id| *id)
|
||||
}
|
||||
fn GetDateFormatW() {
|
||||
todo!("GetDateFormatW")
|
||||
|
|
@ -439,8 +509,17 @@ emulate!(
|
|||
fn GetSystemTime() {
|
||||
todo!("GetSystemTime")
|
||||
}
|
||||
fn GetSystemTimeAsFileTime() {
|
||||
todo!("GetSystemTimeAsFileTime")
|
||||
/// <https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime>
|
||||
fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: *mut std::ffi::c_void) {
|
||||
#[repr(C)]
|
||||
struct _FILETIME {
|
||||
dwLowDateTime: u32,
|
||||
dwHighDateTime: u32,
|
||||
}
|
||||
lpSystemTimeAsFileTime.cast::<_FILETIME>().write(_FILETIME {
|
||||
dwLowDateTime: 0,
|
||||
dwHighDateTime: 0,
|
||||
});
|
||||
}
|
||||
fn GetSystemTimePreciseAsFileTime() {
|
||||
todo!("GetSystemTimePreciseAsFileTime")
|
||||
|
|
@ -544,8 +623,8 @@ emulate!(
|
|||
fn LoadLibraryExA() {
|
||||
todo!("LoadLibraryExA")
|
||||
}
|
||||
fn LoadLibraryExW() {
|
||||
todo!("LoadLibraryExW")
|
||||
fn LoadLibraryExW(lpLibFileName: LPCWSTR, hFile: HANDLE, dwFlags: u32) {
|
||||
todo!("LoadLibraryExW: {:?}", lpLibFileName)
|
||||
}
|
||||
fn LoadLibraryW() {
|
||||
todo!("LoadLibraryW")
|
||||
|
|
@ -568,8 +647,10 @@ emulate!(
|
|||
fn OpenSemaphoreA() {
|
||||
todo!("OpenSemaphoreA")
|
||||
}
|
||||
fn QueryPerformanceCounter() {
|
||||
todo!("QueryPerformanceCounter")
|
||||
/// <https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter>
|
||||
fn QueryPerformanceCounter(lpPerformanceCount: *mut u64) -> bool {
|
||||
lpPerformanceCount.write(0);
|
||||
true
|
||||
}
|
||||
fn QueryPerformanceFrequency() {
|
||||
todo!("QueryPerformanceFrequency")
|
||||
|
|
|
|||
96
src/lib.rs
96
src/lib.rs
|
|
@ -6,6 +6,7 @@ use std::{
|
|||
fmt::Debug,
|
||||
ops::Deref,
|
||||
path::{Path, PathBuf},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, bytemuck::Zeroable, bytemuck::Pod)]
|
||||
|
|
@ -77,7 +78,7 @@ struct OptionalHeader {
|
|||
#[repr(C)]
|
||||
struct DataDirectory {
|
||||
// RVA
|
||||
va: u32,
|
||||
rva: u32,
|
||||
size: u32,
|
||||
}
|
||||
|
||||
|
|
@ -220,7 +221,25 @@ struct ExportDirectoryTable {
|
|||
name_pointer_rva: u32,
|
||||
ordinal_table_rva: u32,
|
||||
}
|
||||
const _: () = assert!(size_of::<ExportDirectoryTable>() == 40);
|
||||
|
||||
const IMAGE_REL_BASED_ABSOLUTE: u8 = 0;
|
||||
const IMAGE_REL_BASED_DIR64: u8 = 10;
|
||||
|
||||
struct BaseRelocationType(u8);
|
||||
impl Debug for BaseRelocationType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let s = match self.0 {
|
||||
IMAGE_REL_BASED_ABSOLUTE => "IMAGE_REL_BASED_ABSOLUTE",
|
||||
1 => "IMAGE_REL_BASED_HIGH",
|
||||
2 => "IMAGE_REL_BASED_LOW",
|
||||
3 => "IMAGE_REL_BASED_HIGHLOW",
|
||||
4 => "IMAGE_REL_BASED_HIGHADJ",
|
||||
IMAGE_REL_BASED_DIR64 => "IMAGE_REL_BASED_DIR64",
|
||||
_ => "<unknown>",
|
||||
};
|
||||
f.write_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664;
|
||||
const IMAGE_FILE_MACHINE_ARM64: u16 = 0xaa64;
|
||||
|
|
@ -250,6 +269,14 @@ impl<'pe> Deref for Image<'pe> {
|
|||
}
|
||||
}
|
||||
|
||||
struct TheGlobalState {
|
||||
loaded_libraries: Vec<(String, Image<'static>)>,
|
||||
}
|
||||
|
||||
static GLOBAL_STATE: Mutex<TheGlobalState> = Mutex::new(TheGlobalState {
|
||||
loaded_libraries: Vec::new(),
|
||||
});
|
||||
|
||||
#[tracing::instrument(skip(pe, is_dll))]
|
||||
fn load<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe> {
|
||||
load_inner(pe, executable_path, is_dll)
|
||||
|
|
@ -354,15 +381,51 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
|||
);
|
||||
}
|
||||
|
||||
tracing::debug!("Applying relocations");
|
||||
let mut base_relocations = &image[opt_header.base_relocation_table.rva as usize..]
|
||||
[..opt_header.base_relocation_table.size as usize];
|
||||
while !base_relocations.is_empty() {
|
||||
let page_rva = u32::from_ne_bytes(base_relocations[..4].try_into().unwrap());
|
||||
let block_size = u32::from_ne_bytes(base_relocations[4..][..4].try_into().unwrap());
|
||||
|
||||
base_relocations = &base_relocations[8..];
|
||||
|
||||
tracing::trace!(?page_rva, ?block_size, "Base relocation block");
|
||||
|
||||
let remaining = (block_size - 8) / 2;
|
||||
for _ in 0..remaining {
|
||||
let word = u16::from_ne_bytes(base_relocations[..2].try_into().unwrap());
|
||||
let relocation_type = word >> 12;
|
||||
let offset = word & 0xFFF;
|
||||
|
||||
let relocation_type = BaseRelocationType(relocation_type as u8);
|
||||
|
||||
base_relocations = &base_relocations[2..];
|
||||
|
||||
let diff = image.base.wrapping_sub(opt_header.image_base as usize);
|
||||
|
||||
let va = image.base + page_rva as usize + offset as usize;
|
||||
let va_ptr = std::ptr::with_exposed_provenance_mut::<u64>(va);
|
||||
match relocation_type.0 as u8 {
|
||||
IMAGE_REL_BASED_ABSOLUTE => {} // need to ignore
|
||||
IMAGE_REL_BASED_DIR64 => unsafe {
|
||||
let old = va_ptr.read_unaligned();
|
||||
let new = old as usize + diff;
|
||||
va_ptr.write_unaligned(new as u64);
|
||||
},
|
||||
_ => panic!("bad relocation type in {executable_path:?}: {relocation_type:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tracing::debug!("resolving imports");
|
||||
let import_directory_table = bytemuck::cast_slice::<_, ImportDirectoryTableEntry>(
|
||||
&image.loaded[opt_header.import_table.va as usize..]
|
||||
&image.loaded[opt_header.import_table.rva as usize..]
|
||||
[..opt_header.import_table.size as usize],
|
||||
)
|
||||
.to_vec();
|
||||
|
||||
tracing::debug!("checking imports");
|
||||
for import_directory in import_directory_table {
|
||||
tracing::debug!(?import_directory, "Resolving next import");
|
||||
tracing::debug!(?import_directory, "Resolving next import directory");
|
||||
|
||||
let dll_name =
|
||||
CStr::from_bytes_until_nul(&image.loaded[import_directory.name_rva as usize..])
|
||||
|
|
@ -409,7 +472,7 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
|||
// You may be able to skip the binary search of the "hint" of the import is the correct index already.
|
||||
|
||||
let export_directory_table = bytemuck::cast_slice::<u8, ExportDirectoryTable>(
|
||||
&img[img.opt_header.export_table.va as usize..]
|
||||
&img[img.opt_header.export_table.rva as usize..]
|
||||
[..size_of::<ExportDirectoryTable>()],
|
||||
)[0];
|
||||
tracing::debug!(
|
||||
|
|
@ -426,7 +489,7 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
|||
let name = u32::from_ne_bytes(img[name_ptr..][..4].try_into().unwrap());
|
||||
let name = CStr::from_bytes_until_nul(&img[name as usize..]).unwrap();
|
||||
names.push(name.to_owned());
|
||||
tracing::debug!(?name, "DLL {dll_name} has export");
|
||||
tracing::trace!(?name, "DLL {dll_name} has export");
|
||||
}
|
||||
|
||||
LoadedDll::Real(img, export_directory_table, names)
|
||||
|
|
@ -455,8 +518,8 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
|||
+ (unbiased_ordinal * 4);
|
||||
let export_rva = u32::from_ne_bytes(img[eat_addr_rva..][..4].try_into().unwrap());
|
||||
|
||||
if (img.opt_header.export_table.va
|
||||
..(img.opt_header.export_table.va + img.opt_header.export_table.size))
|
||||
if (img.opt_header.export_table.rva
|
||||
..(img.opt_header.export_table.rva + img.opt_header.export_table.size))
|
||||
.contains(&export_rva)
|
||||
{
|
||||
todo!("symbol forwarding")
|
||||
|
|
@ -468,10 +531,13 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
|||
let ordinal_name_flag = import_lookup >> 63;
|
||||
let resolved_va = if ordinal_name_flag == 1 {
|
||||
let ordinal_number = import_lookup & 0xFFFF;
|
||||
tracing::debug!(" import by ordinal: {ordinal_number}");
|
||||
tracing::debug!("import by ordinal: {ordinal_number}");
|
||||
|
||||
match &dll {
|
||||
LoadedDll::Emulated => panic!("unsupported: emulated import via ordinal"),
|
||||
LoadedDll::Emulated => {
|
||||
tracing::error!("unsupported: emulated import via ordinal for {dll_name}. resolving them to 0");
|
||||
0
|
||||
}
|
||||
LoadedDll::Real(img, export_directory_table, _) => {
|
||||
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table
|
||||
let unbiased_ordinal =
|
||||
|
|
@ -530,9 +596,9 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
|||
};
|
||||
|
||||
assert_eq!(size_of::<usize>(), size_of::<u64>());
|
||||
image.loaded[import_directory.import_address_table_rva as usize..]
|
||||
[i * size_of::<u64>()..][..size_of::<u64>()]
|
||||
.copy_from_slice(&resolved_va.to_ne_bytes());
|
||||
let iat = &mut image.loaded[import_directory.import_address_table_rva as usize..]
|
||||
[i * size_of::<u64>()..][..size_of::<u64>()];
|
||||
iat.copy_from_slice(&resolved_va.to_ne_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
23
src/sys.rs
23
src/sys.rs
|
|
@ -93,13 +93,22 @@ mod imp {
|
|||
}
|
||||
|
||||
pub(crate) unsafe fn call_entrypoint_via_stdcall(fnptr: usize) -> u32 {
|
||||
// todo this might be correct or not idk??? is it close enough in this case maybe?? use asm probably.
|
||||
let fnptr = unsafe {
|
||||
std::mem::transmute::<*const (), unsafe extern "C" fn() -> u32>(
|
||||
std::ptr::with_exposed_provenance(fnptr),
|
||||
)
|
||||
};
|
||||
unsafe { fnptr() }
|
||||
let mut out: u32;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "x86_64")] {
|
||||
std::arch::asm!(
|
||||
"call {}",
|
||||
"mov {1:e}, eax",
|
||||
in(reg) fnptr,
|
||||
out(reg) out,
|
||||
);
|
||||
} else {
|
||||
compile_error!("unsupported architecture");
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
SHELL = bash
|
||||
RUSTC = rustc --target x86_64-pc-windows-msvc -Copt-level=3 -Cpanic=abort -Clinker=lld-link -Clink-arg=/NODEFAULTLIB -Clink-arg=/debug:none -Cdebuginfo=0
|
||||
|
||||
build: empty_exe.exe one_dll.exe two_dll.exe
|
||||
build: empty_exe.exe one_dll.exe two_dll.exe constant_data.exe
|
||||
|
||||
empty_exe.exe: empty_exe.rs
|
||||
$(RUSTC) empty_exe.rs
|
||||
|
||||
constant_data.exe: constant_data.rs
|
||||
$(RUSTC) constant_data.rs
|
||||
|
||||
one_dll.exe: one_dll.rs small_dll.dll
|
||||
$(RUSTC) one_dll.rs
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue