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 5 package syscall
6 7 import (
8 "internal/syscall/windows/sysdll"
9 "sync"
10 "sync/atomic"
11 "unsafe"
12 )
13 14 // DLLError describes reasons for DLL load failures.
15 type DLLError struct {
16 Err error
17 ObjName string
18 Msg string
19 }
20 21 func (e *DLLError) Error() string { return e.Msg }
22 23 func (e *DLLError) Unwrap() error { return e.Err }
24 25 // Implemented in ../runtime/syscall_windows.go.
26 27 // Deprecated: Use [SyscallN] instead.
28 func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
29 30 // Deprecated: Use [SyscallN] instead.
31 func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
32 33 // Deprecated: Use [SyscallN] instead.
34 func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
35 36 // Deprecated: Use [SyscallN] instead.
37 func 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.
40 func 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.
43 func 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
46 func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
47 func loadlibrary(filename *uint16) (handle uintptr, err Errno)
48 func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)
49 func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
50 51 // A DLL implements access to a single DLL.
52 type 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.
65 func 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.
92 func 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.
102 func (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.
124 func (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.
133 func (d *DLL) Release() (err error) {
134 return FreeLibrary(d.Handle)
135 }
136 137 // A Proc implements access to a procedure inside a [DLL].
138 type 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.
146 func (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
166 func (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.
180 type 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.
188 func (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.
208 func (d *LazyDLL) mustLoad() {
209 e := d.Load()
210 if e != nil {
211 panic(e)
212 }
213 }
214 215 // Handle returns d's module handle.
216 func (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.
222 func (d *LazyDLL) NewProc(name string) *LazyProc {
223 return &LazyProc{l: d, Name: name}
224 }
225 226 // NewLazyDLL creates new [LazyDLL] associated with [DLL] file.
227 func 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.
233 type 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.
243 func (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.
267 func (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.
276 func (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
285 func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
286 p.mustFind()
287 return p.proc.Call(a...)
288 }
289