github.com/Andyfoo/golang/x/sys@v0.0.0-20190901054642-57c1bf301704/windows/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 windows
     6  
     7  import (
     8  	"sync"
     9  	"sync/atomic"
    10  	"syscall"
    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.goc; we provide jumps to them in our assembly file.
    24  func loadlibrary(filename *uint16) (handle uintptr, err syscall.Errno)
    25  func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err syscall.Errno)
    26  
    27  // A DLL implements access to a single DLL.
    28  type DLL struct {
    29  	Name   string
    30  	Handle Handle
    31  }
    32  
    33  // LoadDLL loads DLL file into memory.
    34  //
    35  // Warning: using LoadDLL without an absolute path name is subject to
    36  // DLL preloading attacks. To safely load a system DLL, use LazyDLL
    37  // with System set to true, or use LoadLibraryEx directly.
    38  func LoadDLL(name string) (dll *DLL, err error) {
    39  	namep, err := UTF16PtrFromString(name)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	h, e := loadlibrary(namep)
    44  	if e != 0 {
    45  		return nil, &DLLError{
    46  			Err:     e,
    47  			ObjName: name,
    48  			Msg:     "Failed to load " + name + ": " + e.Error(),
    49  		}
    50  	}
    51  	d := &DLL{
    52  		Name:   name,
    53  		Handle: Handle(h),
    54  	}
    55  	return d, nil
    56  }
    57  
    58  // MustLoadDLL is like LoadDLL but panics if load operation failes.
    59  func MustLoadDLL(name string) *DLL {
    60  	d, e := LoadDLL(name)
    61  	if e != nil {
    62  		panic(e)
    63  	}
    64  	return d
    65  }
    66  
    67  // FindProc searches DLL d for procedure named name and returns *Proc
    68  // if found. It returns an error if search fails.
    69  func (d *DLL) FindProc(name string) (proc *Proc, err error) {
    70  	namep, err := BytePtrFromString(name)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	a, e := getprocaddress(uintptr(d.Handle), namep)
    75  	if e != 0 {
    76  		return nil, &DLLError{
    77  			Err:     e,
    78  			ObjName: name,
    79  			Msg:     "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
    80  		}
    81  	}
    82  	p := &Proc{
    83  		Dll:  d,
    84  		Name: name,
    85  		addr: a,
    86  	}
    87  	return p, nil
    88  }
    89  
    90  // MustFindProc is like FindProc but panics if search fails.
    91  func (d *DLL) MustFindProc(name string) *Proc {
    92  	p, e := d.FindProc(name)
    93  	if e != nil {
    94  		panic(e)
    95  	}
    96  	return p
    97  }
    98  
    99  // Release unloads DLL d from memory.
   100  func (d *DLL) Release() (err error) {
   101  	return FreeLibrary(d.Handle)
   102  }
   103  
   104  // A Proc implements access to a procedure inside a DLL.
   105  type Proc struct {
   106  	Dll  *DLL
   107  	Name string
   108  	addr uintptr
   109  }
   110  
   111  // Addr returns the address of the procedure represented by p.
   112  // The return value can be passed to Syscall to run the procedure.
   113  func (p *Proc) Addr() uintptr {
   114  	return p.addr
   115  }
   116  
   117  //go:uintptrescapes
   118  
   119  // Call executes procedure p with arguments a. It will panic, if more than 15 arguments
   120  // are supplied.
   121  //
   122  // The returned error is always non-nil, constructed from the result of GetLastError.
   123  // Callers must inspect the primary return value to decide whether an error occurred
   124  // (according to the semantics of the specific function being called) before consulting
   125  // the error. The error will be guaranteed to contain windows.Errno.
   126  func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
   127  	switch len(a) {
   128  	case 0:
   129  		return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
   130  	case 1:
   131  		return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
   132  	case 2:
   133  		return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
   134  	case 3:
   135  		return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
   136  	case 4:
   137  		return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
   138  	case 5:
   139  		return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
   140  	case 6:
   141  		return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
   142  	case 7:
   143  		return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
   144  	case 8:
   145  		return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
   146  	case 9:
   147  		return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
   148  	case 10:
   149  		return syscall.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)
   150  	case 11:
   151  		return syscall.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)
   152  	case 12:
   153  		return syscall.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])
   154  	case 13:
   155  		return syscall.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)
   156  	case 14:
   157  		return syscall.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)
   158  	case 15:
   159  		return syscall.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])
   160  	default:
   161  		panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
   162  	}
   163  }
   164  
   165  // A LazyDLL implements access to a single DLL.
   166  // It will delay the load of the DLL until the first
   167  // call to its Handle method or to one of its
   168  // LazyProc's Addr method.
   169  type LazyDLL struct {
   170  	Name string
   171  
   172  	// System determines whether the DLL must be loaded from the
   173  	// Windows System directory, bypassing the normal DLL search
   174  	// path.
   175  	System bool
   176  
   177  	mu  sync.Mutex
   178  	dll *DLL // non nil once DLL is loaded
   179  }
   180  
   181  // Load loads DLL file d.Name into memory. It returns an error if fails.
   182  // Load will not try to load DLL, if it is already loaded into memory.
   183  func (d *LazyDLL) Load() error {
   184  	// Non-racy version of:
   185  	// if d.dll != nil {
   186  	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil {
   187  		return nil
   188  	}
   189  	d.mu.Lock()
   190  	defer d.mu.Unlock()
   191  	if d.dll != nil {
   192  		return nil
   193  	}
   194  
   195  	// kernel32.dll is special, since it's where LoadLibraryEx comes from.
   196  	// The kernel already special-cases its name, so it's always
   197  	// loaded from system32.
   198  	var dll *DLL
   199  	var err error
   200  	if d.Name == "kernel32.dll" {
   201  		dll, err = LoadDLL(d.Name)
   202  	} else {
   203  		dll, err = loadLibraryEx(d.Name, d.System)
   204  	}
   205  	if err != nil {
   206  		return err
   207  	}
   208  
   209  	// Non-racy version of:
   210  	// d.dll = dll
   211  	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
   212  	return nil
   213  }
   214  
   215  // mustLoad is like Load but panics if search fails.
   216  func (d *LazyDLL) mustLoad() {
   217  	e := d.Load()
   218  	if e != nil {
   219  		panic(e)
   220  	}
   221  }
   222  
   223  // Handle returns d's module handle.
   224  func (d *LazyDLL) Handle() uintptr {
   225  	d.mustLoad()
   226  	return uintptr(d.dll.Handle)
   227  }
   228  
   229  // NewProc returns a LazyProc for accessing the named procedure in the DLL d.
   230  func (d *LazyDLL) NewProc(name string) *LazyProc {
   231  	return &LazyProc{l: d, Name: name}
   232  }
   233  
   234  // NewLazyDLL creates new LazyDLL associated with DLL file.
   235  func NewLazyDLL(name string) *LazyDLL {
   236  	return &LazyDLL{Name: name}
   237  }
   238  
   239  // NewLazySystemDLL is like NewLazyDLL, but will only
   240  // search Windows System directory for the DLL if name is
   241  // a base name (like "advapi32.dll").
   242  func NewLazySystemDLL(name string) *LazyDLL {
   243  	return &LazyDLL{Name: name, System: true}
   244  }
   245  
   246  // A LazyProc implements access to a procedure inside a LazyDLL.
   247  // It delays the lookup until the Addr method is called.
   248  type LazyProc struct {
   249  	Name string
   250  
   251  	mu   sync.Mutex
   252  	l    *LazyDLL
   253  	proc *Proc
   254  }
   255  
   256  // Find searches DLL for procedure named p.Name. It returns
   257  // an error if search fails. Find will not search procedure,
   258  // if it is already found and loaded into memory.
   259  func (p *LazyProc) Find() error {
   260  	// Non-racy version of:
   261  	// if p.proc == nil {
   262  	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
   263  		p.mu.Lock()
   264  		defer p.mu.Unlock()
   265  		if p.proc == nil {
   266  			e := p.l.Load()
   267  			if e != nil {
   268  				return e
   269  			}
   270  			proc, e := p.l.dll.FindProc(p.Name)
   271  			if e != nil {
   272  				return e
   273  			}
   274  			// Non-racy version of:
   275  			// p.proc = proc
   276  			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
   277  		}
   278  	}
   279  	return nil
   280  }
   281  
   282  // mustFind is like Find but panics if search fails.
   283  func (p *LazyProc) mustFind() {
   284  	e := p.Find()
   285  	if e != nil {
   286  		panic(e)
   287  	}
   288  }
   289  
   290  // Addr returns the address of the procedure represented by p.
   291  // The return value can be passed to Syscall to run the procedure.
   292  // It will panic if the procedure cannot be found.
   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. It will also panic if the procedure cannot be found.
   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 windows.Errno.
   307  func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
   308  	p.mustFind()
   309  	return p.proc.Call(a...)
   310  }
   311  
   312  var canDoSearchSystem32Once struct {
   313  	sync.Once
   314  	v bool
   315  }
   316  
   317  func initCanDoSearchSystem32() {
   318  	// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
   319  	// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
   320  	// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
   321  	// systems that have KB2533623 installed. To determine whether the
   322  	// flags are available, use GetProcAddress to get the address of the
   323  	// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
   324  	// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
   325  	// flags can be used with LoadLibraryEx."
   326  	canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil)
   327  }
   328  
   329  func canDoSearchSystem32() bool {
   330  	canDoSearchSystem32Once.Do(initCanDoSearchSystem32)
   331  	return canDoSearchSystem32Once.v
   332  }
   333  
   334  func isBaseName(name string) bool {
   335  	for _, c := range name {
   336  		if c == ':' || c == '/' || c == '\\' {
   337  			return false
   338  		}
   339  	}
   340  	return true
   341  }
   342  
   343  // loadLibraryEx wraps the Windows LoadLibraryEx function.
   344  //
   345  // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
   346  //
   347  // If name is not an absolute path, LoadLibraryEx searches for the DLL
   348  // in a variety of automatic locations unless constrained by flags.
   349  // See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx
   350  func loadLibraryEx(name string, system bool) (*DLL, error) {
   351  	loadDLL := name
   352  	var flags uintptr
   353  	if system {
   354  		if canDoSearchSystem32() {
   355  			const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
   356  			flags = LOAD_LIBRARY_SEARCH_SYSTEM32
   357  		} else if isBaseName(name) {
   358  			// WindowsXP or unpatched Windows machine
   359  			// trying to load "foo.dll" out of the system
   360  			// folder, but LoadLibraryEx doesn't support
   361  			// that yet on their system, so emulate it.
   362  			systemdir, err := GetSystemDirectory()
   363  			if err != nil {
   364  				return nil, err
   365  			}
   366  			loadDLL = systemdir + "\\" + name
   367  		}
   368  	}
   369  	h, err := LoadLibraryEx(loadDLL, 0, flags)
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  	return &DLL{Name: name, Handle: h}, nil
   374  }
   375  
   376  type errString string
   377  
   378  func (s errString) Error() string { return string(s) }