std\backtrace\src\symbolize\gimli/libs_windows.rs
1use super::super::super::windows_sys::*;
2use super::mystd::ffi::OsString;
3use super::mystd::os::windows::prelude::*;
4use super::{coff, mmap, Library, LibrarySegment};
5use alloc::vec;
6use alloc::vec::Vec;
7use core::mem;
8use core::mem::MaybeUninit;
9
10// For loading native libraries on Windows, see some discussion on
11// rust-lang/rust#71060 for the various strategies here.
12pub(super) fn native_libraries() -> Vec<Library> {
13 let mut ret = Vec::new();
14 unsafe {
15 add_loaded_images(&mut ret);
16 }
17 return ret;
18}
19
20unsafe fn add_loaded_images(ret: &mut Vec<Library>) {
21 unsafe {
22 let snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
23 if snap == INVALID_HANDLE_VALUE {
24 return;
25 }
26
27 // huge struct, probably should avoid manually initializing it even if we can
28 let mut me = MaybeUninit::<MODULEENTRY32W>::zeroed().assume_init();
29 me.dwSize = mem::size_of_val(&me) as u32;
30 if Module32FirstW(snap, &mut me) == TRUE {
31 loop {
32 if let Some(lib) = load_library(&me) {
33 ret.push(lib);
34 }
35
36 if Module32NextW(snap, &mut me) != TRUE {
37 break;
38 }
39 }
40 }
41
42 CloseHandle(snap);
43 }
44}
45
46unsafe fn load_library(me: &MODULEENTRY32W) -> Option<Library> {
47 let pos = me
48 .szExePath
49 .iter()
50 .position(|i| *i == 0)
51 .unwrap_or(me.szExePath.len());
52 let name = OsString::from_wide(&me.szExePath[..pos]);
53
54 // MinGW libraries currently don't support ASLR
55 // (rust-lang/rust#16514), but DLLs can still be relocated around in
56 // the address space. It appears that addresses in debug info are
57 // all as-if this library was loaded at its "image base", which is a
58 // field in its COFF file headers. Since this is what debuginfo
59 // seems to list we parse the symbol table and store addresses as if
60 // the library was loaded at "image base" as well.
61 //
62 // The library may not be loaded at "image base", however.
63 // (presumably something else may be loaded there?) This is where
64 // the `bias` field comes into play, and we need to figure out the
65 // value of `bias` here. Unfortunately though it's not clear how to
66 // acquire this from a loaded module. What we do have, however, is
67 // the actual load address (`modBaseAddr`).
68 //
69 // As a bit of a cop-out for now we mmap the file, read the file
70 // header information, then drop the mmap. This is wasteful because
71 // we'll probably reopen the mmap later, but this should work well
72 // enough for now.
73 //
74 // Once we have the `image_base` (desired load location) and the
75 // `base_addr` (actual load location) we can fill in the `bias`
76 // (difference between the actual and desired) and then the stated
77 // address of each segment is the `image_base` since that's what the
78 // file says.
79 //
80 // For now it appears that unlike ELF/MachO we can make do with one
81 // segment per library, using `modBaseSize` as the whole size.
82 let mmap = mmap(name.as_ref())?;
83 let image_base = coff::get_image_base(&mmap)?;
84 let base_addr = me.modBaseAddr as usize;
85 Some(Library {
86 name,
87 bias: base_addr.wrapping_sub(image_base),
88 segments: vec![LibrarySegment {
89 stated_virtual_memory_address: image_base,
90 len: me.modBaseSize as usize,
91 }],
92 })
93}