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
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 then 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.
292func (p *LazyProc) Addr() uintptr {
293	p.mustFind()
294	return p.proc.Addr()
295}
296
297//go:uintptrescapes
298
299// Call executes procedure p with arguments a. It will panic, if more then 15 arguments
300// are supplied.
301//
302// The returned error is always non-nil, constructed from the result of GetLastError.
303// Callers must inspect the primary return value to decide whether an error occurred
304// (according to the semantics of the specific function being called) before consulting
305// the error. The error will be guaranteed to contain windows.Errno.
306func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
307	p.mustFind()
308	return p.proc.Call(a...)
309}
310
311var canDoSearchSystem32Once struct {
312	sync.Once
313	v bool
314}
315
316func initCanDoSearchSystem32() {
317	// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
318	// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
319	// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
320	// systems that have KB2533623 installed. To determine whether the
321	// flags are available, use GetProcAddress to get the address of the
322	// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
323	// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
324	// flags can be used with LoadLibraryEx."
325	canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil)
326}
327
328func canDoSearchSystem32() bool {
329	canDoSearchSystem32Once.Do(initCanDoSearchSystem32)
330	return canDoSearchSystem32Once.v
331}
332
333func isBaseName(name string) bool {
334	for _, c := range name {
335		if c == ':' || c == '/' || c == '\\' {
336			return false
337		}
338	}
339	return true
340}
341
342// loadLibraryEx wraps the Windows LoadLibraryEx function.
343//
344// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
345//
346// If name is not an absolute path, LoadLibraryEx searches for the DLL
347// in a variety of automatic locations unless constrained by flags.
348// See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx
349func loadLibraryEx(name string, system bool) (*DLL, error) {
350	loadDLL := name
351	var flags uintptr
352	if system {
353		if canDoSearchSystem32() {
354			const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
355			flags = LOAD_LIBRARY_SEARCH_SYSTEM32
356		} else if isBaseName(name) {
357			// WindowsXP or unpatched Windows machine
358			// trying to load "foo.dll" out of the system
359			// folder, but LoadLibraryEx doesn't support
360			// that yet on their system, so emulate it.
361			windir, _ := Getenv("WINDIR") // old var; apparently works on XP
362			if windir == "" {
363				return nil, errString("%WINDIR% not defined")
364			}
365			loadDLL = windir + "\\System32\\" + name
366		}
367	}
368	h, err := LoadLibraryEx(loadDLL, 0, flags)
369	if err != nil {
370		return nil, err
371	}
372	return &DLL{Name: name, Handle: h}, nil
373}
374
375type errString string
376
377func (s errString) Error() string { return string(s) }
378