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