...
Run Format

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

View as plain text