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