blob: b47ed42459bc43a0981ee4cdac0b8733a9b000de [file] [log] [blame]
use super::super::super::windows::*;
use super::mystd::os::windows::prelude::*;
use super::{coff, mmap, Library, LibrarySegment, OsString};
use alloc::vec;
use alloc::vec::Vec;
use core::mem;
use core::mem::MaybeUninit;
// For loading native libraries on Windows, see some discussion on
// rust-lang/rust#71060 for the various strategies here.
pub(super) fn native_libraries() -> Vec<Library> {
let mut ret = Vec::new();
unsafe {
add_loaded_images(&mut ret);
}
return ret;
}
unsafe fn add_loaded_images(ret: &mut Vec<Library>) {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
if snap == INVALID_HANDLE_VALUE {
return;
}
let mut me = MaybeUninit::<MODULEENTRY32W>::zeroed().assume_init();
me.dwSize = mem::size_of_val(&me) as DWORD;
if Module32FirstW(snap, &mut me) == TRUE {
loop {
if let Some(lib) = load_library(&me) {
ret.push(lib);
}
if Module32NextW(snap, &mut me) != TRUE {
break;
}
}
}
CloseHandle(snap);
}
unsafe fn load_library(me: &MODULEENTRY32W) -> Option<Library> {
let pos = me
.szExePath
.iter()
.position(|i| *i == 0)
.unwrap_or(me.szExePath.len());
let name = OsString::from_wide(&me.szExePath[..pos]);
// MinGW libraries currently don't support ASLR
// (rust-lang/rust#16514), but DLLs can still be relocated around in
// the address space. It appears that addresses in debug info are
// all as-if this library was loaded at its "image base", which is a
// field in its COFF file headers. Since this is what debuginfo
// seems to list we parse the symbol table and store addresses as if
// the library was loaded at "image base" as well.
//
// The library may not be loaded at "image base", however.
// (presumably something else may be loaded there?) This is where
// the `bias` field comes into play, and we need to figure out the
// value of `bias` here. Unfortunately though it's not clear how to
// acquire this from a loaded module. What we do have, however, is
// the actual load address (`modBaseAddr`).
//
// As a bit of a cop-out for now we mmap the file, read the file
// header information, then drop the mmap. This is wasteful because
// we'll probably reopen the mmap later, but this should work well
// enough for now.
//
// Once we have the `image_base` (desired load location) and the
// `base_addr` (actual load location) we can fill in the `bias`
// (difference between the actual and desired) and then the stated
// address of each segment is the `image_base` since that's what the
// file says.
//
// For now it appears that unlike ELF/MachO we can make do with one
// segment per library, using `modBaseSize` as the whole size.
let mmap = mmap(name.as_ref())?;
let image_base = coff::get_image_base(&mmap)?;
let base_addr = me.modBaseAddr as usize;
Some(Library {
name,
bias: base_addr.wrapping_sub(image_base),
segments: vec![LibrarySegment {
stated_virtual_memory_address: image_base,
len: me.modBaseSize as usize,
}],
})
}