github.com/ebitengine/purego@v0.8.0-alpha.2.0.20240512170805-6cd12240d332/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  func loadSymbol(handle uintptr, name string) (uintptr, error) {
    67  	return Dlsym(handle, name)
    68  }
    69  
    70  // these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go
    71  // the indirection is necessary because a function is actually a pointer to the pointer to the code.
    72  // sadly, I do not know of anyway to remove the assembly stubs entirely because //go:linkname doesn't
    73  // appear to work if you link directly to the C function on darwin arm64.
    74  
    75  //go:linkname dlopen dlopen
    76  var dlopen uintptr
    77  var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
    78  
    79  //go:linkname dlsym dlsym
    80  var dlsym uintptr
    81  var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
    82  
    83  //go:linkname dlclose dlclose
    84  var dlclose uintptr
    85  var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
    86  
    87  //go:linkname dlerror dlerror
    88  var dlerror uintptr
    89  var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))