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

View as plain text