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