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