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 }