dlfcn.go raw

   1  // SPDX-License-Identifier: Apache-2.0
   2  // SPDX-FileCopyrightText: 2022 The Ebitengine Authors
   3  
   4  //go:build (darwin || freebsd || linux || netbsd) && !android && !faketime
   5  
   6  package purego
   7  
   8  import (
   9  	"unsafe"
  10  )
  11  
  12  // Unix Specification for dlfcn.h: https://pubs.opengroup.org/onlinepubs/7908799/xsh/dlfcn.h.html
  13  
  14  var (
  15  	fnDlopen  func(path string, mode int) uintptr
  16  	fnDlsym   func(handle uintptr, name string) uintptr
  17  	fnDlerror func() string
  18  	fnDlclose func(handle uintptr) bool
  19  )
  20  
  21  func init() {
  22  	RegisterFunc(&fnDlopen, dlopenABI0)
  23  	RegisterFunc(&fnDlsym, dlsymABI0)
  24  	RegisterFunc(&fnDlerror, dlerrorABI0)
  25  	RegisterFunc(&fnDlclose, dlcloseABI0)
  26  }
  27  
  28  // Dlopen examines the dynamic library or bundle file specified by path. If the file is compatible
  29  // with the current process and has not already been loaded into the
  30  // current process, it is loaded and linked. After being linked, if it contains
  31  // any initializer functions, they are called, before Dlopen
  32  // returns. It returns a handle that can be used with Dlsym and Dlclose.
  33  // A second call to Dlopen with the same path will return the same handle, but the internal
  34  // reference count for the handle will be incremented. Therefore, all
  35  // Dlopen calls should be balanced with a Dlclose call.
  36  //
  37  // This function is not available on Windows.
  38  // Use [golang.org/x/sys/windows.LoadLibrary], [golang.org/x/sys/windows.LoadLibraryEx],
  39  // [golang.org/x/sys/windows.NewLazyDLL], or [golang.org/x/sys/windows.NewLazySystemDLL] for Windows instead.
  40  func Dlopen(path string, mode int) (uintptr, error) {
  41  	u := fnDlopen(path, mode)
  42  	if u == 0 {
  43  		return 0, Dlerror{fnDlerror()}
  44  	}
  45  	return u, nil
  46  }
  47  
  48  // Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name.
  49  // It returns the address where that symbol is loaded into memory. If the symbol is not found,
  50  // in the specified library or any of the libraries that were automatically loaded by Dlopen
  51  // when that library was loaded, Dlsym returns zero.
  52  //
  53  // This function is not available on Windows.
  54  // Use [golang.org/x/sys/windows.GetProcAddress] for Windows instead.
  55  func Dlsym(handle uintptr, name string) (uintptr, error) {
  56  	u := fnDlsym(handle, name)
  57  	if u == 0 {
  58  		return 0, Dlerror{fnDlerror()}
  59  	}
  60  	return u, nil
  61  }
  62  
  63  // Dlclose decrements the reference count on the dynamic library handle.
  64  // If the reference count drops to zero and no other loaded libraries
  65  // use symbols in it, then the dynamic library is unloaded.
  66  //
  67  // This function is not available on Windows.
  68  // Use [golang.org/x/sys/windows.FreeLibrary] for Windows instead.
  69  func Dlclose(handle uintptr) error {
  70  	if fnDlclose(handle) {
  71  		return Dlerror{fnDlerror()}
  72  	}
  73  	return nil
  74  }
  75  
  76  func loadSymbol(handle uintptr, name string) (uintptr, error) {
  77  	return Dlsym(handle, name)
  78  }
  79  
  80  // these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go
  81  // the indirection is necessary because a function is actually a pointer to the pointer to the code.
  82  // sadly, I do not know of anyway to remove the assembly stubs entirely because //go:linkname doesn't
  83  // appear to work if you link directly to the C function on darwin arm64.
  84  
  85  //go:linkname dlopen dlopen
  86  var dlopen uint8
  87  var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
  88  
  89  //go:linkname dlsym dlsym
  90  var dlsym uint8
  91  var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
  92  
  93  //go:linkname dlclose dlclose
  94  var dlclose uint8
  95  var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
  96  
  97  //go:linkname dlerror dlerror
  98  var dlerror uint8
  99  var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))
 100