...

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	// 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) }
   379	

View as plain text