dll_windows.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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. package windows
  5. import (
  6. "sync"
  7. "sync/atomic"
  8. "syscall"
  9. "unsafe"
  10. )
  11. // DLLError describes reasons for DLL load failures.
  12. type DLLError struct {
  13. Err error
  14. ObjName string
  15. Msg string
  16. }
  17. func (e *DLLError) Error() string { return e.Msg }
  18. // Implemented in runtime/syscall_windows.goc; we provide jumps to them in our assembly file.
  19. func loadlibrary(filename *uint16) (handle uintptr, err syscall.Errno)
  20. func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err syscall.Errno)
  21. // A DLL implements access to a single DLL.
  22. type DLL struct {
  23. Name string
  24. Handle Handle
  25. }
  26. // LoadDLL loads DLL file into memory.
  27. //
  28. // Warning: using LoadDLL without an absolute path name is subject to
  29. // DLL preloading attacks. To safely load a system DLL, use LazyDLL
  30. // with System set to true, or use LoadLibraryEx directly.
  31. func LoadDLL(name string) (dll *DLL, err error) {
  32. namep, err := UTF16PtrFromString(name)
  33. if err != nil {
  34. return nil, err
  35. }
  36. h, e := loadlibrary(namep)
  37. if e != 0 {
  38. return nil, &DLLError{
  39. Err: e,
  40. ObjName: name,
  41. Msg: "Failed to load " + name + ": " + e.Error(),
  42. }
  43. }
  44. d := &DLL{
  45. Name: name,
  46. Handle: Handle(h),
  47. }
  48. return d, nil
  49. }
  50. // MustLoadDLL is like LoadDLL but panics if load operation failes.
  51. func MustLoadDLL(name string) *DLL {
  52. d, e := LoadDLL(name)
  53. if e != nil {
  54. panic(e)
  55. }
  56. return d
  57. }
  58. // FindProc searches DLL d for procedure named name and returns *Proc
  59. // if found. It returns an error if search fails.
  60. func (d *DLL) FindProc(name string) (proc *Proc, err error) {
  61. namep, err := BytePtrFromString(name)
  62. if err != nil {
  63. return nil, err
  64. }
  65. a, e := getprocaddress(uintptr(d.Handle), namep)
  66. if e != 0 {
  67. return nil, &DLLError{
  68. Err: e,
  69. ObjName: name,
  70. Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
  71. }
  72. }
  73. p := &Proc{
  74. Dll: d,
  75. Name: name,
  76. addr: a,
  77. }
  78. return p, nil
  79. }
  80. // MustFindProc is like FindProc but panics if search fails.
  81. func (d *DLL) MustFindProc(name string) *Proc {
  82. p, e := d.FindProc(name)
  83. if e != nil {
  84. panic(e)
  85. }
  86. return p
  87. }
  88. // Release unloads DLL d from memory.
  89. func (d *DLL) Release() (err error) {
  90. return FreeLibrary(d.Handle)
  91. }
  92. // A Proc implements access to a procedure inside a DLL.
  93. type Proc struct {
  94. Dll *DLL
  95. Name string
  96. addr uintptr
  97. }
  98. // Addr returns the address of the procedure represented by p.
  99. // The return value can be passed to Syscall to run the procedure.
  100. func (p *Proc) Addr() uintptr {
  101. return p.addr
  102. }
  103. // Call executes procedure p with arguments a. It will panic, if more then 15 arguments
  104. // are supplied.
  105. //
  106. // The returned error is always non-nil, constructed from the result of GetLastError.
  107. // Callers must inspect the primary return value to decide whether an error occurred
  108. // (according to the semantics of the specific function being called) before consulting
  109. // the error. The error will be guaranteed to contain windows.Errno.
  110. func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
  111. switch len(a) {
  112. case 0:
  113. return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
  114. case 1:
  115. return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
  116. case 2:
  117. return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
  118. case 3:
  119. return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
  120. case 4:
  121. return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
  122. case 5:
  123. return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
  124. case 6:
  125. return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
  126. case 7:
  127. return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
  128. case 8:
  129. return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
  130. case 9:
  131. return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
  132. case 10:
  133. return syscall.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)
  134. case 11:
  135. return syscall.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)
  136. case 12:
  137. return syscall.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])
  138. case 13:
  139. return syscall.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)
  140. case 14:
  141. return syscall.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)
  142. case 15:
  143. return syscall.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])
  144. default:
  145. panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
  146. }
  147. return
  148. }
  149. // A LazyDLL implements access to a single DLL.
  150. // It will delay the load of the DLL until the first
  151. // call to its Handle method or to one of its
  152. // LazyProc's Addr method.
  153. type LazyDLL struct {
  154. Name string
  155. // System determines whether the DLL must be loaded from the
  156. // Windows System directory, bypassing the normal DLL search
  157. // path.
  158. System bool
  159. mu sync.Mutex
  160. dll *DLL // non nil once DLL is loaded
  161. }
  162. // Load loads DLL file d.Name into memory. It returns an error if fails.
  163. // Load will not try to load DLL, if it is already loaded into memory.
  164. func (d *LazyDLL) Load() error {
  165. // Non-racy version of:
  166. // if d.dll != nil {
  167. if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil {
  168. return nil
  169. }
  170. d.mu.Lock()
  171. defer d.mu.Unlock()
  172. if d.dll != nil {
  173. return nil
  174. }
  175. // kernel32.dll is special, since it's where LoadLibraryEx comes from.
  176. // The kernel already special-cases its name, so it's always
  177. // loaded from system32.
  178. var dll *DLL
  179. var err error
  180. if d.Name == "kernel32.dll" {
  181. dll, err = LoadDLL(d.Name)
  182. } else {
  183. dll, err = loadLibraryEx(d.Name, d.System)
  184. }
  185. if err != nil {
  186. return err
  187. }
  188. // Non-racy version of:
  189. // d.dll = dll
  190. atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
  191. return nil
  192. }
  193. // mustLoad is like Load but panics if search fails.
  194. func (d *LazyDLL) mustLoad() {
  195. e := d.Load()
  196. if e != nil {
  197. panic(e)
  198. }
  199. }
  200. // Handle returns d's module handle.
  201. func (d *LazyDLL) Handle() uintptr {
  202. d.mustLoad()
  203. return uintptr(d.dll.Handle)
  204. }
  205. // NewProc returns a LazyProc for accessing the named procedure in the DLL d.
  206. func (d *LazyDLL) NewProc(name string) *LazyProc {
  207. return &LazyProc{l: d, Name: name}
  208. }
  209. // NewLazyDLL creates new LazyDLL associated with DLL file.
  210. func NewLazyDLL(name string) *LazyDLL {
  211. return &LazyDLL{Name: name}
  212. }
  213. // NewLazySystemDLL is like NewLazyDLL, but will only
  214. // search Windows System directory for the DLL if name is
  215. // a base name (like "advapi32.dll").
  216. func NewLazySystemDLL(name string) *LazyDLL {
  217. return &LazyDLL{Name: name, System: true}
  218. }
  219. // A LazyProc implements access to a procedure inside a LazyDLL.
  220. // It delays the lookup until the Addr method is called.
  221. type LazyProc struct {
  222. Name string
  223. mu sync.Mutex
  224. l *LazyDLL
  225. proc *Proc
  226. }
  227. // Find searches DLL for procedure named p.Name. It returns
  228. // an error if search fails. Find will not search procedure,
  229. // if it is already found and loaded into memory.
  230. func (p *LazyProc) Find() error {
  231. // Non-racy version of:
  232. // if p.proc == nil {
  233. if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
  234. p.mu.Lock()
  235. defer p.mu.Unlock()
  236. if p.proc == nil {
  237. e := p.l.Load()
  238. if e != nil {
  239. return e
  240. }
  241. proc, e := p.l.dll.FindProc(p.Name)
  242. if e != nil {
  243. return e
  244. }
  245. // Non-racy version of:
  246. // p.proc = proc
  247. atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
  248. }
  249. }
  250. return nil
  251. }
  252. // mustFind is like Find but panics if search fails.
  253. func (p *LazyProc) mustFind() {
  254. e := p.Find()
  255. if e != nil {
  256. panic(e)
  257. }
  258. }
  259. // Addr returns the address of the procedure represented by p.
  260. // The return value can be passed to Syscall to run the procedure.
  261. func (p *LazyProc) Addr() uintptr {
  262. p.mustFind()
  263. return p.proc.Addr()
  264. }
  265. // Call executes procedure p with arguments a. It will panic, if more then 15 arguments
  266. // are supplied.
  267. //
  268. // The returned error is always non-nil, constructed from the result of GetLastError.
  269. // Callers must inspect the primary return value to decide whether an error occurred
  270. // (according to the semantics of the specific function being called) before consulting
  271. // the error. The error will be guaranteed to contain windows.Errno.
  272. func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
  273. p.mustFind()
  274. return p.proc.Call(a...)
  275. }
  276. var canDoSearchSystem32Once struct {
  277. sync.Once
  278. v bool
  279. }
  280. func initCanDoSearchSystem32() {
  281. // https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
  282. // "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
  283. // Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
  284. // systems that have KB2533623 installed. To determine whether the
  285. // flags are available, use GetProcAddress to get the address of the
  286. // AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
  287. // function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
  288. // flags can be used with LoadLibraryEx."
  289. canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil)
  290. }
  291. func canDoSearchSystem32() bool {
  292. canDoSearchSystem32Once.Do(initCanDoSearchSystem32)
  293. return canDoSearchSystem32Once.v
  294. }
  295. func isBaseName(name string) bool {
  296. for _, c := range name {
  297. if c == ':' || c == '/' || c == '\\' {
  298. return false
  299. }
  300. }
  301. return true
  302. }
  303. // loadLibraryEx wraps the Windows LoadLibraryEx function.
  304. //
  305. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
  306. //
  307. // If name is not an absolute path, LoadLibraryEx searches for the DLL
  308. // in a variety of automatic locations unless constrained by flags.
  309. // See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx
  310. func loadLibraryEx(name string, system bool) (*DLL, error) {
  311. loadDLL := name
  312. var flags uintptr
  313. if system {
  314. if canDoSearchSystem32() {
  315. const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
  316. flags = LOAD_LIBRARY_SEARCH_SYSTEM32
  317. } else if isBaseName(name) {
  318. // WindowsXP or unpatched Windows machine
  319. // trying to load "foo.dll" out of the system
  320. // folder, but LoadLibraryEx doesn't support
  321. // that yet on their system, so emulate it.
  322. windir, _ := Getenv("WINDIR") // old var; apparently works on XP
  323. if windir == "" {
  324. return nil, errString("%WINDIR% not defined")
  325. }
  326. loadDLL = windir + "\\System32\\" + name
  327. }
  328. }
  329. h, err := LoadLibraryEx(loadDLL, 0, flags)
  330. if err != nil {
  331. return nil, err
  332. }
  333. return &DLL{Name: name, Handle: h}, nil
  334. }
  335. type errString string
  336. func (s errString) Error() string { return string(s) }