detached 1e7d6d6

purego: support string arguments in Go callbacks from C

commit
1e7d6d6fac76affcbf0d2f7ba9765e54c8bd76e9
author
brice · · 2026-04-15 03:32 UTC
committer
brice · · 2026-09-27 05:38 UTC
parent
a44f95e
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +27 -1
M syscall_unix.go +27 -1
@@ -11,6 +11,8 @@ import (
1111 "runtime"
1212 "sync"
1313 "unsafe"
14+
15+ "github.com/bnema/purego/internal/strings"
1416 )
1517
1618 var syscallXABI0 uintptr
@@ -78,7 +80,7 @@ func compileCallback(fn any) uintptr {
7880 continue
7981 case reflect.Interface, reflect.Func, reflect.Slice,
8082 reflect.Chan, reflect.Complex64, reflect.Complex128,
81- reflect.String, reflect.Map, reflect.Invalid:
83+ reflect.Map, reflect.Invalid:
8284 panic("purego: unsupported argument type: " + in.Kind().String())
8385 }
8486 }
@@ -230,6 +232,30 @@ func callbackWrap(a *callbackArgs) {
230232 }
231233 }
232234 floatsN += slots
235+ case reflect.String:
236+ slots = 1
237+ var ptr uintptr
238+ if intsN+slots > numOfIntegerRegisters() {
239+ if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
240+ ptr = uintptr(callbackArgFromStack(a.args, stackSlot, &stackByteOffset, reflect.TypeOf(uintptr(0))).Uint())
241+ } else if stackFrame != nil {
242+ if runtime.GOARCH == "s390x" {
243+ ptr = uintptr(callbackArgFromSlotBigEndian(unsafe.Pointer(&stackFrame[stackSlot]), reflect.TypeOf(uintptr(0))).Uint())
244+ } else {
245+ ptr = *(*uintptr)(unsafe.Pointer(&stackFrame[stackSlot]))
246+ }
247+ stackSlot += slots
248+ } else {
249+ ptr = frame[stackSlot]
250+ stackSlot += slots
251+ }
252+ } else {
253+ pos := intsN + numOfFloatRegisters()
254+ ptr = frame[pos]
255+ }
256+ intsN += slots
257+ args[i] = reflect.ValueOf(strings.GoString(ptr))
258+ continue
233259 case reflect.Struct:
234260 if i == 0 && inType.AssignableTo(reflect.TypeFor[CDecl]()) {
235261 args[i] = reflect.Zero(inType)
@@ -11,6 +11,8 @@ import (
11 "runtime"11 "runtime"
12 "sync"12 "sync"
13 "unsafe"13 "unsafe"
14+
15+ "github.com/bnema/purego/internal/strings"
14 )16 )
15 17
16 var syscallXABI0 uintptr18 var syscallXABI0 uintptr
@@ -78,7 +80,7 @@ func compileCallback(fn any) uintptr {
78 continue80 continue
79 case reflect.Interface, reflect.Func, reflect.Slice,81 case reflect.Interface, reflect.Func, reflect.Slice,
80 reflect.Chan, reflect.Complex64, reflect.Complex128,82 reflect.Chan, reflect.Complex64, reflect.Complex128,
81- reflect.String, reflect.Map, reflect.Invalid:83+ reflect.Map, reflect.Invalid:
82 panic("purego: unsupported argument type: " + in.Kind().String())84 panic("purego: unsupported argument type: " + in.Kind().String())
83 }85 }
84 }86 }
@@ -230,6 +232,30 @@ func callbackWrap(a *callbackArgs) {
230 }232 }
231 }233 }
232 floatsN += slots234 floatsN += slots
235+ case reflect.String:
236+ slots = 1
237+ var ptr uintptr
238+ if intsN+slots > numOfIntegerRegisters() {
239+ if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
240+ ptr = uintptr(callbackArgFromStack(a.args, stackSlot, &stackByteOffset, reflect.TypeOf(uintptr(0))).Uint())
241+ } else if stackFrame != nil {
242+ if runtime.GOARCH == "s390x" {
243+ ptr = uintptr(callbackArgFromSlotBigEndian(unsafe.Pointer(&stackFrame[stackSlot]), reflect.TypeOf(uintptr(0))).Uint())
244+ } else {
245+ ptr = *(*uintptr)(unsafe.Pointer(&stackFrame[stackSlot]))
246+ }
247+ stackSlot += slots
248+ } else {
249+ ptr = frame[stackSlot]
250+ stackSlot += slots
251+ }
252+ } else {
253+ pos := intsN + numOfFloatRegisters()
254+ ptr = frame[pos]
255+ }
256+ intsN += slots
257+ args[i] = reflect.ValueOf(strings.GoString(ptr))
258+ continue
233 case reflect.Struct:259 case reflect.Struct:
234 if i == 0 && inType.AssignableTo(reflect.TypeFor[CDecl]()) {260 if i == 0 && inType.AssignableTo(reflect.TypeFor[CDecl]()) {
235 args[i] = reflect.Zero(inType)261 args[i] = reflect.Zero(inType)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close