msgx_darwin.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. //go:build !ios
  2. package msgx
  3. import (
  4. "sync"
  5. "syscall"
  6. "unsafe"
  7. )
  8. // dlsym is public libSystem API, imported the way x/sys/unix imports its libSystem stubs. The local name is
  9. // distinct from runtime/race's dlsym import.
  10. //
  11. //go:cgo_import_dynamic msgx_dlsym dlsym "/usr/lib/libSystem.B.dylib"
  12. // dlsymTrampolineAddr is the address of msgx_darwin.s's jump to dlsym.
  13. var dlsymTrampolineAddr uintptr
  14. // syscall6X calls the C function at fn on the system stack and returns errno when it returns a 64-bit -1.
  15. // It is how x/sys/unix calls libSystem, and the runtime keeps it linkable for that.
  16. //
  17. //go:linkname syscall6X syscall.syscall6X
  18. func syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
  19. // rtldDefault is dlfcn.h's RTLD_DEFAULT, ((void *)-2): search every image loaded in the process.
  20. const rtldDefault = ^uintptr(1)
  21. var (
  22. sendmsgXName = []byte("sendmsg_x\x00")
  23. recvmsgXName = []byte("recvmsg_x\x00")
  24. )
  25. type funcs struct {
  26. sendmsgX uintptr
  27. recvmsgX uintptr
  28. }
  29. var resolve = sync.OnceValue(func() funcs {
  30. return funcs{sendmsgX: lookup(sendmsgXName), recvmsgX: lookup(recvmsgXName)}
  31. })
  32. // lookup returns the address of the NUL-terminated C symbol name, or 0 when no loaded image exports it.
  33. func lookup(name []byte) uintptr {
  34. p, _, _ := syscall6X(dlsymTrampolineAddr, rtldDefault, uintptr(unsafe.Pointer(&name[0])), 0, 0, 0, 0)
  35. return p
  36. }
  37. // Available reports whether libSystem exports both sendmsg_x and recvmsg_x. When it is false, Send and Recv
  38. // return ENOSYS.
  39. func Available() bool {
  40. f := resolve()
  41. return f.sendmsgX != 0 && f.recvmsgX != 0
  42. }
  43. // Send passes hdrs to sendmsg_x on fd and returns how many messages the kernel took.
  44. func Send(fd uintptr, hdrs []Hdr, flags int) (int, syscall.Errno) {
  45. return call(resolve().sendmsgX, fd, hdrs, flags)
  46. }
  47. // Recv passes hdrs to recvmsg_x on fd and returns how many messages it filled.
  48. func Recv(fd uintptr, hdrs []Hdr, flags int) (int, syscall.Errno) {
  49. return call(resolve().recvmsgX, fd, hdrs, flags)
  50. }
  51. func call(fn, fd uintptr, hdrs []Hdr, flags int) (int, syscall.Errno) {
  52. if fn == 0 {
  53. return 0, syscall.ENOSYS
  54. }
  55. if len(hdrs) == 0 {
  56. return 0, syscall.EINVAL
  57. }
  58. r, _, errno := syscall6X(fn, fd, uintptr(unsafe.Pointer(&hdrs[0])), uintptr(len(hdrs)), uintptr(flags), 0, 0)
  59. if errno != 0 {
  60. return 0, errno
  61. }
  62. return int(r), 0
  63. }