blob: a7873e6ad8c93ef200979aa001c2cf1b3ebe8616 [file] [log] [blame] [edit]
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package syscall
import (
"internal/syscall/windows/sysdll"
"sync"
"sync/atomic"
"unsafe"
)
// DLLError describes reasons for DLL load failures.
type DLLError struct {
Err error
ObjName string
Msg string
}
func (e *DLLError) Error() string { return e.Msg }
func (e *DLLError) Unwrap() error { return e.Err }
// Implemented in ../runtime/syscall_windows.go.
// Deprecated: Use [SyscallN] instead.
func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
// Deprecated: Use [SyscallN] instead.
func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
// Deprecated: Use [SyscallN] instead.
func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
// Deprecated: Use [SyscallN] instead.
func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno)
// Deprecated: Use [SyscallN] instead.
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)
// Deprecated: Use [SyscallN] instead.
func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno)
//go:noescape
func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
func loadlibrary(filename *uint16) (handle uintptr, err Errno)
func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
// A DLL implements access to a single DLL.
type DLL struct {
Name string
Handle Handle
}
// LoadDLL loads the named DLL file into memory.
//
// If name is not an absolute path and is not a known system DLL used by
// Go, Windows will search for the named DLL in many locations, causing
// potential DLL preloading attacks.
//
// Use [LazyDLL] in golang.org/x/sys/windows for a secure way to
// load system DLLs.
func LoadDLL(name string) (*DLL, error) {
namep, err := UTF16PtrFromString(name)
if err != nil {
return nil, err
}
var h uintptr
var e Errno
if sysdll.IsSystemDLL[name] {
h, e = loadsystemlibrary(namep)
} else {
h, e = loadlibrary(namep)
}
if e != 0 {
return nil, &DLLError{
Err: e,
ObjName: name,
Msg: "Failed to load " + name + ": " + e.Error(),
}
}
d := &DLL{
Name: name,
Handle: Handle(h),
}
return d, nil
}
// MustLoadDLL is like [LoadDLL] but panics if load operation fails.
func MustLoadDLL(name string) *DLL {
d, e := LoadDLL(name)
if e != nil {
panic(e)
}
return d
}
// FindProc searches [DLL] d for procedure named name and returns [*Proc]
// if found. It returns an error if search fails.
func (d *DLL) FindProc(name string) (proc *Proc, err error) {
namep, err := BytePtrFromString(name)
if err != nil {
return nil, err
}
a, e := getprocaddress(uintptr(d.Handle), namep)
if e != 0 {
return nil, &DLLError{
Err: e,
ObjName: name,
Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
}
}
p := &Proc{
Dll: d,
Name: name,
addr: a,
}
return p, nil
}
// MustFindProc is like [DLL.FindProc] but panics if search fails.
func (d *DLL) MustFindProc(name string) *Proc {
p, e := d.FindProc(name)
if e != nil {
panic(e)
}
return p
}
// Release unloads [DLL] d from memory.
func (d *DLL) Release() (err error) {
return FreeLibrary(d.Handle)
}
// A Proc implements access to a procedure inside a [DLL].
type Proc struct {
Dll *DLL
Name string
addr uintptr
}
// Addr returns the address of the procedure represented by p.
// The return value can be passed to Syscall to run the procedure.
func (p *Proc) Addr() uintptr {
return p.addr
}
// Call executes procedure p with arguments a.
//
// The returned error is always non-nil, constructed from the result of GetLastError.
// Callers must inspect the primary return value to decide whether an error occurred
// (according to the semantics of the specific function being called) before consulting
// the error. The error always has type [Errno].
//
// On amd64, Call can pass and return floating-point values. To pass
// an argument x with C type "float", use
// uintptr(math.Float32bits(x)). To pass an argument with C type
// "double", use uintptr(math.Float64bits(x)). Floating-point return
// values are returned in r2. The return value for C type "float" is
// [math.Float32frombits](uint32(r2)). For C type "double", it is
// [math.Float64frombits](uint64(r2)).
//
//go:uintptrescapes
func (p *Proc) Call(a ...uintptr) (uintptr, uintptr, error) {
return SyscallN(p.Addr(), a...)
}
// A LazyDLL implements access to a single [DLL].
// It will delay the load of the DLL until the first
// call to its [LazyDLL.Handle] method or to one of its
// [LazyProc]'s Addr method.
//
// LazyDLL is subject to the same DLL preloading attacks as documented
// on [LoadDLL].
//
// Use LazyDLL in golang.org/x/sys/windows for a secure way to
// load system DLLs.
type LazyDLL struct {
mu sync.Mutex
dll *DLL // non nil once DLL is loaded
Name string
}
// Load loads DLL file d.Name into memory. It returns an error if fails.
// Load will not try to load DLL, if it is already loaded into memory.
func (d *LazyDLL) Load() error {
// Non-racy version of:
// if d.dll == nil {
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil {
d.mu.Lock()
defer d.mu.Unlock()
if d.dll == nil {
dll, e := LoadDLL(d.Name)
if e != nil {
return e
}
// Non-racy version of:
// d.dll = dll
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
}
}
return nil
}
// mustLoad is like Load but panics if search fails.
func (d *LazyDLL) mustLoad() {
e := d.Load()
if e != nil {
panic(e)
}
}
// Handle returns d's module handle.
func (d *LazyDLL) Handle() uintptr {
d.mustLoad()
return uintptr(d.dll.Handle)
}
// NewProc returns a [LazyProc] for accessing the named procedure in the [DLL] d.
func (d *LazyDLL) NewProc(name string) *LazyProc {
return &LazyProc{l: d, Name: name}
}
// NewLazyDLL creates new [LazyDLL] associated with [DLL] file.
func NewLazyDLL(name string) *LazyDLL {
return &LazyDLL{Name: name}
}
// A LazyProc implements access to a procedure inside a [LazyDLL].
// It delays the lookup until the [LazyProc.Addr], [LazyProc.Call], or [LazyProc.Find] method is called.
type LazyProc struct {
mu sync.Mutex
Name string
l *LazyDLL
proc *Proc
}
// Find searches [DLL] for procedure named p.Name. It returns
// an error if search fails. Find will not search procedure,
// if it is already found and loaded into memory.
func (p *LazyProc) Find() error {
// Non-racy version of:
// if p.proc == nil {
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
p.mu.Lock()
defer p.mu.Unlock()
if p.proc == nil {
e := p.l.Load()
if e != nil {
return e
}
proc, e := p.l.dll.FindProc(p.Name)
if e != nil {
return e
}
// Non-racy version of:
// p.proc = proc
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
}
}
return nil
}
// mustFind is like Find but panics if search fails.
func (p *LazyProc) mustFind() {
e := p.Find()
if e != nil {
panic(e)
}
}
// Addr returns the address of the procedure represented by p.
// The return value can be passed to Syscall to run the procedure.
func (p *LazyProc) Addr() uintptr {
p.mustFind()
return p.proc.Addr()
}
// Call executes procedure p with arguments a. See the documentation of
// Proc.Call for more information.
//
//go:uintptrescapes
func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
p.mustFind()
return p.proc.Call(a...)
}