// 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" ) // Use double underscore to avoid name collision autogenerated functions. //go:cgo_import_dynamic syscall.__LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" //go:cgo_import_dynamic syscall.__GetProcAddress GetProcAddress%2 "kernel32.dll" var ( __LoadLibraryExW unsafe.Pointer __GetProcAddress unsafe.Pointer ) // 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 } // N.B. For the Syscall functions below: // // //go:uintptrkeepalive because the uintptr argument may be converted pointers // that need to be kept alive in the caller. // // //go:nosplit because stack copying does not account for uintptrkeepalive, so // the stack must not grow. Stack copying cannot blindly assume that all // uintptr arguments are pointers, because some values may look like pointers, // but not really be pointers, and adjusting their value would break the call. // Deprecated: Use [SyscallN] instead. // //go:nosplit //go:uintptrkeepalive func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { return syscalln(trap, nargs, a1, a2, a3) } // Deprecated: Use [SyscallN] instead. // //go:nosplit //go:uintptrkeepalive func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6) } // Deprecated: Use [SyscallN] instead. // //go:nosplit //go:uintptrkeepalive func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) { return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9) } // Deprecated: Use [SyscallN] instead. // //go:nosplit //go:uintptrkeepalive func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno) { return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) } // Deprecated: Use [SyscallN] instead. // //go:nosplit //go:uintptrkeepalive 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) { return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) } // Deprecated: Use [SyscallN] instead. // //go:nosplit //go:uintptrkeepalive 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) { return syscalln(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) } // SyscallN executes procedure p with arguments args. // // See [Proc.Call] for more information. // //go:nosplit //go:uintptrkeepalive func SyscallN(p uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) { return syscalln(p, uintptr(len(args)), args...) } // syscalln is implemented in runtime/syscall_windows.go. // //go:noescape func syscalln(fn, n uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) // N.B. For the loadlibrary, loadlibrary, and getprocaddress functions below: // // //go:linkname to act as an allowlist for linker's -checklinkname, as // golang.org/x/sys/windows linknames these functions. //go:linkname loadlibrary func loadlibrary(filename *uint16) (uintptr, Errno) { handle, _, err := SyscallN(uintptr(__LoadLibraryExW), uintptr(unsafe.Pointer(filename)), 0, 0) if handle != 0 { err = 0 } return handle, err } //go:linkname loadsystemlibrary func loadsystemlibrary(filename *uint16) (uintptr, Errno) { const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 handle, _, err := SyscallN(uintptr(__LoadLibraryExW), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) if handle != 0 { err = 0 } return handle, err } //go:linkname getprocaddress func getprocaddress(handle uintptr, procname *uint8) (uintptr, Errno) { proc, _, err := SyscallN(uintptr(__GetProcAddress), handle, uintptr(unsafe.Pointer(procname))) if proc != 0 { err = 0 } return proc, err } // 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...) }