Skip to content

Conversation

@tmc
Copy link
Contributor

@tmc tmc commented Oct 30, 2025

What issue is this addressing?

Extends struct argument support to Linux amd64 and arm64 platforms.

What type of issue is this addressing?

enhancement

What this PR does | solves

This PR removes the Darwin-only restriction on struct arguments and enables struct-by-value support on Linux for both amd64 and arm64 architectures.

Closes #236

@hajimehoshi
Copy link
Member

Write an issue number this PR works on.

@TotallyGamerJet
Copy link
Collaborator

Write an issue number this PR works on.

I believe this closes #236

@hajimehoshi
Copy link
Member

Thanks. Note that, as #236 is a new feature rather than a bug fix, I'm not going to backport this fix to the stable branch.

func.go Outdated
panic("purego: struct arguments are only supported on darwin amd64 & arm64")
// Struct arguments are supported on:
// - darwin amd64 & arm64
// - linux amd64 & arm64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment is redundant. Create a utility function like isStructSupportedForRegisterFunc and use it.

func.go Outdated
panic("purego: struct return values only supported on darwin arm64 & amd64")
// Struct return values are supported on:
// - darwin amd64 & arm64
// - linux amd64 & arm64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggested function isStructSupportedForRegisterFunc should also be available here

tmc added 5 commits October 31, 2025 11:31
Expands struct argument and return value support from Darwin-only to both
Darwin and Linux on amd64 and arm64 architectures. This change enables
purego to handle struct marshalling on additional platforms while
maintaining the existing safety checks for unsupported architectures.

Updates platform validation logic to check both GOOS and GOARCH, ensuring
struct support is only enabled on tested platform combinations. The build
constraints for struct tests are updated accordingly to include Linux.

Adds test coverage for pointer wrapper structs to validate proper handling
of single and multiple pointer fields within structs, ensuring correct
register allocation and marshalling across the expanded platform support.
Addresses platform-specific differences in struct argument tests between
Darwin and Linux by using consistent data types and avoiding sign
extension issues.

Changes the C struct field from int8_t to char for better cross-platform
consistency, and updates test values to use all positive numbers to avoid
sign extension differences between platforms. Also simplifies the build
constraint logic for better readability.

These changes ensure struct tests pass reliably on both Darwin and Linux
while maintaining the same test coverage.
Refactors RegisterFunc to use a shared isStructSupportedForRegisterFunc helper
function instead of duplicating the platform validation logic for both struct
arguments and return values. This improves code maintainability while
preserving the same validation behavior.
Changes the Array4Chars struct field from char to signed char to ensure
consistent signedness across platforms. The signedness of plain char is
implementation-defined and can vary between compilers and platforms,
potentially causing test inconsistencies.

Using signed char makes the signedness explicit and ensures consistent
behavior in struct marshalling tests across Darwin and Linux platforms.
@tmc tmc force-pushed the linux-struct-support branch from 29b5a8f to 3c62bd1 Compare October 31, 2025 18:36
@tmc
Copy link
Contributor Author

tmc commented Oct 31, 2025

rebased and I believe have addressed outstanding comments

return (uintptr_t)wrapper.ctx;
}

// Two pointer struct - tests register allocation for multiple pointer fields
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is redundant.

return s.f0 + s.f1 + s.f2 + s.f3;
}

// Simple pointer wrapper struct - tests single pointer field
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is redundant.

struct_test.go Outdated
var AddPointers func(wrapper TwoPointers) uintptr
purego.RegisterLibFunc(&AddPointers, lib, "AddPointers")

// Use actual allocated memory to satisfy checkptr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is checkptr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as in go tool compile -dcheckptr which go test -race sets:
go tool compile -d help

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but I think this can be simplified, 1m.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm yeah actually this causes go vet failures like this:

./struct_test.go:510:48: possible misuse of unsafe.Pointer
./struct_test.go:525:37: possible misuse of unsafe.Pointer
./struct_test.go:525:59: possible misuse of unsafe.Pointer

tmc added 3 commits October 31, 2025 11:54
Removes descriptive comments from PointerWrapper and TwoPointers struct
tests in both Go and C code as the test purpose is clear from context
and variable names.
Replaces dynamic memory allocation with constant uintptr values in
PointerWrapper and TwoPointers tests. This eliminates the need for
runtime.KeepAlive calls and makes the tests more deterministic by
using fixed pointer values instead of heap-allocated objects.
Uses stack-allocated variables instead of uintptr constants to fix go vet
warnings about misuse of unsafe.Pointer. The new approach is simpler than
heap allocation (no runtime.KeepAlive needed) while still being correct.
Copy link
Member

@hajimehoshi hajimehoshi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@TotallyGamerJet Please take a look

purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars")
const expectedSum = 1 + 2 + 4 + 8
if ret := Array4CharsFn(Array4Chars{a: [...]int8{1, 2, 4, 8}}); ret != expectedSum {
const expectedSum = 123
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional:

const expectedSum = 10 + 20 + 30 + 63

@JupiterRider
Copy link
Contributor

JupiterRider commented Nov 1, 2025

Are there any limitations like nested structs, float32 struct members, or the order of the struct members?

@tmc
Copy link
Contributor Author

tmc commented Nov 2, 2025

Are there any limitations like nested structs, float32 struct members, or the order of the struct members?

I don't think so -- certainly welcome including more edge cases in tests.

JupiterRider added a commit to JupiterRider/purego that referenced this pull request Nov 8, 2025
@JupiterRider
Copy link
Contributor

JupiterRider commented Nov 8, 2025

@tmc I created a new testcase which fails:

=== RUN   TestRegisterFunc_structReturns
    struct_test.go:537: Build /tmp/TestRegisterFunc_structReturns467303311/001/structreturntest.so
    struct_test.go:802: ReturnMixed5 returned {a:0xc00032c1b0 b:1 c:0 d:1088841318} wanted {a:0xc00032c1b0 b:1 c:7.2 d:9}
--- FAIL: TestRegisterFunc_structReturns (0.08s)

I also noticed that string and bool struct members are not supported:

panic: purego: unsupported kind string
panic: purego: unsupported kind bool

@tmc
Copy link
Contributor Author

tmc commented Nov 8, 2025

Thanks, I'll get that incorporated and addressed (at least the bool case).

Extends struct field type support to include boolean fields by adding
reflect.Bool to the list of supported types in checkStructFieldsSupported.

Adds comprehensive test coverage for boolean fields in struct returns,
including Mixed5 (with pointer and boolean), SmallBool (small struct with
boolean), and LargeBool (large struct with boolean) test cases. The C
implementations validate proper marshalling of boolean values across
different struct layouts and sizes.
@tmc
Copy link
Contributor Author

tmc commented Nov 8, 2025

string handling is a bit more involved and I think that should be done in a follow-on commit but I have bool support in now.

@tmc
Copy link
Contributor Author

tmc commented Nov 8, 2025

note: I have string support in https://github.com/tmc/purego/tree/add-string-field-support -- will open a PR for it after this goes in

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Struct support on Linux

4 participants