Source file src/syscall/dll_windows.go

     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  // Use double underscore to avoid name collision autogenerated functions.
    15  //go:cgo_import_dynamic syscall.__LoadLibraryExW LoadLibraryExW%3 "kernel32.dll"
    16  //go:cgo_import_dynamic syscall.__GetProcAddress GetProcAddress%2 "kernel32.dll"
    17  
    18  var (
    19  	__LoadLibraryExW unsafe.Pointer
    20  	__GetProcAddress unsafe.Pointer
    21  )
    22  
    23  // DLLError describes reasons for DLL load failures.
    24  type DLLError struct {
    25  	Err     error
    26  	ObjName string
    27  	Msg     string
    28  }
    29  
    30  func (e *DLLError) Error() string { return e.Msg }
    31  
    32  func (e *DLLError) Unwrap() error { return e.Err }
    33  
    34  // N.B. For the Syscall functions below:
    35  //
    36  // //go:uintptrkeepalive because the uintptr argument may be converted pointers
    37  // that need to be kept alive in the caller.
    38  //
    39  // //go:nosplit because stack copying does not account for uintptrkeepalive, so
    40  // the stack must not grow. Stack copying cannot blindly assume that all
    41  // uintptr arguments are pointers, because some values may look like pointers,
    42  // but not really be pointers, and adjusting their value would break the call.
    43  
    44  // Deprecated: Use [SyscallN] instead.
    45  //
    46  //go:nosplit
    47  //go:uintptrkeepalive
    48  func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
    49  	return syscalln(trap, nargs, a1, a2, a3)
    50  }
    51  
    52  // Deprecated: Use [SyscallN] instead.
    53  //
    54  //go:nosplit
    55  //go:uintptrkeepalive
    56  func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
    57  	return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6)
    58  }
    59  
    60  // Deprecated: Use [SyscallN] instead.
    61  //
    62  //go:nosplit
    63  //go:uintptrkeepalive
    64  func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) {
    65  	return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9)
    66  }
    67  
    68  // Deprecated: Use [SyscallN] instead.
    69  //
    70  //go:nosplit
    71  //go:uintptrkeepalive
    72  func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno) {
    73  	return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)
    74  }
    75  
    76  // Deprecated: Use [SyscallN] instead.
    77  //
    78  //go:nosplit
    79  //go:uintptrkeepalive
    80  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) {
    81  	return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
    82  }
    83  
    84  // Deprecated: Use [SyscallN] instead.
    85  //
    86  //go:nosplit
    87  //go:uintptrkeepalive
    88  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) {
    89  	return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18)
    90  }
    91  
    92  // SyscallN executes procedure p with arguments args.
    93  //
    94  // See [Proc.Call] for more information.
    95  //
    96  //go:nosplit
    97  //go:uintptrkeepalive
    98  func SyscallN(p uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) {
    99  	return syscalln(p, uintptr(len(args)), args...)
   100  }
   101  
   102  // syscalln is implemented in runtime/syscall_windows.go.
   103  //
   104  //go:noescape
   105  func syscalln(fn, n uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
   106  
   107  // N.B. For the loadlibrary, loadlibrary, and getprocaddress functions below:
   108  //
   109  // //go:linkname to act as an allowlist for linker's -checklinkname, as
   110  // golang.org/x/sys/windows linknames these functions.
   111  
   112  //go:linkname loadlibrary
   113  func loadlibrary(filename *uint16) (uintptr, Errno) {
   114  	handle, _, err := SyscallN(uintptr(__LoadLibraryExW), uintptr(unsafe.Pointer(filename)), 0, 0)
   115  	if handle != 0 {
   116  		err = 0
   117  	}
   118  	return handle, err
   119  }
   120  
   121  //go:linkname loadsystemlibrary
   122  func loadsystemlibrary(filename *uint16) (uintptr, Errno) {
   123  	const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
   124  	handle, _, err := SyscallN(uintptr(__LoadLibraryExW), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)
   125  	if handle != 0 {
   126  		err = 0
   127  	}
   128  	return handle, err
   129  }
   130  
   131  //go:linkname getprocaddress
   132  func getprocaddress(handle uintptr, procname *uint8) (uintptr, Errno) {
   133  	proc, _, err := SyscallN(uintptr(__GetProcAddress), handle, uintptr(unsafe.Pointer(procname)))
   134  	if proc != 0 {
   135  		err = 0
   136  	}
   137  	return proc, err
   138  }
   139  
   140  // A DLL implements access to a single DLL.
   141  type DLL struct {
   142  	Name   string
   143  	Handle Handle
   144  }
   145  
   146  // LoadDLL loads the named DLL file into memory.
   147  //
   148  // If name is not an absolute path and is not a known system DLL used by
   149  // Go, Windows will search for the named DLL in many locations, causing
   150  // potential DLL preloading attacks.
   151  //
   152  // Use [LazyDLL] in golang.org/x/sys/windows for a secure way to
   153  // load system DLLs.
   154  func LoadDLL(name string) (*DLL, error) {
   155  	namep, err := UTF16PtrFromString(name)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	var h uintptr
   160  	var e Errno
   161  	if sysdll.IsSystemDLL[name] {
   162  		h, e = loadsystemlibrary(namep)
   163  	} else {
   164  		h, e = loadlibrary(namep)
   165  	}
   166  	if e != 0 {
   167  		return nil, &DLLError{
   168  			Err:     e,
   169  			ObjName: name,
   170  			Msg:     "Failed to load " + name + ": " + e.Error(),
   171  		}
   172  	}
   173  	d := &DLL{
   174  		Name:   name,
   175  		Handle: Handle(h),
   176  	}
   177  	return d, nil
   178  }
   179  
   180  // MustLoadDLL is like [LoadDLL] but panics if load operation fails.
   181  func MustLoadDLL(name string) *DLL {
   182  	d, e := LoadDLL(name)
   183  	if e != nil {
   184  		panic(e)
   185  	}
   186  	return d
   187  }
   188  
   189  // FindProc searches [DLL] d for procedure named name and returns [*Proc]
   190  // if found. It returns an error if search fails.
   191  func (d *DLL) FindProc(name string) (proc *Proc, err error) {
   192  	namep, err := BytePtrFromString(name)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  	a, e := getprocaddress(uintptr(d.Handle), namep)
   197  	if e != 0 {
   198  		return nil, &DLLError{
   199  			Err:     e,
   200  			ObjName: name,
   201  			Msg:     "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
   202  		}
   203  	}
   204  	p := &Proc{
   205  		Dll:  d,
   206  		Name: name,
   207  		addr: a,
   208  	}
   209  	return p, nil
   210  }
   211  
   212  // MustFindProc is like [DLL.FindProc] but panics if search fails.
   213  func (d *DLL) MustFindProc(name string) *Proc {
   214  	p, e := d.FindProc(name)
   215  	if e != nil {
   216  		panic(e)
   217  	}
   218  	return p
   219  }
   220  
   221  // Release unloads [DLL] d from memory.
   222  func (d *DLL) Release() (err error) {
   223  	return FreeLibrary(d.Handle)
   224  }
   225  
   226  // A Proc implements access to a procedure inside a [DLL].
   227  type Proc struct {
   228  	Dll  *DLL
   229  	Name string
   230  	addr uintptr
   231  }
   232  
   233  // Addr returns the address of the procedure represented by p.
   234  // The return value can be passed to Syscall to run the procedure.
   235  func (p *Proc) Addr() uintptr {
   236  	return p.addr
   237  }
   238  
   239  // Call executes procedure p with arguments a.
   240  //
   241  // The returned error is always non-nil, constructed from the result of GetLastError.
   242  // Callers must inspect the primary return value to decide whether an error occurred
   243  // (according to the semantics of the specific function being called) before consulting
   244  // the error. The error always has type [Errno].
   245  //
   246  // On amd64, Call can pass and return floating-point values. To pass
   247  // an argument x with C type "float", use
   248  // uintptr(math.Float32bits(x)). To pass an argument with C type
   249  // "double", use uintptr(math.Float64bits(x)). Floating-point return
   250  // values are returned in r2. The return value for C type "float" is
   251  // [math.Float32frombits](uint32(r2)). For C type "double", it is
   252  // [math.Float64frombits](uint64(r2)).
   253  //
   254  //go:uintptrescapes
   255  func (p *Proc) Call(a ...uintptr) (uintptr, uintptr, error) {
   256  	return SyscallN(p.Addr(), a...)
   257  }
   258  
   259  // A LazyDLL implements access to a single [DLL].
   260  // It will delay the load of the DLL until the first
   261  // call to its [LazyDLL.Handle] method or to one of its
   262  // [LazyProc]'s Addr method.
   263  //
   264  // LazyDLL is subject to the same DLL preloading attacks as documented
   265  // on [LoadDLL].
   266  //
   267  // Use LazyDLL in golang.org/x/sys/windows for a secure way to
   268  // load system DLLs.
   269  type LazyDLL struct {
   270  	mu   sync.Mutex
   271  	dll  *DLL // non nil once DLL is loaded
   272  	Name string
   273  }
   274  
   275  // Load loads DLL file d.Name into memory. It returns an error if fails.
   276  // Load will not try to load DLL, if it is already loaded into memory.
   277  func (d *LazyDLL) Load() error {
   278  	// Non-racy version of:
   279  	// if d.dll == nil {
   280  	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil {
   281  		d.mu.Lock()
   282  		defer d.mu.Unlock()
   283  		if d.dll == nil {
   284  			dll, e := LoadDLL(d.Name)
   285  			if e != nil {
   286  				return e
   287  			}
   288  			// Non-racy version of:
   289  			// d.dll = dll
   290  			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
   291  		}
   292  	}
   293  	return nil
   294  }
   295  
   296  // mustLoad is like Load but panics if search fails.
   297  func (d *LazyDLL) mustLoad() {
   298  	e := d.Load()
   299  	if e != nil {
   300  		panic(e)
   301  	}
   302  }
   303  
   304  // Handle returns d's module handle.
   305  func (d *LazyDLL) Handle() uintptr {
   306  	d.mustLoad()
   307  	return uintptr(d.dll.Handle)
   308  }
   309  
   310  // NewProc returns a [LazyProc] for accessing the named procedure in the [DLL] d.
   311  func (d *LazyDLL) NewProc(name string) *LazyProc {
   312  	return &LazyProc{l: d, Name: name}
   313  }
   314  
   315  // NewLazyDLL creates new [LazyDLL] associated with [DLL] file.
   316  func NewLazyDLL(name string) *LazyDLL {
   317  	return &LazyDLL{Name: name}
   318  }
   319  
   320  // A LazyProc implements access to a procedure inside a [LazyDLL].
   321  // It delays the lookup until the [LazyProc.Addr], [LazyProc.Call], or [LazyProc.Find] method is called.
   322  type LazyProc struct {
   323  	mu   sync.Mutex
   324  	Name string
   325  	l    *LazyDLL
   326  	proc *Proc
   327  }
   328  
   329  // Find searches [DLL] for procedure named p.Name. It returns
   330  // an error if search fails. Find will not search procedure,
   331  // if it is already found and loaded into memory.
   332  func (p *LazyProc) Find() error {
   333  	// Non-racy version of:
   334  	// if p.proc == nil {
   335  	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
   336  		p.mu.Lock()
   337  		defer p.mu.Unlock()
   338  		if p.proc == nil {
   339  			e := p.l.Load()
   340  			if e != nil {
   341  				return e
   342  			}
   343  			proc, e := p.l.dll.FindProc(p.Name)
   344  			if e != nil {
   345  				return e
   346  			}
   347  			// Non-racy version of:
   348  			// p.proc = proc
   349  			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
   350  		}
   351  	}
   352  	return nil
   353  }
   354  
   355  // mustFind is like Find but panics if search fails.
   356  func (p *LazyProc) mustFind() {
   357  	e := p.Find()
   358  	if e != nil {
   359  		panic(e)
   360  	}
   361  }
   362  
   363  // Addr returns the address of the procedure represented by p.
   364  // The return value can be passed to Syscall to run the procedure.
   365  func (p *LazyProc) Addr() uintptr {
   366  	p.mustFind()
   367  	return p.proc.Addr()
   368  }
   369  
   370  // Call executes procedure p with arguments a. See the documentation of
   371  // Proc.Call for more information.
   372  //
   373  //go:uintptrescapes
   374  func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
   375  	p.mustFind()
   376  	return p.proc.Call(a...)
   377  }
   378  

View as plain text