dll_windows.mx raw

   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  //go:noescape
  46  func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
  47  func loadlibrary(filename *uint16) (handle uintptr, err Errno)
  48  func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)
  49  func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
  50  
  51  // A DLL implements access to a single DLL.
  52  type 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.
  65  func 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.
  92  func 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.
 102  func (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.
 124  func (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.
 133  func (d *DLL) Release() (err error) {
 134  	return FreeLibrary(d.Handle)
 135  }
 136  
 137  // A Proc implements access to a procedure inside a [DLL].
 138  type 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.
 146  func (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
 166  func (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.
 180  type 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.
 188  func (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.
 208  func (d *LazyDLL) mustLoad() {
 209  	e := d.Load()
 210  	if e != nil {
 211  		panic(e)
 212  	}
 213  }
 214  
 215  // Handle returns d's module handle.
 216  func (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.
 222  func (d *LazyDLL) NewProc(name string) *LazyProc {
 223  	return &LazyProc{l: d, Name: name}
 224  }
 225  
 226  // NewLazyDLL creates new [LazyDLL] associated with [DLL] file.
 227  func 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.
 233  type 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.
 243  func (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.
 267  func (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.
 276  func (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
 285  func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
 286  	p.mustFind()
 287  	return p.proc.Call(a...)
 288  }
 289