func.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. // SPDX-License-Identifier: Apache-2.0
  2. // SPDX-FileCopyrightText: 2022 The Ebitengine Authors
  3. //go:build darwin || freebsd || linux || windows
  4. package purego
  5. import (
  6. "fmt"
  7. "math"
  8. "reflect"
  9. "runtime"
  10. "unsafe"
  11. "github.com/ebitengine/purego/internal/strings"
  12. )
  13. // RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name).
  14. // It panics if it can't find the name symbol.
  15. func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
  16. sym, err := loadSymbol(handle, name)
  17. if err != nil {
  18. panic(err)
  19. }
  20. RegisterFunc(fptr, sym)
  21. }
  22. // RegisterFunc takes a pointer to a Go function representing the calling convention of the C function.
  23. // fptr will be set to a function that when called will call the C function given by cfn with the
  24. // parameters passed in the correct registers and stack.
  25. //
  26. // A panic is produced if the type is not a function pointer or if the function returns more than 1 value.
  27. //
  28. // These conversions describe how a Go type in the fptr will be used to call
  29. // the C function. It is important to note that there is no way to verify that fptr
  30. // matches the C function. This also holds true for struct types where the padding
  31. // needs to be ensured to match that of C; RegisterFunc does not verify this.
  32. //
  33. // # Type Conversions (Go <=> C)
  34. //
  35. // string <=> char*
  36. // bool <=> _Bool
  37. // uintptr <=> uintptr_t
  38. // uint <=> uint32_t or uint64_t
  39. // uint8 <=> uint8_t
  40. // uint16 <=> uint16_t
  41. // uint32 <=> uint32_t
  42. // uint64 <=> uint64_t
  43. // int <=> int32_t or int64_t
  44. // int8 <=> int8_t
  45. // int16 <=> int16_t
  46. // int32 <=> int32_t
  47. // int64 <=> int64_t
  48. // float32 <=> float
  49. // float64 <=> double
  50. // struct <=> struct (WIP - darwin only)
  51. // func <=> C function
  52. // unsafe.Pointer, *T <=> void*
  53. // []T => void*
  54. //
  55. // There is a special case when the last argument of fptr is a variadic interface (or []interface}
  56. // it will be expanded into a call to the C function as if it had the arguments in that slice.
  57. // This means that using arg ...interface{} is like a cast to the function with the arguments inside arg.
  58. // This is not the same as C variadic.
  59. //
  60. // # Memory
  61. //
  62. // In general it is not possible for purego to guarantee the lifetimes of objects returned or received from
  63. // calling functions using RegisterFunc. For arguments to a C function it is important that the C function doesn't
  64. // hold onto a reference to Go memory. This is the same as the [Cgo rules].
  65. //
  66. // However, there are some special cases. When passing a string as an argument if the string does not end in a null
  67. // terminated byte (\x00) then the string will be copied into memory maintained by purego. The memory is only valid for
  68. // that specific call. Therefore, if the C code keeps a reference to that string it may become invalid at some
  69. // undefined time. However, if the string does already contain a null-terminated byte then no copy is done.
  70. // It is then the responsibility of the caller to ensure the string stays alive as long as it's needed in C memory.
  71. // This can be done using runtime.KeepAlive or allocating the string in C memory using malloc. When a C function
  72. // returns a null-terminated pointer to char a Go string can be used. Purego will allocate a new string in Go memory
  73. // and copy the data over. This string will be garbage collected whenever Go decides it's no longer referenced.
  74. // This C created string will not be freed by purego. If the pointer to char is not null-terminated or must continue
  75. // to point to C memory (because it's a buffer for example) then use a pointer to byte and then convert that to a slice
  76. // using unsafe.Slice. Doing this means that it becomes the responsibility of the caller to care about the lifetime
  77. // of the pointer
  78. //
  79. // # Structs
  80. //
  81. // Purego can handle the most common structs that have fields of builtin types like int8, uint16, float32, etc. However,
  82. // it does not support aligning fields properly. It is therefore the responsibility of the caller to ensure
  83. // that all padding is added to the Go struct to match the C one. See `BoolStructFn` in struct_test.go for an example.
  84. //
  85. // # Example
  86. //
  87. // All functions below call this C function:
  88. //
  89. // char *foo(char *str);
  90. //
  91. // // Let purego convert types
  92. // var foo func(s string) string
  93. // goString := foo("copied")
  94. // // Go will garbage collect this string
  95. //
  96. // // Manually, handle allocations
  97. // var foo2 func(b string) *byte
  98. // mustFree := foo2("not copied\x00")
  99. // defer free(mustFree)
  100. //
  101. // [Cgo rules]: https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C
  102. func RegisterFunc(fptr interface{}, cfn uintptr) {
  103. fn := reflect.ValueOf(fptr).Elem()
  104. ty := fn.Type()
  105. if ty.Kind() != reflect.Func {
  106. panic("purego: fptr must be a function pointer")
  107. }
  108. if ty.NumOut() > 1 {
  109. panic("purego: function can only return zero or one values")
  110. }
  111. if cfn == 0 {
  112. panic("purego: cfn is nil")
  113. }
  114. if ty.NumOut() == 1 && (ty.Out(0).Kind() == reflect.Float32 || ty.Out(0).Kind() == reflect.Float64) &&
  115. runtime.GOARCH != "arm64" && runtime.GOARCH != "amd64" {
  116. panic("purego: float returns are not supported")
  117. }
  118. {
  119. // this code checks how many registers and stack this function will use
  120. // to avoid crashing with too many arguments
  121. var ints int
  122. var floats int
  123. var stack int
  124. for i := 0; i < ty.NumIn(); i++ {
  125. arg := ty.In(i)
  126. switch arg.Kind() {
  127. case reflect.Func:
  128. // This only does preliminary testing to ensure the CDecl argument
  129. // is the first argument. Full testing is done when the callback is actually
  130. // created in NewCallback.
  131. for j := 0; j < arg.NumIn(); j++ {
  132. in := arg.In(j)
  133. if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
  134. continue
  135. }
  136. if j != 0 {
  137. panic("purego: CDecl must be the first argument")
  138. }
  139. }
  140. case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
  141. reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Ptr, reflect.UnsafePointer,
  142. reflect.Slice, reflect.Bool:
  143. if ints < numOfIntegerRegisters() {
  144. ints++
  145. } else {
  146. stack++
  147. }
  148. case reflect.Float32, reflect.Float64:
  149. const is32bit = unsafe.Sizeof(uintptr(0)) == 4
  150. if is32bit {
  151. panic("purego: floats only supported on 64bit platforms")
  152. }
  153. if floats < numOfFloats {
  154. floats++
  155. } else {
  156. stack++
  157. }
  158. case reflect.Struct:
  159. if runtime.GOOS != "darwin" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64") {
  160. panic("purego: struct arguments are only supported on darwin amd64 & arm64")
  161. }
  162. if arg.Size() == 0 {
  163. continue
  164. }
  165. addInt := func(u uintptr) {
  166. ints++
  167. }
  168. addFloat := func(u uintptr) {
  169. floats++
  170. }
  171. addStack := func(u uintptr) {
  172. stack++
  173. }
  174. _ = addStruct(reflect.New(arg).Elem(), &ints, &floats, &stack, addInt, addFloat, addStack, nil)
  175. default:
  176. panic("purego: unsupported kind " + arg.Kind().String())
  177. }
  178. }
  179. if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
  180. if runtime.GOOS != "darwin" {
  181. panic("purego: struct return values only supported on darwin arm64 & amd64")
  182. }
  183. outType := ty.Out(0)
  184. checkStructFieldsSupported(outType)
  185. if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
  186. // on amd64 if struct is bigger than 16 bytes allocate the return struct
  187. // and pass it in as a hidden first argument.
  188. ints++
  189. }
  190. }
  191. sizeOfStack := maxArgs - numOfIntegerRegisters()
  192. if stack > sizeOfStack {
  193. panic("purego: too many arguments")
  194. }
  195. }
  196. v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
  197. if len(args) > 0 {
  198. if variadic, ok := args[len(args)-1].Interface().([]interface{}); ok {
  199. // subtract one from args bc the last argument in args is []interface{}
  200. // which we are currently expanding
  201. tmp := make([]reflect.Value, len(args)-1+len(variadic))
  202. n := copy(tmp, args[:len(args)-1])
  203. for i, v := range variadic {
  204. tmp[n+i] = reflect.ValueOf(v)
  205. }
  206. args = tmp
  207. }
  208. }
  209. var sysargs [maxArgs]uintptr
  210. stack := sysargs[numOfIntegerRegisters():]
  211. var floats [numOfFloats]uintptr
  212. var numInts int
  213. var numFloats int
  214. var numStack int
  215. var addStack, addInt, addFloat func(x uintptr)
  216. if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
  217. // Windows arm64 uses the same calling convention as macOS and Linux
  218. addStack = func(x uintptr) {
  219. stack[numStack] = x
  220. numStack++
  221. }
  222. addInt = func(x uintptr) {
  223. if numInts >= numOfIntegerRegisters() {
  224. addStack(x)
  225. } else {
  226. sysargs[numInts] = x
  227. numInts++
  228. }
  229. }
  230. addFloat = func(x uintptr) {
  231. if numFloats < len(floats) {
  232. floats[numFloats] = x
  233. numFloats++
  234. } else {
  235. addStack(x)
  236. }
  237. }
  238. } else {
  239. // On Windows amd64 the arguments are passed in the numbered registered.
  240. // So the first int is in the first integer register and the first float
  241. // is in the second floating register if there is already a first int.
  242. // This is in contrast to how macOS and Linux pass arguments which
  243. // tries to use as many registers as possible in the calling convention.
  244. addStack = func(x uintptr) {
  245. sysargs[numStack] = x
  246. numStack++
  247. }
  248. addInt = addStack
  249. addFloat = addStack
  250. }
  251. var keepAlive []interface{}
  252. defer func() {
  253. runtime.KeepAlive(keepAlive)
  254. runtime.KeepAlive(args)
  255. }()
  256. var syscall syscall15Args
  257. if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
  258. outType := ty.Out(0)
  259. if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
  260. val := reflect.New(outType)
  261. keepAlive = append(keepAlive, val)
  262. addInt(val.Pointer())
  263. } else if runtime.GOARCH == "arm64" && outType.Size() > maxRegAllocStructSize {
  264. isAllFloats, numFields := isAllSameFloat(outType)
  265. if !isAllFloats || numFields > 4 {
  266. val := reflect.New(outType)
  267. keepAlive = append(keepAlive, val)
  268. syscall.arm64_r8 = val.Pointer()
  269. }
  270. }
  271. }
  272. for _, v := range args {
  273. switch v.Kind() {
  274. case reflect.String:
  275. ptr := strings.CString(v.String())
  276. keepAlive = append(keepAlive, ptr)
  277. addInt(uintptr(unsafe.Pointer(ptr)))
  278. case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  279. addInt(uintptr(v.Uint()))
  280. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  281. addInt(uintptr(v.Int()))
  282. case reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
  283. // There is no need to keepAlive this pointer separately because it is kept alive in the args variable
  284. addInt(v.Pointer())
  285. case reflect.Func:
  286. addInt(NewCallback(v.Interface()))
  287. case reflect.Bool:
  288. if v.Bool() {
  289. addInt(1)
  290. } else {
  291. addInt(0)
  292. }
  293. case reflect.Float32:
  294. addFloat(uintptr(math.Float32bits(float32(v.Float()))))
  295. case reflect.Float64:
  296. addFloat(uintptr(math.Float64bits(v.Float())))
  297. case reflect.Struct:
  298. keepAlive = addStruct(v, &numInts, &numFloats, &numStack, addInt, addFloat, addStack, keepAlive)
  299. default:
  300. panic("purego: unsupported kind: " + v.Kind().String())
  301. }
  302. }
  303. if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
  304. // Use the normal arm64 calling convention even on Windows
  305. syscall = syscall15Args{
  306. cfn,
  307. sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5],
  308. sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
  309. sysargs[12], sysargs[13], sysargs[14],
  310. floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7],
  311. syscall.arm64_r8,
  312. }
  313. runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall))
  314. } else {
  315. // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats
  316. syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4],
  317. sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11],
  318. sysargs[12], sysargs[13], sysargs[14])
  319. syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support
  320. }
  321. if ty.NumOut() == 0 {
  322. return nil
  323. }
  324. outType := ty.Out(0)
  325. v := reflect.New(outType).Elem()
  326. switch outType.Kind() {
  327. case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  328. v.SetUint(uint64(syscall.a1))
  329. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  330. v.SetInt(int64(syscall.a1))
  331. case reflect.Bool:
  332. v.SetBool(byte(syscall.a1) != 0)
  333. case reflect.UnsafePointer:
  334. // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer
  335. v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1)))
  336. case reflect.Ptr:
  337. v = reflect.NewAt(outType, unsafe.Pointer(&syscall.a1)).Elem()
  338. case reflect.Func:
  339. // wrap this C function in a nicely typed Go function
  340. v = reflect.New(outType)
  341. RegisterFunc(v.Interface(), syscall.a1)
  342. case reflect.String:
  343. v.SetString(strings.GoString(syscall.a1))
  344. case reflect.Float32:
  345. // NOTE: syscall.r2 is only the floating return value on 64bit platforms.
  346. // On 32bit platforms syscall.r2 is the upper part of a 64bit return.
  347. v.SetFloat(float64(math.Float32frombits(uint32(syscall.f1))))
  348. case reflect.Float64:
  349. // NOTE: syscall.r2 is only the floating return value on 64bit platforms.
  350. // On 32bit platforms syscall.r2 is the upper part of a 64bit return.
  351. v.SetFloat(math.Float64frombits(uint64(syscall.f1)))
  352. case reflect.Struct:
  353. v = getStruct(outType, syscall)
  354. default:
  355. panic("purego: unsupported return kind: " + outType.Kind().String())
  356. }
  357. return []reflect.Value{v}
  358. })
  359. fn.Set(v)
  360. }
  361. // maxRegAllocStructSize is the biggest a struct can be while still fitting in registers.
  362. // if it is bigger than this than enough space must be allocated on the heap and then passed into
  363. // the function as the first parameter on amd64 or in R8 on arm64.
  364. //
  365. // If you change this make sure to update it in objc_runtime_darwin.go
  366. const maxRegAllocStructSize = 16
  367. func isAllSameFloat(ty reflect.Type) (allFloats bool, numFields int) {
  368. allFloats = true
  369. root := ty.Field(0).Type
  370. for root.Kind() == reflect.Struct {
  371. root = root.Field(0).Type
  372. }
  373. first := root.Kind()
  374. if first != reflect.Float32 && first != reflect.Float64 {
  375. allFloats = false
  376. }
  377. for i := 0; i < ty.NumField(); i++ {
  378. f := ty.Field(i).Type
  379. if f.Kind() == reflect.Struct {
  380. var structNumFields int
  381. allFloats, structNumFields = isAllSameFloat(f)
  382. numFields += structNumFields
  383. continue
  384. }
  385. numFields++
  386. if f.Kind() != first {
  387. allFloats = false
  388. }
  389. }
  390. return allFloats, numFields
  391. }
  392. func checkStructFieldsSupported(ty reflect.Type) {
  393. for i := 0; i < ty.NumField(); i++ {
  394. f := ty.Field(i).Type
  395. if f.Kind() == reflect.Array {
  396. f = f.Elem()
  397. } else if f.Kind() == reflect.Struct {
  398. checkStructFieldsSupported(f)
  399. continue
  400. }
  401. switch f.Kind() {
  402. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
  403. reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
  404. reflect.Uintptr, reflect.Ptr, reflect.UnsafePointer, reflect.Float64, reflect.Float32:
  405. default:
  406. panic(fmt.Sprintf("purego: struct field type %s is not supported", f))
  407. }
  408. }
  409. }
  410. func roundUpTo8(val uintptr) uintptr {
  411. return (val + 7) &^ 7
  412. }
  413. func numOfIntegerRegisters() int {
  414. switch runtime.GOARCH {
  415. case "arm64":
  416. return 8
  417. case "amd64":
  418. return 6
  419. default:
  420. // since this platform isn't supported and can therefore only access
  421. // integer registers it is fine to return the maxArgs
  422. return maxArgs
  423. }
  424. }