Source file src/cmd/vendor/golang.org/x/sys/windows/dll_windows.go

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

View as plain text