|
1 // Copyright 2011 The Go Authors. All rights reserved. |
|
2 // Use of this source code is governed by a BSD-style |
|
3 // license that can be found in the LICENSE file. |
|
4 |
|
5 package windows |
|
6 |
|
7 import ( |
|
8 "sync" |
|
9 "sync/atomic" |
|
10 "syscall" |
|
11 "unsafe" |
|
12 ) |
|
13 |
|
14 // We need to use LoadLibrary and GetProcAddress from the Go runtime, because |
|
15 // the these symbols are loaded by the system linker and are required to |
|
16 // dynamically load additional symbols. Note that in the Go runtime, these |
|
17 // return syscall.Handle and syscall.Errno, but these are the same, in fact, |
|
18 // as windows.Handle and windows.Errno, and we intend to keep these the same. |
|
19 |
|
20 //go:linkname syscall_loadlibrary syscall.loadlibrary |
|
21 func syscall_loadlibrary(filename *uint16) (handle Handle, err Errno) |
|
22 |
|
23 //go:linkname syscall_getprocaddress syscall.getprocaddress |
|
24 func syscall_getprocaddress(handle Handle, procname *uint8) (proc uintptr, err Errno) |
|
25 |
|
26 // DLLError describes reasons for DLL load failures. |
|
27 type DLLError struct { |
|
28 Err error |
|
29 ObjName string |
|
30 Msg string |
|
31 } |
|
32 |
|
33 func (e *DLLError) Error() string { return e.Msg } |
|
34 |
|
35 func (e *DLLError) Unwrap() error { return e.Err } |
|
36 |
|
37 // A DLL implements access to a single DLL. |
|
38 type DLL struct { |
|
39 Name string |
|
40 Handle Handle |
|
41 } |
|
42 |
|
43 // LoadDLL loads DLL file into memory. |
|
44 // |
|
45 // Warning: using LoadDLL without an absolute path name is subject to |
|
46 // DLL preloading attacks. To safely load a system DLL, use LazyDLL |
|
47 // with System set to true, or use LoadLibraryEx directly. |
|
48 func LoadDLL(name string) (dll *DLL, err error) { |
|
49 namep, err := UTF16PtrFromString(name) |
|
50 if err != nil { |
|
51 return nil, err |
|
52 } |
|
53 h, e := syscall_loadlibrary(namep) |
|
54 if e != 0 { |
|
55 return nil, &DLLError{ |
|
56 Err: e, |
|
57 ObjName: name, |
|
58 Msg: "Failed to load " + name + ": " + e.Error(), |
|
59 } |
|
60 } |
|
61 d := &DLL{ |
|
62 Name: name, |
|
63 Handle: h, |
|
64 } |
|
65 return d, nil |
|
66 } |
|
67 |
|
68 // MustLoadDLL is like LoadDLL but panics if load operation failes. |
|
69 func MustLoadDLL(name string) *DLL { |
|
70 d, e := LoadDLL(name) |
|
71 if e != nil { |
|
72 panic(e) |
|
73 } |
|
74 return d |
|
75 } |
|
76 |
|
77 // FindProc searches DLL d for procedure named name and returns *Proc |
|
78 // if found. It returns an error if search fails. |
|
79 func (d *DLL) FindProc(name string) (proc *Proc, err error) { |
|
80 namep, err := BytePtrFromString(name) |
|
81 if err != nil { |
|
82 return nil, err |
|
83 } |
|
84 a, e := syscall_getprocaddress(d.Handle, namep) |
|
85 if e != 0 { |
|
86 return nil, &DLLError{ |
|
87 Err: e, |
|
88 ObjName: name, |
|
89 Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(), |
|
90 } |
|
91 } |
|
92 p := &Proc{ |
|
93 Dll: d, |
|
94 Name: name, |
|
95 addr: a, |
|
96 } |
|
97 return p, nil |
|
98 } |
|
99 |
|
100 // MustFindProc is like FindProc but panics if search fails. |
|
101 func (d *DLL) MustFindProc(name string) *Proc { |
|
102 p, e := d.FindProc(name) |
|
103 if e != nil { |
|
104 panic(e) |
|
105 } |
|
106 return p |
|
107 } |
|
108 |
|
109 // FindProcByOrdinal searches DLL d for procedure by ordinal and returns *Proc |
|
110 // if found. It returns an error if search fails. |
|
111 func (d *DLL) FindProcByOrdinal(ordinal uintptr) (proc *Proc, err error) { |
|
112 a, e := GetProcAddressByOrdinal(d.Handle, ordinal) |
|
113 name := "#" + itoa(int(ordinal)) |
|
114 if e != nil { |
|
115 return nil, &DLLError{ |
|
116 Err: e, |
|
117 ObjName: name, |
|
118 Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(), |
|
119 } |
|
120 } |
|
121 p := &Proc{ |
|
122 Dll: d, |
|
123 Name: name, |
|
124 addr: a, |
|
125 } |
|
126 return p, nil |
|
127 } |
|
128 |
|
129 // MustFindProcByOrdinal is like FindProcByOrdinal but panics if search fails. |
|
130 func (d *DLL) MustFindProcByOrdinal(ordinal uintptr) *Proc { |
|
131 p, e := d.FindProcByOrdinal(ordinal) |
|
132 if e != nil { |
|
133 panic(e) |
|
134 } |
|
135 return p |
|
136 } |
|
137 |
|
138 // Release unloads DLL d from memory. |
|
139 func (d *DLL) Release() (err error) { |
|
140 return FreeLibrary(d.Handle) |
|
141 } |
|
142 |
|
143 // A Proc implements access to a procedure inside a DLL. |
|
144 type Proc struct { |
|
145 Dll *DLL |
|
146 Name string |
|
147 addr uintptr |
|
148 } |
|
149 |
|
150 // Addr returns the address of the procedure represented by p. |
|
151 // The return value can be passed to Syscall to run the procedure. |
|
152 func (p *Proc) Addr() uintptr { |
|
153 return p.addr |
|
154 } |
|
155 |
|
156 //go:uintptrescapes |
|
157 |
|
158 // Call executes procedure p with arguments a. It will panic, if more than 15 arguments |
|
159 // are supplied. |
|
160 // |
|
161 // The returned error is always non-nil, constructed from the result of GetLastError. |
|
162 // Callers must inspect the primary return value to decide whether an error occurred |
|
163 // (according to the semantics of the specific function being called) before consulting |
|
164 // the error. The error will be guaranteed to contain windows.Errno. |
|
165 func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { |
|
166 switch len(a) { |
|
167 case 0: |
|
168 return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0) |
|
169 case 1: |
|
170 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0) |
|
171 case 2: |
|
172 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0) |
|
173 case 3: |
|
174 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2]) |
|
175 case 4: |
|
176 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0) |
|
177 case 5: |
|
178 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0) |
|
179 case 6: |
|
180 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5]) |
|
181 case 7: |
|
182 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0) |
|
183 case 8: |
|
184 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0) |
|
185 case 9: |
|
186 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]) |
|
187 case 10: |
|
188 return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0) |
|
189 case 11: |
|
190 return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0) |
|
191 case 12: |
|
192 return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]) |
|
193 case 13: |
|
194 return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0) |
|
195 case 14: |
|
196 return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0) |
|
197 case 15: |
|
198 return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14]) |
|
199 default: |
|
200 panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".") |
|
201 } |
|
202 } |
|
203 |
|
204 // A LazyDLL implements access to a single DLL. |
|
205 // It will delay the load of the DLL until the first |
|
206 // call to its Handle method or to one of its |
|
207 // LazyProc's Addr method. |
|
208 type LazyDLL struct { |
|
209 Name string |
|
210 |
|
211 // System determines whether the DLL must be loaded from the |
|
212 // Windows System directory, bypassing the normal DLL search |
|
213 // path. |
|
214 System bool |
|
215 |
|
216 mu sync.Mutex |
|
217 dll *DLL // non nil once DLL is loaded |
|
218 } |
|
219 |
|
220 // Load loads DLL file d.Name into memory. It returns an error if fails. |
|
221 // Load will not try to load DLL, if it is already loaded into memory. |
|
222 func (d *LazyDLL) Load() error { |
|
223 // Non-racy version of: |
|
224 // if d.dll != nil { |
|
225 if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil { |
|
226 return nil |
|
227 } |
|
228 d.mu.Lock() |
|
229 defer d.mu.Unlock() |
|
230 if d.dll != nil { |
|
231 return nil |
|
232 } |
|
233 |
|
234 // kernel32.dll is special, since it's where LoadLibraryEx comes from. |
|
235 // The kernel already special-cases its name, so it's always |
|
236 // loaded from system32. |
|
237 var dll *DLL |
|
238 var err error |
|
239 if d.Name == "kernel32.dll" { |
|
240 dll, err = LoadDLL(d.Name) |
|
241 } else { |
|
242 dll, err = loadLibraryEx(d.Name, d.System) |
|
243 } |
|
244 if err != nil { |
|
245 return err |
|
246 } |
|
247 |
|
248 // Non-racy version of: |
|
249 // d.dll = dll |
|
250 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll)) |
|
251 return nil |
|
252 } |
|
253 |
|
254 // mustLoad is like Load but panics if search fails. |
|
255 func (d *LazyDLL) mustLoad() { |
|
256 e := d.Load() |
|
257 if e != nil { |
|
258 panic(e) |
|
259 } |
|
260 } |
|
261 |
|
262 // Handle returns d's module handle. |
|
263 func (d *LazyDLL) Handle() uintptr { |
|
264 d.mustLoad() |
|
265 return uintptr(d.dll.Handle) |
|
266 } |
|
267 |
|
268 // NewProc returns a LazyProc for accessing the named procedure in the DLL d. |
|
269 func (d *LazyDLL) NewProc(name string) *LazyProc { |
|
270 return &LazyProc{l: d, Name: name} |
|
271 } |
|
272 |
|
273 // NewLazyDLL creates new LazyDLL associated with DLL file. |
|
274 func NewLazyDLL(name string) *LazyDLL { |
|
275 return &LazyDLL{Name: name} |
|
276 } |
|
277 |
|
278 // NewLazySystemDLL is like NewLazyDLL, but will only |
|
279 // search Windows System directory for the DLL if name is |
|
280 // a base name (like "advapi32.dll"). |
|
281 func NewLazySystemDLL(name string) *LazyDLL { |
|
282 return &LazyDLL{Name: name, System: true} |
|
283 } |
|
284 |
|
285 // A LazyProc implements access to a procedure inside a LazyDLL. |
|
286 // It delays the lookup until the Addr method is called. |
|
287 type LazyProc struct { |
|
288 Name string |
|
289 |
|
290 mu sync.Mutex |
|
291 l *LazyDLL |
|
292 proc *Proc |
|
293 } |
|
294 |
|
295 // Find searches DLL for procedure named p.Name. It returns |
|
296 // an error if search fails. Find will not search procedure, |
|
297 // if it is already found and loaded into memory. |
|
298 func (p *LazyProc) Find() error { |
|
299 // Non-racy version of: |
|
300 // if p.proc == nil { |
|
301 if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil { |
|
302 p.mu.Lock() |
|
303 defer p.mu.Unlock() |
|
304 if p.proc == nil { |
|
305 e := p.l.Load() |
|
306 if e != nil { |
|
307 return e |
|
308 } |
|
309 proc, e := p.l.dll.FindProc(p.Name) |
|
310 if e != nil { |
|
311 return e |
|
312 } |
|
313 // Non-racy version of: |
|
314 // p.proc = proc |
|
315 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc)) |
|
316 } |
|
317 } |
|
318 return nil |
|
319 } |
|
320 |
|
321 // mustFind is like Find but panics if search fails. |
|
322 func (p *LazyProc) mustFind() { |
|
323 e := p.Find() |
|
324 if e != nil { |
|
325 panic(e) |
|
326 } |
|
327 } |
|
328 |
|
329 // Addr returns the address of the procedure represented by p. |
|
330 // The return value can be passed to Syscall to run the procedure. |
|
331 // It will panic if the procedure cannot be found. |
|
332 func (p *LazyProc) Addr() uintptr { |
|
333 p.mustFind() |
|
334 return p.proc.Addr() |
|
335 } |
|
336 |
|
337 //go:uintptrescapes |
|
338 |
|
339 // Call executes procedure p with arguments a. It will panic, if more than 15 arguments |
|
340 // are supplied. It will also panic if the procedure cannot be found. |
|
341 // |
|
342 // The returned error is always non-nil, constructed from the result of GetLastError. |
|
343 // Callers must inspect the primary return value to decide whether an error occurred |
|
344 // (according to the semantics of the specific function being called) before consulting |
|
345 // the error. The error will be guaranteed to contain windows.Errno. |
|
346 func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { |
|
347 p.mustFind() |
|
348 return p.proc.Call(a...) |
|
349 } |
|
350 |
|
351 var canDoSearchSystem32Once struct { |
|
352 sync.Once |
|
353 v bool |
|
354 } |
|
355 |
|
356 func initCanDoSearchSystem32() { |
|
357 // https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: |
|
358 // "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows |
|
359 // Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on |
|
360 // systems that have KB2533623 installed. To determine whether the |
|
361 // flags are available, use GetProcAddress to get the address of the |
|
362 // AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories |
|
363 // function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* |
|
364 // flags can be used with LoadLibraryEx." |
|
365 canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil) |
|
366 } |
|
367 |
|
368 func canDoSearchSystem32() bool { |
|
369 canDoSearchSystem32Once.Do(initCanDoSearchSystem32) |
|
370 return canDoSearchSystem32Once.v |
|
371 } |
|
372 |
|
373 func isBaseName(name string) bool { |
|
374 for _, c := range name { |
|
375 if c == ':' || c == '/' || c == '\\' { |
|
376 return false |
|
377 } |
|
378 } |
|
379 return true |
|
380 } |
|
381 |
|
382 // loadLibraryEx wraps the Windows LoadLibraryEx function. |
|
383 // |
|
384 // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx |
|
385 // |
|
386 // If name is not an absolute path, LoadLibraryEx searches for the DLL |
|
387 // in a variety of automatic locations unless constrained by flags. |
|
388 // See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx |
|
389 func loadLibraryEx(name string, system bool) (*DLL, error) { |
|
390 loadDLL := name |
|
391 var flags uintptr |
|
392 if system { |
|
393 if canDoSearchSystem32() { |
|
394 flags = LOAD_LIBRARY_SEARCH_SYSTEM32 |
|
395 } else if isBaseName(name) { |
|
396 // WindowsXP or unpatched Windows machine |
|
397 // trying to load "foo.dll" out of the system |
|
398 // folder, but LoadLibraryEx doesn't support |
|
399 // that yet on their system, so emulate it. |
|
400 systemdir, err := GetSystemDirectory() |
|
401 if err != nil { |
|
402 return nil, err |
|
403 } |
|
404 loadDLL = systemdir + "\\" + name |
|
405 } |
|
406 } |
|
407 h, err := LoadLibraryEx(loadDLL, 0, flags) |
|
408 if err != nil { |
|
409 return nil, err |
|
410 } |
|
411 return &DLL{Name: name, Handle: h}, nil |
|
412 } |
|
413 |
|
414 type errString string |
|
415 |
|
416 func (s errString) Error() string { return string(s) } |