github.com/iDigitalFlame/xmt@v0.5.4/device/winapi/loader.go (about) 1 //go:build windows 2 // +build windows 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 package winapi 21 22 import ( 23 "sync" 24 "syscall" 25 26 // Needed to use "linkname" 27 _ "unsafe" 28 29 "github.com/iDigitalFlame/xmt/util/xerr" 30 ) 31 32 const errPending = syscall.Errno(997) 33 34 var searchSystem32 struct { 35 _ [0]func() 36 sync.Once 37 v bool 38 } 39 40 //go:linkname systemDirectoryPrefix syscall.systemDirectoryPrefix 41 var systemDirectoryPrefix string 42 43 func isBaseName(s string) bool { 44 for i := range s { 45 switch s[i] { 46 case ':', '/', '\\': 47 return false 48 } 49 } 50 return true 51 } 52 func byteSlicePtr(s string) *byte { 53 a := make([]byte, len(s)+1) 54 copy(a, s) 55 return &a[0] 56 } 57 func (p *lazyProc) address() uintptr { 58 if p.addr > 0 { 59 // NOTE(dij): Might be racy, but will catch most of the re-used calls 60 // that are already populated without an additional alloc and 61 // call to "find". 62 return p.addr 63 } 64 if err := p.find(); err != nil { 65 if !canPanic { 66 syscall.Exit(2) 67 return 0 68 } 69 panic(err.Error()) 70 } 71 return p.addr 72 } 73 func unboxError(e syscall.Errno) error { 74 switch e { 75 case 0: 76 return syscall.EINVAL 77 case 997: 78 return errPending 79 } 80 return e 81 } 82 83 // LoadDLL loads DLL file into memory. 84 // 85 // This function will attempt to load non-absolute paths from the system 86 // dependent DLL directory (usually system32). 87 func LoadDLL(s string) (uintptr, error) { 88 return loadLibraryEx(s) 89 } 90 func loadDLL(s string) (uintptr, error) { 91 n, err := UTF16PtrFromString(s) 92 if err != nil { 93 return 0, err 94 } 95 h, err2 := syscallLoadLibrary(n) 96 if err2 != 0 { 97 if xerr.ExtendedInfo { 98 return 0, xerr.Wrap(`cannot load DLL "`+s+`"`, err) 99 } 100 return 0, xerr.Wrap("cannot load DLL", err) 101 } 102 return h, nil 103 } 104 func loadLibraryEx(s string) (uintptr, error) { 105 var ( 106 n = s 107 f uintptr 108 ) 109 if doSearchSystem32() { 110 // 0x800 - LOAD_LIBRARY_SEARCH_SYSTEM32 111 f = 0x800 112 } else if isBaseName(s) { 113 n = systemDirectoryPrefix + s 114 } 115 return LoadLibraryEx(n, f) 116 } 117 func findProc(h uintptr, s, n string) (uintptr, error) { 118 h, err := syscallGetProcAddress(h, byteSlicePtr(s)) 119 if err != 0 { 120 if xerr.ExtendedInfo { 121 return 0, xerr.Wrap(`cannot load DLL "`+n+`" function "`+s+`"`, err) 122 } 123 return 0, xerr.Wrap("cannot load DLL function", err) 124 } 125 return h, nil 126 } 127 128 //go:linkname syscallLoadLibrary syscall.loadlibrary 129 func syscallLoadLibrary(n *uint16) (uintptr, syscall.Errno) 130 131 //go:linkname syscallGetProcAddress syscall.getprocaddress 132 func syscallGetProcAddress(h uintptr, n *uint8) (uintptr, syscall.Errno)