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