Black Lives Matter. Support the Equal Justice Initiative.

Source file src/syscall/dll_windows.go

Documentation: syscall

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

View as plain text