dll_windows.go (12311B)
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 windows 6 7 import ( 8 "sync" 9 "sync/atomic" 10 "syscall" 11 "unsafe" 12 ) 13 14 // We need to use LoadLibrary and GetProcAddress from the Go runtime, because 15 // the these symbols are loaded by the system linker and are required to 16 // dynamically load additional symbols. Note that in the Go runtime, these 17 // return syscall.Handle and syscall.Errno, but these are the same, in fact, 18 // as windows.Handle and windows.Errno, and we intend to keep these the same. 19 20 //go:linkname syscall_loadlibrary syscall.loadlibrary 21 func syscall_loadlibrary(filename *uint16) (handle Handle, err Errno) 22 23 //go:linkname syscall_getprocaddress syscall.getprocaddress 24 func syscall_getprocaddress(handle Handle, procname *uint8) (proc uintptr, err Errno) 25 26 // DLLError describes reasons for DLL load failures. 27 type DLLError struct { 28 Err error 29 ObjName string 30 Msg string 31 } 32 33 func (e *DLLError) Error() string { return e.Msg } 34 35 // A DLL implements access to a single DLL. 36 type DLL struct { 37 Name string 38 Handle Handle 39 } 40 41 // LoadDLL loads DLL file into memory. 42 // 43 // Warning: using LoadDLL without an absolute path name is subject to 44 // DLL preloading attacks. To safely load a system DLL, use LazyDLL 45 // with System set to true, or use LoadLibraryEx directly. 46 func LoadDLL(name string) (dll *DLL, err error) { 47 namep, err := UTF16PtrFromString(name) 48 if err != nil { 49 return nil, err 50 } 51 h, e := syscall_loadlibrary(namep) 52 if e != 0 { 53 return nil, &DLLError{ 54 Err: e, 55 ObjName: name, 56 Msg: "Failed to load " + name + ": " + e.Error(), 57 } 58 } 59 d := &DLL{ 60 Name: name, 61 Handle: h, 62 } 63 return d, nil 64 } 65 66 // MustLoadDLL is like LoadDLL but panics if load operation failes. 67 func MustLoadDLL(name string) *DLL { 68 d, e := LoadDLL(name) 69 if e != nil { 70 panic(e) 71 } 72 return d 73 } 74 75 // FindProc searches DLL d for procedure named name and returns *Proc 76 // if found. It returns an error if search fails. 77 func (d *DLL) FindProc(name string) (proc *Proc, err error) { 78 namep, err := BytePtrFromString(name) 79 if err != nil { 80 return nil, err 81 } 82 a, e := syscall_getprocaddress(d.Handle, namep) 83 if e != 0 { 84 return nil, &DLLError{ 85 Err: e, 86 ObjName: name, 87 Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(), 88 } 89 } 90 p := &Proc{ 91 Dll: d, 92 Name: name, 93 addr: a, 94 } 95 return p, nil 96 } 97 98 // MustFindProc is like FindProc but panics if search fails. 99 func (d *DLL) MustFindProc(name string) *Proc { 100 p, e := d.FindProc(name) 101 if e != nil { 102 panic(e) 103 } 104 return p 105 } 106 107 // FindProcByOrdinal searches DLL d for procedure by ordinal and returns *Proc 108 // if found. It returns an error if search fails. 109 func (d *DLL) FindProcByOrdinal(ordinal uintptr) (proc *Proc, err error) { 110 a, e := GetProcAddressByOrdinal(d.Handle, ordinal) 111 name := "#" + itoa(int(ordinal)) 112 if e != nil { 113 return nil, &DLLError{ 114 Err: e, 115 ObjName: name, 116 Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(), 117 } 118 } 119 p := &Proc{ 120 Dll: d, 121 Name: name, 122 addr: a, 123 } 124 return p, nil 125 } 126 127 // MustFindProcByOrdinal is like FindProcByOrdinal but panics if search fails. 128 func (d *DLL) MustFindProcByOrdinal(ordinal uintptr) *Proc { 129 p, e := d.FindProcByOrdinal(ordinal) 130 if e != nil { 131 panic(e) 132 } 133 return p 134 } 135 136 // Release unloads DLL d from memory. 137 func (d *DLL) Release() (err error) { 138 return FreeLibrary(d.Handle) 139 } 140 141 // A Proc implements access to a procedure inside a DLL. 142 type Proc struct { 143 Dll *DLL 144 Name string 145 addr uintptr 146 } 147 148 // Addr returns the address of the procedure represented by p. 149 // The return value can be passed to Syscall to run the procedure. 150 func (p *Proc) Addr() uintptr { 151 return p.addr 152 } 153 154 //go:uintptrescapes 155 156 // Call executes procedure p with arguments a. It will panic, if more than 15 arguments 157 // are supplied. 158 // 159 // The returned error is always non-nil, constructed from the result of GetLastError. 160 // Callers must inspect the primary return value to decide whether an error occurred 161 // (according to the semantics of the specific function being called) before consulting 162 // the error. The error will be guaranteed to contain windows.Errno. 163 func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { 164 switch len(a) { 165 case 0: 166 return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0) 167 case 1: 168 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0) 169 case 2: 170 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0) 171 case 3: 172 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2]) 173 case 4: 174 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0) 175 case 5: 176 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0) 177 case 6: 178 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5]) 179 case 7: 180 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0) 181 case 8: 182 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0) 183 case 9: 184 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]) 185 case 10: 186 return syscall.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) 187 case 11: 188 return syscall.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) 189 case 12: 190 return syscall.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]) 191 case 13: 192 return syscall.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) 193 case 14: 194 return syscall.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) 195 case 15: 196 return syscall.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]) 197 default: 198 panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".") 199 } 200 } 201 202 // A LazyDLL implements access to a single DLL. 203 // It will delay the load of the DLL until the first 204 // call to its Handle method or to one of its 205 // LazyProc's Addr method. 206 type LazyDLL struct { 207 Name string 208 209 // System determines whether the DLL must be loaded from the 210 // Windows System directory, bypassing the normal DLL search 211 // path. 212 System bool 213 214 mu sync.Mutex 215 dll *DLL // non nil once DLL is loaded 216 } 217 218 // Load loads DLL file d.Name into memory. It returns an error if fails. 219 // Load will not try to load DLL, if it is already loaded into memory. 220 func (d *LazyDLL) Load() error { 221 // Non-racy version of: 222 // if d.dll != nil { 223 if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil { 224 return nil 225 } 226 d.mu.Lock() 227 defer d.mu.Unlock() 228 if d.dll != nil { 229 return nil 230 } 231 232 // kernel32.dll is special, since it's where LoadLibraryEx comes from. 233 // The kernel already special-cases its name, so it's always 234 // loaded from system32. 235 var dll *DLL 236 var err error 237 if d.Name == "kernel32.dll" { 238 dll, err = LoadDLL(d.Name) 239 } else { 240 dll, err = loadLibraryEx(d.Name, d.System) 241 } 242 if err != nil { 243 return err 244 } 245 246 // Non-racy version of: 247 // d.dll = dll 248 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll)) 249 return nil 250 } 251 252 // mustLoad is like Load but panics if search fails. 253 func (d *LazyDLL) mustLoad() { 254 e := d.Load() 255 if e != nil { 256 panic(e) 257 } 258 } 259 260 // Handle returns d's module handle. 261 func (d *LazyDLL) Handle() uintptr { 262 d.mustLoad() 263 return uintptr(d.dll.Handle) 264 } 265 266 // NewProc returns a LazyProc for accessing the named procedure in the DLL d. 267 func (d *LazyDLL) NewProc(name string) *LazyProc { 268 return &LazyProc{l: d, Name: name} 269 } 270 271 // NewLazyDLL creates new LazyDLL associated with DLL file. 272 func NewLazyDLL(name string) *LazyDLL { 273 return &LazyDLL{Name: name} 274 } 275 276 // NewLazySystemDLL is like NewLazyDLL, but will only 277 // search Windows System directory for the DLL if name is 278 // a base name (like "advapi32.dll"). 279 func NewLazySystemDLL(name string) *LazyDLL { 280 return &LazyDLL{Name: name, System: true} 281 } 282 283 // A LazyProc implements access to a procedure inside a LazyDLL. 284 // It delays the lookup until the Addr method is called. 285 type LazyProc struct { 286 Name string 287 288 mu sync.Mutex 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 // It will panic if the procedure cannot be found. 330 func (p *LazyProc) Addr() uintptr { 331 p.mustFind() 332 return p.proc.Addr() 333 } 334 335 //go:uintptrescapes 336 337 // Call executes procedure p with arguments a. It will panic, if more than 15 arguments 338 // are supplied. It will also panic if the procedure cannot be found. 339 // 340 // The returned error is always non-nil, constructed from the result of GetLastError. 341 // Callers must inspect the primary return value to decide whether an error occurred 342 // (according to the semantics of the specific function being called) before consulting 343 // the error. The error will be guaranteed to contain windows.Errno. 344 func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { 345 p.mustFind() 346 return p.proc.Call(a...) 347 } 348 349 var canDoSearchSystem32Once struct { 350 sync.Once 351 v bool 352 } 353 354 func initCanDoSearchSystem32() { 355 // https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: 356 // "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows 357 // Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on 358 // systems that have KB2533623 installed. To determine whether the 359 // flags are available, use GetProcAddress to get the address of the 360 // AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories 361 // function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* 362 // flags can be used with LoadLibraryEx." 363 canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil) 364 } 365 366 func canDoSearchSystem32() bool { 367 canDoSearchSystem32Once.Do(initCanDoSearchSystem32) 368 return canDoSearchSystem32Once.v 369 } 370 371 func isBaseName(name string) bool { 372 for _, c := range name { 373 if c == ':' || c == '/' || c == '\\' { 374 return false 375 } 376 } 377 return true 378 } 379 380 // loadLibraryEx wraps the Windows LoadLibraryEx function. 381 // 382 // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx 383 // 384 // If name is not an absolute path, LoadLibraryEx searches for the DLL 385 // in a variety of automatic locations unless constrained by flags. 386 // See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx 387 func loadLibraryEx(name string, system bool) (*DLL, error) { 388 loadDLL := name 389 var flags uintptr 390 if system { 391 if canDoSearchSystem32() { 392 const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 393 flags = LOAD_LIBRARY_SEARCH_SYSTEM32 394 } else if isBaseName(name) { 395 // WindowsXP or unpatched Windows machine 396 // trying to load "foo.dll" out of the system 397 // folder, but LoadLibraryEx doesn't support 398 // that yet on their system, so emulate it. 399 systemdir, err := GetSystemDirectory() 400 if err != nil { 401 return nil, err 402 } 403 loadDLL = systemdir + "\\" + name 404 } 405 } 406 h, err := LoadLibraryEx(loadDLL, 0, flags) 407 if err != nil { 408 return nil, err 409 } 410 return &DLL{Name: name, Handle: h}, nil 411 } 412 413 type errString string 414 415 func (s errString) Error() string { return string(s) }