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