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