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

View as plain text