@@ -88,36 +88,93 @@ fn mmap(path: &Path) -> Option<Mmap> {
8888
8989cfg_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
98+ // For loading native libraries on Windows, see some discussion on
99+ // rust-lang/rust#71060 for the various strategies here.
105100 fn native_libraries( ) -> Vec <Library > {
106101 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- }
102+ unsafe { add_loaded_images( & mut ret) ; }
119103 return ret;
120104 }
105+
106+ unsafe fn add_loaded_images( ret: & mut Vec <Library >) {
107+ let snap = CreateToolhelp32Snapshot ( TH32CS_SNAPMODULE , 0 ) ;
108+ if snap == INVALID_HANDLE_VALUE {
109+ return ;
110+ }
111+
112+ let mut me = MaybeUninit :: <MODULEENTRY32W >:: zeroed( ) . assume_init( ) ;
113+ me. dwSize = mem:: size_of_val( & me) as DWORD ;
114+ if Module32FirstW ( snap, & mut me) == TRUE {
115+ loop {
116+ if let Some ( lib) = load_library( & me) {
117+ ret. push( lib) ;
118+ }
119+
120+ if Module32NextW ( snap, & mut me) != TRUE {
121+ break ;
122+ }
123+ }
124+
125+ }
126+
127+ CloseHandle ( snap) ;
128+ }
129+
130+ unsafe fn load_library( me: & MODULEENTRY32W ) -> Option <Library > {
131+ let pos = me
132+ . szExePath
133+ . iter( )
134+ . position( |i| * i == 0 )
135+ . unwrap_or( me. szExePath. len( ) ) ;
136+ let name = OsString :: from_wide( & me. szExePath[ ..pos] ) ;
137+
138+ // MinGW libraries currently don't support ASLR
139+ // (rust-lang/rust#16514), but DLLs can still be relocated around in
140+ // the address space. It appears that addresses in debug info are
141+ // all as-if this library was loaded at its "image base", which is a
142+ // field in its COFF file headers. Since this is what debuginfo
143+ // seems to list we parse the symbol table and store addresses as if
144+ // the library was loaded at "image base" as well.
145+ //
146+ // The library may not be loaded at "image base", however.
147+ // (presumably something else may be loaded there?) This is where
148+ // the `bias` field comes into play, and we need to figure out the
149+ // value of `bias` here. Unfortunately though it's not clear how to
150+ // acquire this from a loaded module. What we do have, however, is
151+ // the actual load address (`modBaseAddr`).
152+ //
153+ // As a bit of a cop-out for now we mmap the file, read the file
154+ // header information, then drop the mmap. This is wasteful because
155+ // we'll probably reopen the mmap later, but this should work well
156+ // enough for now.
157+ //
158+ // Once we have the `image_base` (desired load location) and the
159+ // `base_addr` (actual load location) we can fill in the `bias`
160+ // (difference between the actual and desired) and then the stated
161+ // address of each segment is the `image_base` since that's what the
162+ // file says.
163+ //
164+ // For now it appears that unlike ELF/MachO we can make do with one
165+ // segment per library, using `modBaseSize` as the whole size.
166+ let mmap = mmap( name. as_ref( ) ) ?;
167+ let image_base = coff:: get_image_base( & mmap) ?;
168+ let base_addr = me. modBaseAddr as usize ;
169+ Some ( Library {
170+ name,
171+ bias: base_addr. wrapping_sub( image_base) ,
172+ segments: vec![ LibrarySegment {
173+ stated_virtual_memory_address: image_base,
174+ len: me. modBaseSize as usize ,
175+ } ] ,
176+ } )
177+ }
121178 } else if #[ cfg( target_os = "macos" ) ] {
122179 // macOS uses the Mach-O file format and uses DYLD-specific APIs to
123180 // load a list of native libraries that are part of the appplication.
0 commit comments