Skip to content

Commit 5194235

Browse files
committed
Initial support for MinGW dynamic libs
1 parent d4f24b1 commit 5194235

File tree

3 files changed

+120
-25
lines changed

3 files changed

+120
-25
lines changed

src/symbolize/gimli.rs

+72-22
Original file line numberDiff line numberDiff line change
@@ -88,36 +88,86 @@ fn mmap(path: &Path) -> Option<Mmap> {
8888

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

10295
mod coff;
10396
use self::coff::Object;
10497

10598
fn native_libraries() -> Vec<Library> {
10699
let mut ret = Vec::new();
107-
if let Ok(path) = std::env::current_exe() {
108-
let mut segments = Vec::new();
109-
segments.push(LibrarySegment {
110-
stated_virtual_memory_address: 0,
111-
len: usize::max_value(),
112-
});
113-
ret.push(Library {
114-
name: path.into(),
115-
segments,
116-
bias: 0,
117-
});
118-
}
100+
unsafe { add_loaded_images(&mut ret); }
119101
return ret;
120102
}
103+
104+
unsafe fn add_loaded_images(ret: &mut Vec<Library>) {
105+
let mut system_info = MaybeUninit::zeroed().assume_init();
106+
GetSystemInfo(&mut system_info);
107+
108+
let mut memory_basic_info = MaybeUninit::zeroed().assume_init();
109+
let query_size = mem::size_of_val(&memory_basic_info);
110+
111+
let start = system_info.lpMinimumApplicationAddress;
112+
let end = system_info.lpMaximumApplicationAddress;
113+
let mut base = start;
114+
while base < end {
115+
if VirtualQuery(base, &mut memory_basic_info, query_size) == 0 {
116+
break;
117+
}
118+
if !base.is_null() {
119+
let alloc_base = memory_basic_info.AllocationBase;
120+
121+
// If this `alloc_base` is the same as our base address we
122+
// queried, then we found a new library.
123+
if alloc_base == base {
124+
if let Some(lib) = get_library(base, &memory_basic_info) {
125+
ret.push(lib);
126+
}
127+
128+
// ... otherwise see if the allocation base is the same as
129+
// the preivous library, and if so we just discovered a new
130+
// segment for the previous library.
131+
} else if let Some(last) = ret.last_mut() {
132+
if last.segments[0].stated_virtual_memory_address == alloc_base as usize {
133+
last.segments.push(LibrarySegment {
134+
stated_virtual_memory_address: base as usize,
135+
len: memory_basic_info.RegionSize,
136+
});
137+
}
138+
}
139+
}
140+
141+
base = ((base as usize) + memory_basic_info.RegionSize) as LPVOID;
142+
}
143+
}
144+
145+
unsafe fn get_library(base: LPVOID, info: &MEMORY_BASIC_INFORMATION) -> Option<Library> {
146+
let mut buf = Vec::with_capacity(128);
147+
let module = base as HMODULE;
148+
loop {
149+
let len = GetModuleFileNameW(module, buf.as_mut_ptr(), buf.capacity() as u32);
150+
if len == 0 {
151+
return None;
152+
}
153+
if len as usize == buf.capacity() && GetLastError() == ERROR_INSUFFICIENT_BUFFER {
154+
let new_cap = buf.capacity() * 2;
155+
buf.reserve(new_cap);
156+
} else {
157+
buf.set_len(len as usize);
158+
break;
159+
}
160+
}
161+
let filename = OsString::from_wide(&buf[..]);
162+
Some(Library {
163+
name: filename.into(),
164+
segments: vec![LibrarySegment {
165+
stated_virtual_memory_address: base as usize,
166+
len: info.RegionSize,
167+
}],
168+
bias: 0,
169+
})
170+
}
121171
} else if #[cfg(target_os = "macos")] {
122172
// macOS uses the Mach-O file format and uses DYLD-specific APIs to
123173
// load a list of native libraries that are part of the appplication.

src/windows.rs

+47
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,37 @@ ffi! {
311311
pub Reserved1: [DWORD64; 4],
312312
}
313313

314+
#[repr(C)]
315+
pub struct SYSTEM_INFO {
316+
pub u: SYSTEM_INFO_u,
317+
pub dwPageSize: DWORD,
318+
pub lpMinimumApplicationAddress: LPVOID,
319+
pub lpMaximumApplicationAddress: LPVOID,
320+
pub dwActiveProcessorMask: *mut DWORD,
321+
pub dwNumberOfProcessors: DWORD,
322+
pub dwProcessorType: DWORD,
323+
pub dwAllocationGranularity: DWORD,
324+
pub wProcessorLevel: WORD,
325+
pub wProcessorRevision: WORD,
326+
}
327+
328+
#[repr(C)]
329+
pub struct SYSTEM_INFO_u {
330+
pub dwOemId: DWORD,
331+
}
332+
333+
#[repr(C)]
334+
pub struct MEMORY_BASIC_INFORMATION {
335+
pub BaseAddress: PVOID,
336+
pub AllocationBase: PVOID,
337+
pub AllocationProtect: DWORD,
338+
pub PartitionId: WORD,
339+
pub RegionSize: SIZE_T,
340+
pub State: DWORD,
341+
pub Protect: DWORD,
342+
pub Type: DWORD,
343+
}
344+
314345
pub const MAX_SYM_NAME: usize = 2000;
315346
pub const AddrModeFlat: ADDRESS_MODE = 3;
316347
pub const TRUE: BOOL = 1;
@@ -327,6 +358,7 @@ ffi! {
327358
pub const INFINITE: DWORD = !0;
328359
pub const PAGE_READONLY: DWORD = 2;
329360
pub const FILE_MAP_READ: DWORD = 4;
361+
pub const ERROR_INSUFFICIENT_BUFFER: DWORD = 122;
330362

331363
pub type DWORD = u32;
332364
pub type PDWORD = *mut u32;
@@ -339,6 +371,7 @@ ffi! {
339371
pub type LPSTR = *mut i8;
340372
pub type LPCSTR = *const i8;
341373
pub type PWSTR = *mut u16;
374+
pub type LPWSTR = *mut u16;
342375
pub type WORD = u16;
343376
pub type ULONG = u32;
344377
pub type ULONG64 = u64;
@@ -350,6 +383,8 @@ ffi! {
350383
pub type SIZE_T = usize;
351384
pub type LPVOID = *mut c_void;
352385
pub type LPCVOID = *const c_void;
386+
pub type LPSYSTEM_INFO = *mut SYSTEM_INFO;
387+
pub type PMEMORY_BASIC_INFORMATION = *mut MEMORY_BASIC_INFORMATION;
353388

354389
extern "system" {
355390
pub fn GetCurrentProcess() -> HANDLE;
@@ -401,6 +436,18 @@ ffi! {
401436
dwNumberOfBytesToMap: SIZE_T,
402437
) -> LPVOID;
403438
pub fn UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL;
439+
pub fn GetSystemInfo(info: LPSYSTEM_INFO) -> ();
440+
pub fn VirtualQuery(
441+
lpAddress: LPCVOID,
442+
lpBuffer: PMEMORY_BASIC_INFORMATION,
443+
dwLength: SIZE_T,
444+
) -> SIZE_T;
445+
pub fn GetModuleFileNameW(
446+
hModule: HMODULE,
447+
lpFilename: LPWSTR,
448+
nSize: DWORD,
449+
) -> DWORD;
450+
pub fn GetLastError() -> DWORD;
404451
}
405452
}
406453

tests/accuracy/main.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ type Pos = (&'static str, u32);
1818
fn doit() {
1919
// Skip musl which is by default statically linked and doesn't support
2020
// dynamic libraries.
21-
//
22-
// FIXME(#333) doesn't work on MinGW yet
23-
if !cfg!(target_env = "musl") && !(cfg!(windows) && cfg!(target_env = "gnu")) {
21+
if !cfg!(target_env = "musl") {
2422
// TODO(#238) this shouldn't have to happen first in this function, but
2523
// currently it does.
2624
let mut dir = std::env::current_exe().unwrap();

0 commit comments

Comments
 (0)