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 syscall
6
7import (
8	"internal/syscall/windows/sysdll"
9	"sync"
10	"sync/atomic"
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.go.
24func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
25func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
26func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
27func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno)
28func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno)
29func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno)
30func loadlibrary(filename *uint16) (handle uintptr, err Errno)
31func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)
32func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
33
34// A DLL implements access to a single DLL.
35type DLL struct {
36	Name   string
37	Handle Handle
38}
39
40// We use this for computing the absolute path for system DLLs on systems
41// where SEARCH_SYSTEM32 is not available.
42var systemDirectoryPrefix string
43
44func init() {
45	n := uint32(MAX_PATH)
46	for {
47		b := make([]uint16, n)
48		l, e := getSystemDirectory(&b[0], n)
49		if e != nil {
50			panic("Unable to determine system directory: " + e.Error())
51		}
52		if l <= n {
53			systemDirectoryPrefix = UTF16ToString(b[:l]) + "\\"
54			break
55		}
56		n = l
57	}
58}
59
60// LoadDLL loads the named DLL file into memory.
61//
62// If name is not an absolute path and is not a known system DLL used by
63// Go, Windows will search for the named DLL in many locations, causing
64// potential DLL preloading attacks.
65//
66// Use LazyDLL in golang.org/x/sys/windows for a secure way to
67// load system DLLs.
68func LoadDLL(name string) (*DLL, error) {
69	namep, err := UTF16PtrFromString(name)
70	if err != nil {
71		return nil, err
72	}
73	var h uintptr
74	var e Errno
75	if sysdll.IsSystemDLL[name] {
76		absoluteFilepathp, err := UTF16PtrFromString(systemDirectoryPrefix + name)
77		if err != nil {
78			return nil, err
79		}
80		h, e = loadsystemlibrary(namep, absoluteFilepathp)
81	} else {
82		h, e = loadlibrary(namep)
83	}
84	if e != 0 {
85		return nil, &DLLError{
86			Err:     e,
87			ObjName: name,
88			Msg:     "Failed to load " + name + ": " + e.Error(),
89		}
90	}
91	d := &DLL{
92		Name:   name,
93		Handle: Handle(h),
94	}
95	return d, nil
96}
97
98// MustLoadDLL is like LoadDLL but panics if load operation fails.
99func MustLoadDLL(name string) *DLL {
100	d, e := LoadDLL(name)
101	if e != nil {
102		panic(e)
103	}
104	return d
105}
106
107// FindProc searches DLL d for procedure named name and returns *Proc
108// if found. It returns an error if search fails.
109func (d *DLL) FindProc(name string) (proc *Proc, err error) {
110	namep, err := BytePtrFromString(name)
111	if err != nil {
112		return nil, err
113	}
114	a, e := getprocaddress(uintptr(d.Handle), namep)
115	if e != 0 {
116		return nil, &DLLError{
117			Err:     e,
118			ObjName: name,
119			Msg:     "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
120		}
121	}
122	p := &Proc{
123		Dll:  d,
124		Name: name,
125		addr: a,
126	}
127	return p, nil
128}
129
130// MustFindProc is like FindProc but panics if search fails.
131func (d *DLL) MustFindProc(name string) *Proc {
132	p, e := d.FindProc(name)
133	if e != nil {
134		panic(e)
135	}
136	return p
137}
138
139// Release unloads DLL d from memory.
140func (d *DLL) Release() (err error) {
141	return FreeLibrary(d.Handle)
142}
143
144// A Proc implements access to a procedure inside a DLL.
145type Proc struct {
146	Dll  *DLL
147	Name string
148	addr uintptr
149}
150
151// Addr returns the address of the procedure represented by p.
152// The return value can be passed to Syscall to run the procedure.
153func (p *Proc) Addr() uintptr {
154	return p.addr
155}
156
157//go:uintptrescapes
158
159// Call executes procedure p with arguments a. It will panic if more than 18 arguments
160// are supplied.
161//
162// The returned error is always non-nil, constructed from the result of GetLastError.
163// Callers must inspect the primary return value to decide whether an error occurred
164// (according to the semantics of the specific function being called) before consulting
165// the error. The error will be guaranteed to contain syscall.Errno.
166func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
167	switch len(a) {
168	case 0:
169		return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
170	case 1:
171		return Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
172	case 2:
173		return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
174	case 3:
175		return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
176	case 4:
177		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
178	case 5:
179		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
180	case 6:
181		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
182	case 7:
183		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
184	case 8:
185		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
186	case 9:
187		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
188	case 10:
189		return 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)
190	case 11:
191		return 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)
192	case 12:
193		return 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])
194	case 13:
195		return 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)
196	case 14:
197		return 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)
198	case 15:
199		return 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])
200	case 16:
201		return Syscall18(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], a[15], 0, 0)
202	case 17:
203		return Syscall18(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], a[15], a[16], 0)
204	case 18:
205		return Syscall18(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], a[15], a[16], a[17])
206	default:
207		panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
208	}
209}
210
211// A LazyDLL implements access to a single DLL.
212// It will delay the load of the DLL until the first
213// call to its Handle method or to one of its
214// LazyProc's Addr method.
215//
216// LazyDLL is subject to the same DLL preloading attacks as documented
217// on LoadDLL.
218//
219// Use LazyDLL in golang.org/x/sys/windows for a secure way to
220// load system DLLs.
221type LazyDLL struct {
222	mu   sync.Mutex
223	dll  *DLL // non nil once DLL is loaded
224	Name string
225}
226
227// Load loads DLL file d.Name into memory. It returns an error if fails.
228// Load will not try to load DLL, if it is already loaded into memory.
229func (d *LazyDLL) Load() error {
230	// Non-racy version of:
231	// if d.dll == nil {
232	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil {
233		d.mu.Lock()
234		defer d.mu.Unlock()
235		if d.dll == nil {
236			dll, e := LoadDLL(d.Name)
237			if e != nil {
238				return e
239			}
240			// Non-racy version of:
241			// d.dll = dll
242			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
243		}
244	}
245	return nil
246}
247
248// mustLoad is like Load but panics if search fails.
249func (d *LazyDLL) mustLoad() {
250	e := d.Load()
251	if e != nil {
252		panic(e)
253	}
254}
255
256// Handle returns d's module handle.
257func (d *LazyDLL) Handle() uintptr {
258	d.mustLoad()
259	return uintptr(d.dll.Handle)
260}
261
262// NewProc returns a LazyProc for accessing the named procedure in the DLL d.
263func (d *LazyDLL) NewProc(name string) *LazyProc {
264	return &LazyProc{l: d, Name: name}
265}
266
267// NewLazyDLL creates new LazyDLL associated with DLL file.
268func NewLazyDLL(name string) *LazyDLL {
269	return &LazyDLL{Name: name}
270}
271
272// A LazyProc implements access to a procedure inside a LazyDLL.
273// It delays the lookup until the Addr, Call, or Find method is called.
274type LazyProc struct {
275	mu   sync.Mutex
276	Name string
277	l    *LazyDLL
278	proc *Proc
279}
280
281// Find searches DLL for procedure named p.Name. It returns
282// an error if search fails. Find will not search procedure,
283// if it is already found and loaded into memory.
284func (p *LazyProc) Find() error {
285	// Non-racy version of:
286	// if p.proc == nil {
287	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
288		p.mu.Lock()
289		defer p.mu.Unlock()
290		if p.proc == nil {
291			e := p.l.Load()
292			if e != nil {
293				return e
294			}
295			proc, e := p.l.dll.FindProc(p.Name)
296			if e != nil {
297				return e
298			}
299			// Non-racy version of:
300			// p.proc = proc
301			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
302		}
303	}
304	return nil
305}
306
307// mustFind is like Find but panics if search fails.
308func (p *LazyProc) mustFind() {
309	e := p.Find()
310	if e != nil {
311		panic(e)
312	}
313}
314
315// Addr returns the address of the procedure represented by p.
316// The return value can be passed to Syscall to run the procedure.
317func (p *LazyProc) Addr() uintptr {
318	p.mustFind()
319	return p.proc.Addr()
320}
321
322//go:uintptrescapes
323
324// Call executes procedure p with arguments a. See the documentation of
325// Proc.Call for more information.
326func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
327	p.mustFind()
328	return p.proc.Call(a...)
329}
330