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