github.com/jwijenbergh/purego@v0.0.0-20240126093400-70ff3a61df13/dlfcn.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // SPDX-FileCopyrightText: 2022 The Ebitengine Authors
     3  
     4  //go:build darwin || freebsd || linux
     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  func Dlopen(path string, mode int) (uintptr, error) {
    37  	u := fnDlopen(path, mode)
    38  	if u == 0 {
    39  		return 0, Dlerror{fnDlerror()}
    40  	}
    41  	return u, nil
    42  }
    43  
    44  // Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name.
    45  // It returns the address where that symbol is loaded into memory. If the symbol is not found,
    46  // in the specified library or any of the libraries that were automatically loaded by Dlopen
    47  // when that library was loaded, Dlsym returns zero.
    48  func Dlsym(handle uintptr, name string) (uintptr, error) {
    49  	u := fnDlsym(handle, name)
    50  	if u == 0 {
    51  		return 0, Dlerror{fnDlerror()}
    52  	}
    53  	return u, nil
    54  }
    55  
    56  // Dlclose decrements the reference count on the dynamic library handle.
    57  // If the reference count drops to zero and no other loaded libraries
    58  // use symbols in it, then the dynamic library is unloaded.
    59  func Dlclose(handle uintptr) error {
    60  	if fnDlclose(handle) {
    61  		return Dlerror{fnDlerror()}
    62  	}
    63  	return nil
    64  }
    65  
    66  //go:linkname openLibrary openLibrary
    67  func openLibrary(name string) (uintptr, error) {
    68  	return Dlopen(name, RTLD_NOW|RTLD_GLOBAL)
    69  }
    70  
    71  func loadSymbol(handle uintptr, name string) (uintptr, error) {
    72  	return Dlsym(handle, name)
    73  }
    74  
    75  // these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go
    76  // the indirection is necessary because a function is actually a pointer to the pointer to the code.
    77  // sadly, I do not know of anyway to remove the assembly stubs entirely because //go:linkname doesn't
    78  // appear to work if you link directly to the C function on darwin arm64.
    79  
    80  //go:linkname dlopen dlopen
    81  var dlopen uintptr
    82  var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
    83  
    84  //go:linkname dlsym dlsym
    85  var dlsym uintptr
    86  var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
    87  
    88  //go:linkname dlclose dlclose
    89  var dlclose uintptr
    90  var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
    91  
    92  //go:linkname dlerror dlerror
    93  var dlerror uintptr
    94  var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))