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