@@ -88,36 +88,93 @@ fn mmap(path: &Path) -> Option<Mmap> {
88
88
89
89
cfg_if:: cfg_if! {
90
90
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:: * ;
101
94
102
95
mod coff;
103
96
use self :: coff:: Object ;
104
97
98
+ // For loading native libraries on Windows, see some discussion on
99
+ // rust-lang/rust#71060 for the various strategies here.
105
100
fn native_libraries( ) -> Vec <Library > {
106
101
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) ; }
119
103
return ret;
120
104
}
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
+ }
121
178
} else if #[ cfg( target_os = "macos" ) ] {
122
179
// macOS uses the Mach-O file format and uses DYLD-specific APIs to
123
180
// load a list of native libraries that are part of the appplication.
0 commit comments