Skip to content

Commit

Permalink
Initial support for MinGW dynamic libs
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcrichton committed Jun 10, 2020
1 parent d4f24b1 commit 5194235
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 25 deletions.
94 changes: 72 additions & 22 deletions src/symbolize/gimli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,36 +88,86 @@ fn mmap(path: &Path) -> Option<Mmap> {

cfg_if::cfg_if! {
if #[cfg(windows)] {
// Windows uses COFF object files and currently doesn't implement
// functionality to load a list of native libraries. This seems to work
// well enough for the main executable but seems pretty likely to not
// work for loaded DLLs. For now this seems sufficient, but we may have
// to extend this over time.
//
// Note that the native_libraries loading here simply returns one
// library encompassing the entire address space. This works naively
// but likely indicates something about ASLR is busted. Let's try to
// fix this over time if necessary!
use core::mem::MaybeUninit;
use crate::windows::*;
use std::os::windows::prelude::*;

mod coff;
use self::coff::Object;

fn native_libraries() -> Vec<Library> {
let mut ret = Vec::new();
if let Ok(path) = std::env::current_exe() {
let mut segments = Vec::new();
segments.push(LibrarySegment {
stated_virtual_memory_address: 0,
len: usize::max_value(),
});
ret.push(Library {
name: path.into(),
segments,
bias: 0,
});
}
unsafe { add_loaded_images(&mut ret); }
return ret;
}

unsafe fn add_loaded_images(ret: &mut Vec<Library>) {
let mut system_info = MaybeUninit::zeroed().assume_init();
GetSystemInfo(&mut system_info);

let mut memory_basic_info = MaybeUninit::zeroed().assume_init();
let query_size = mem::size_of_val(&memory_basic_info);

let start = system_info.lpMinimumApplicationAddress;
let end = system_info.lpMaximumApplicationAddress;
let mut base = start;
while base < end {
if VirtualQuery(base, &mut memory_basic_info, query_size) == 0 {
break;
}
if !base.is_null() {
let alloc_base = memory_basic_info.AllocationBase;

// If this `alloc_base` is the same as our base address we
// queried, then we found a new library.
if alloc_base == base {
if let Some(lib) = get_library(base, &memory_basic_info) {
ret.push(lib);
}

// ... otherwise see if the allocation base is the same as
// the preivous library, and if so we just discovered a new
// segment for the previous library.
} else if let Some(last) = ret.last_mut() {
if last.segments[0].stated_virtual_memory_address == alloc_base as usize {
last.segments.push(LibrarySegment {
stated_virtual_memory_address: base as usize,
len: memory_basic_info.RegionSize,
});
}
}
}

base = ((base as usize) + memory_basic_info.RegionSize) as LPVOID;
}
}

unsafe fn get_library(base: LPVOID, info: &MEMORY_BASIC_INFORMATION) -> Option<Library> {
let mut buf = Vec::with_capacity(128);
let module = base as HMODULE;
loop {
let len = GetModuleFileNameW(module, buf.as_mut_ptr(), buf.capacity() as u32);
if len == 0 {
return None;
}
if len as usize == buf.capacity() && GetLastError() == ERROR_INSUFFICIENT_BUFFER {
let new_cap = buf.capacity() * 2;
buf.reserve(new_cap);
} else {
buf.set_len(len as usize);
break;
}
}
let filename = OsString::from_wide(&buf[..]);
Some(Library {
name: filename.into(),
segments: vec![LibrarySegment {
stated_virtual_memory_address: base as usize,
len: info.RegionSize,
}],
bias: 0,
})
}
} else if #[cfg(target_os = "macos")] {
// macOS uses the Mach-O file format and uses DYLD-specific APIs to
// load a list of native libraries that are part of the appplication.
Expand Down
47 changes: 47 additions & 0 deletions src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,37 @@ ffi! {
pub Reserved1: [DWORD64; 4],
}

#[repr(C)]
pub struct SYSTEM_INFO {
pub u: SYSTEM_INFO_u,
pub dwPageSize: DWORD,
pub lpMinimumApplicationAddress: LPVOID,
pub lpMaximumApplicationAddress: LPVOID,
pub dwActiveProcessorMask: *mut DWORD,
pub dwNumberOfProcessors: DWORD,
pub dwProcessorType: DWORD,
pub dwAllocationGranularity: DWORD,
pub wProcessorLevel: WORD,
pub wProcessorRevision: WORD,
}

#[repr(C)]
pub struct SYSTEM_INFO_u {
pub dwOemId: DWORD,
}

#[repr(C)]
pub struct MEMORY_BASIC_INFORMATION {
pub BaseAddress: PVOID,
pub AllocationBase: PVOID,
pub AllocationProtect: DWORD,
pub PartitionId: WORD,
pub RegionSize: SIZE_T,
pub State: DWORD,
pub Protect: DWORD,
pub Type: DWORD,
}

pub const MAX_SYM_NAME: usize = 2000;
pub const AddrModeFlat: ADDRESS_MODE = 3;
pub const TRUE: BOOL = 1;
Expand All @@ -327,6 +358,7 @@ ffi! {
pub const INFINITE: DWORD = !0;
pub const PAGE_READONLY: DWORD = 2;
pub const FILE_MAP_READ: DWORD = 4;
pub const ERROR_INSUFFICIENT_BUFFER: DWORD = 122;

pub type DWORD = u32;
pub type PDWORD = *mut u32;
Expand All @@ -339,6 +371,7 @@ ffi! {
pub type LPSTR = *mut i8;
pub type LPCSTR = *const i8;
pub type PWSTR = *mut u16;
pub type LPWSTR = *mut u16;
pub type WORD = u16;
pub type ULONG = u32;
pub type ULONG64 = u64;
Expand All @@ -350,6 +383,8 @@ ffi! {
pub type SIZE_T = usize;
pub type LPVOID = *mut c_void;
pub type LPCVOID = *const c_void;
pub type LPSYSTEM_INFO = *mut SYSTEM_INFO;
pub type PMEMORY_BASIC_INFORMATION = *mut MEMORY_BASIC_INFORMATION;

extern "system" {
pub fn GetCurrentProcess() -> HANDLE;
Expand Down Expand Up @@ -401,6 +436,18 @@ ffi! {
dwNumberOfBytesToMap: SIZE_T,
) -> LPVOID;
pub fn UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL;
pub fn GetSystemInfo(info: LPSYSTEM_INFO) -> ();
pub fn VirtualQuery(
lpAddress: LPCVOID,
lpBuffer: PMEMORY_BASIC_INFORMATION,
dwLength: SIZE_T,
) -> SIZE_T;
pub fn GetModuleFileNameW(
hModule: HMODULE,
lpFilename: LPWSTR,
nSize: DWORD,
) -> DWORD;
pub fn GetLastError() -> DWORD;
}
}

Expand Down
4 changes: 1 addition & 3 deletions tests/accuracy/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ type Pos = (&'static str, u32);
fn doit() {
// Skip musl which is by default statically linked and doesn't support
// dynamic libraries.
//
// FIXME(#333) doesn't work on MinGW yet
if !cfg!(target_env = "musl") && !(cfg!(windows) && cfg!(target_env = "gnu")) {
if !cfg!(target_env = "musl") {
// TODO(#238) this shouldn't have to happen first in this function, but
// currently it does.
let mut dir = std::env::current_exe().unwrap();
Expand Down

0 comments on commit 5194235

Please sign in to comment.