Mercurial > yakumo_izuru > aya
comparison vendor/golang.org/x/sys/windows/dll_windows.go @ 66:787b5ee0289d draft
Use vendored modules
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
author | yakumo.izuru |
---|---|
date | Sun, 23 Jul 2023 13:18:53 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
65:6d985efa0f7a | 66:787b5ee0289d |
---|---|
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) } |