github.com/s1s1ty/go@v0.0.0-20180207192209-104445e3140f/src/syscall/dll_windows.go (about)

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