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