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