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  // 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  

View as plain text