std\backtrace\src\symbolize\gimli/
libs_windows.rs

1use super::super::super::windows_sys::*;
2use super::mystd::ffi::OsString;
3use super::{coff, mmap, Library, LibrarySegment};
4use alloc::vec;
5use alloc::vec::Vec;
6use core::mem;
7use core::mem::MaybeUninit;
8
9// For loading native libraries on Windows, see some discussion on
10// rust-lang/rust#71060 for the various strategies here.
11pub(super) fn native_libraries() -> Vec<Library> {
12    let mut ret = Vec::new();
13    unsafe {
14        add_loaded_images(&mut ret);
15    }
16    return ret;
17}
18
19unsafe fn add_loaded_images(ret: &mut Vec<Library>) {
20    unsafe {
21        let snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
22        if snap == INVALID_HANDLE_VALUE {
23            return;
24        }
25
26        // huge struct, probably should avoid manually initializing it even if we can
27        let mut me = MaybeUninit::<MODULEENTRY32W>::zeroed().assume_init();
28        me.dwSize = mem::size_of_val(&me) as u32;
29        if Module32FirstW(snap, &mut me) == TRUE {
30            loop {
31                if let Some(lib) = load_library(&me) {
32                    ret.push(lib);
33                }
34
35                if Module32NextW(snap, &mut me) != TRUE {
36                    break;
37                }
38            }
39        }
40
41        CloseHandle(snap);
42    }
43}
44
45// Safety: long_path should be null-terminated
46#[cfg(target_os = "cygwin")]
47unsafe fn get_posix_path(long_path: &[u16]) -> Option<OsString> {
48    use super::mystd::os::unix::ffi::OsStringExt;
49
50    unsafe extern "C" {
51        // Doc: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html
52        // Src: https://github.com/cygwin/cygwin/blob/718a15ba50e0d01c79800bd658c2477f9a603540/winsup/cygwin/path.cc#L3902
53        // Safety:
54        // * `what` should be `CCP_WIN_W_TO_POSIX` here
55        // * `from` is null-terminated UTF-16 path
56        // * `to` is buffer, the buffer size is `size`.
57        fn cygwin_conv_path(
58            what: libc::c_uint,
59            from: *const u16,
60            to: *mut u8,
61            size: libc::size_t,
62        ) -> libc::ssize_t;
63    }
64    const CCP_WIN_W_TO_POSIX: libc::c_uint = 3;
65
66    // If `size` is 0, returns needed buffer size, including null terminator;
67    // or -1 if error.
68    // Safety: if `size` is 0, `to` is not used.
69    let name_len = unsafe {
70        cygwin_conv_path(
71            CCP_WIN_W_TO_POSIX,
72            long_path.as_ptr(),
73            core::ptr::null_mut(),
74            0,
75        )
76    };
77    // Expect at least 1 for null terminator.
78    // It's not likely to return error here.
79    if name_len < 1 {
80        return None;
81    }
82    let name_len = name_len as usize;
83    let mut name_buffer = Vec::with_capacity(name_len);
84    // Safety: `name_buffer` is large enough.
85    let res = unsafe {
86        cygwin_conv_path(
87            CCP_WIN_W_TO_POSIX,
88            long_path.as_ptr(),
89            name_buffer.as_mut_ptr(),
90            name_len,
91        )
92    };
93    // It's not likely to return error here.
94    if res != 0 {
95        return None;
96    }
97    // Remove the null terminator.
98    unsafe { name_buffer.set_len(name_len - 1) };
99    let name = OsString::from_vec(name_buffer);
100    Some(name)
101}
102
103unsafe fn load_library(me: &MODULEENTRY32W) -> Option<Library> {
104    #[cfg(windows)]
105    let name = {
106        use super::mystd::os::windows::prelude::*;
107        let pos = me
108            .szExePath
109            .iter()
110            .position(|i| *i == 0)
111            .unwrap_or(me.szExePath.len());
112        OsString::from_wide(&me.szExePath[..pos])
113    };
114    #[cfg(target_os = "cygwin")]
115    // Safety: the path with max length MAX_PATH always contains a null
116    // terminator. Don't slice it.
117    let name = unsafe { get_posix_path(&me.szExePath[..])? };
118
119    // MinGW libraries currently don't support ASLR
120    // (rust-lang/rust#16514), but DLLs can still be relocated around in
121    // the address space. It appears that addresses in debug info are
122    // all as-if this library was loaded at its "image base", which is a
123    // field in its COFF file headers. Since this is what debuginfo
124    // seems to list we parse the symbol table and store addresses as if
125    // the library was loaded at "image base" as well.
126    //
127    // The library may not be loaded at "image base", however.
128    // (presumably something else may be loaded there?) This is where
129    // the `bias` field comes into play, and we need to figure out the
130    // value of `bias` here. Unfortunately though it's not clear how to
131    // acquire this from a loaded module. What we do have, however, is
132    // the actual load address (`modBaseAddr`).
133    //
134    // As a bit of a cop-out for now we mmap the file, read the file
135    // header information, then drop the mmap. This is wasteful because
136    // we'll probably reopen the mmap later, but this should work well
137    // enough for now.
138    //
139    // Once we have the `image_base` (desired load location) and the
140    // `base_addr` (actual load location) we can fill in the `bias`
141    // (difference between the actual and desired) and then the stated
142    // address of each segment is the `image_base` since that's what the
143    // file says.
144    //
145    // For now it appears that unlike ELF/MachO we can make do with one
146    // segment per library, using `modBaseSize` as the whole size.
147    let mmap = mmap(name.as_ref())?;
148    let image_base = coff::get_image_base(&mmap)?;
149    let base_addr = me.modBaseAddr as usize;
150    Some(Library {
151        name,
152        bias: base_addr.wrapping_sub(image_base),
153        segments: vec![LibrarySegment {
154            stated_virtual_memory_address: image_base,
155            len: me.modBaseSize as usize,
156        }],
157    })
158}