github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/libc/lookup_libc_nocgo_darwin.go (about)

     1  //go:build !cgo && darwin
     2  // +build !cgo,darwin
     3  
     4  package libc
     5  
     6  import (
     7  	"fmt"
     8  	"github.com/eh-steve/goloader"
     9  	"github.com/eh-steve/goloader/goversion"
    10  	"reflect"
    11  	"runtime"
    12  	"syscall"
    13  	"unsafe"
    14  )
    15  
    16  const (
    17  	RTLD_NOW    int32 = 0x00002
    18  	RTLD_GLOBAL int32 = 0x00100
    19  )
    20  
    21  var selfDlHandle uintptr
    22  
    23  func libc_dlopen_trampoline()
    24  
    25  func libc_dlopen_noframe_trampoline()
    26  
    27  //go:cgo_import_dynamic libc_dlopen dlopen "/usr/lib/libSystem.B.dylib"
    28  
    29  func libc_dlsym_trampoline()
    30  
    31  func libc_dlsym_noframe_trampoline()
    32  
    33  //go:cgo_import_dynamic libc_dlsym dlsym "/usr/lib/libSystem.B.dylib"
    34  
    35  //go:linkname libcCall runtime.libcCall
    36  func libcCall(fn, arg unsafe.Pointer) int32
    37  
    38  var dlopenABI0 uintptr
    39  var dlsymABI0 uintptr
    40  
    41  func init() {
    42  
    43  	var dlopenABIInternal, dlsymABIInternal uintptr
    44  	if goversion.GoVersion() >= 21 {
    45  		// Go 1.21 automatically wraps asm funcs with a BP push/pop to create a frame, so no need to do it ourselves
    46  		dlopenABIInternal = reflect.ValueOf(libc_dlopen_noframe_trampoline).Pointer()
    47  		dlsymABIInternal = reflect.ValueOf(libc_dlsym_noframe_trampoline).Pointer()
    48  	} else {
    49  		dlopenABIInternal = reflect.ValueOf(libc_dlopen_trampoline).Pointer()
    50  		dlsymABIInternal = reflect.ValueOf(libc_dlsym_trampoline).Pointer()
    51  	}
    52  
    53  	// reflect.(*Value).Pointer() will always give the ABIInternal FuncPC, not the ABI0.
    54  	// Since we don't have access to internal/abi.FuncPCABI0, we need to find the ABI0
    55  	// version of the function based on whichever one isn't the ABIInternal PC
    56  
    57  	abi0PCs := goloader.FuncPCsABI0([]uintptr{dlopenABIInternal, dlsymABIInternal})
    58  	dlopenABI0 = abi0PCs[0]
    59  	dlsymABI0 = abi0PCs[1]
    60  
    61  	if dlopenABI0 == 0 {
    62  		panic("could not find ABI0 version of libc_dlopen_trampoline")
    63  	}
    64  	if dlsymABI0 == 0 {
    65  		panic("could not find ABI0 version of libc_dlsym_trampoline")
    66  	}
    67  
    68  	h, errNo := selfOpen()
    69  	if h == 0 {
    70  		panic("failed to open self with dlopen, errNo: " + syscall.Errno(errNo).Error())
    71  	}
    72  	selfDlHandle = h
    73  }
    74  
    75  func selfOpen() (uintptr, int32) {
    76  	args := struct {
    77  		soPath    *byte
    78  		flags     int32
    79  		retHandle uintptr
    80  		retErrNo  int32
    81  	}{
    82  		soPath: nil,
    83  		flags:  RTLD_GLOBAL | RTLD_NOW,
    84  	}
    85  	libcCall(unsafe.Pointer(dlopenABI0), unsafe.Pointer(&args))
    86  	runtime.KeepAlive(args)
    87  	return args.retHandle + 2, args.retErrNo // Why +2??
    88  }
    89  
    90  func lookup(symName string) uintptr {
    91  	symNameBytes := make([]byte, len(symName)+1)
    92  	copy(symNameBytes, symName)
    93  	symNamePtr := &symNameBytes[0]
    94  	args := struct {
    95  		handle  uintptr
    96  		symName *byte
    97  		retAddr uintptr
    98  	}{
    99  		handle:  selfDlHandle,
   100  		symName: symNamePtr,
   101  	}
   102  	_ = libcCall(unsafe.Pointer(dlsymABI0), unsafe.Pointer(&args))
   103  	runtime.KeepAlive(args)
   104  	runtime.KeepAlive(symNameBytes)
   105  	return args.retAddr
   106  }
   107  
   108  func LookupDynamicSymbol(symName string) (uintptr, error) {
   109  	addr := lookup(symName)
   110  	if addr == 0 {
   111  		return 0, fmt.Errorf("failed to lookup symbol '%s'", symName)
   112  	}
   113  	return addr, nil
   114  }