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

View as plain text