PureGo Version
v0.10.0-alpha.2
Operating System
Go Version (go version)
1.25
What steps will reproduce the problem?
type FourInt32s struct {
f0, f1, f2, f3 int32
}
// C function
int32_t FourInt32sSum(struct FourInt32s s) {
return s.f0 + s.f1 + s.f2 + s.f3;
}
// Go call
var fn func(FourInt32s) int32
purego.RegisterLibFunc(&fn, lib, "FourInt32sSum")
sum := fn(FourInt32s{1, 2, 3, 4})
// Expected: 10 (1+2+3+4)
// Actual: 12 (1+2+3+6) - f3 corrupted from 4 to 6
What is the expected result?
// Expected: 10 (1+2+3+4)
What happens instead?
// Actual: 12 (1+2+3+6) - f3 corrupted from 4 to 6
Anything else you feel useful to add?
The Bug Chain
-
isHVA() returns true for FourInt32s
- Located in
struct_arm64.go:258-284
- Checks if struct has homogeneous integer fields (line 266:
case reflect.Int32)
- Returns
true for any struct with all int32 fields, size 8 or 16 bytes
-
addStruct() calls placeRegisters() for HVAs
- Located in
struct_arm64.go:73
- Condition:
hva || hfa || size <= 16
- Since
isHVA() returns true, it calls placeRegisters()
-
placeRegisters() uses placeRegistersAArch64() logic
- Located in
struct_arm64.go:89-198 (on non-Darwin ARM64)
- Uses field-by-field bit-shifting and OR operations
- For
int32 fields (line 155-158):
case reflect.Int32:
val |= uint64(f.Int()&0xFFFF_FFFF) << shift
shift += 32
class |= _INT
-
Bit-shifting overflow corrupts the 4th field
- After field 0 (f0=1):
val = 0x0000000000000001, shift = 32
- After field 1 (f1=2):
val = 0x0000000200000001, shift = 64
- After field 2 (f2=3):
shift = 64 causes flush, new val = 0x0000000000000003, shift = 32
- After field 3 (f3=4):
shift = 32, so val |= 4 << 32 = 0x0000000400000003
- But this is wrong! The flush happened too late, causing f3 to OR with f1's position
- Result: Second register contains
0x0000000600000003 instead of 0x0000000400000003
Memory Layout Evidence
Expected (byte-level packing):
Chunk 0 (X0): 0x0000000200000001 (f1:f0 = 2:1)
Chunk 1 (X1): 0x0000000400000003 (f3:f2 = 4:3)
Actual (with bug):
Chunk 0 (X0): 0x0000000200000001 (f1:f0 = 2:1)
Chunk 1 (X1): 0x0000000600000003 (f3:f2 = 6:3) <- f3 corrupted!
C side receives:
byte[12] = 0x06 // Should be 0x04
// Interpreted as f3 = 6 instead of 4
PureGo Version
v0.10.0-alpha.2
Operating System
Go Version (
go version)1.25
What steps will reproduce the problem?
What is the expected result?
// Expected: 10 (1+2+3+4)
What happens instead?
// Actual: 12 (1+2+3+6) - f3 corrupted from 4 to 6
Anything else you feel useful to add?
The Bug Chain
isHVA()returnstrueforFourInt32sstruct_arm64.go:258-284case reflect.Int32)truefor any struct with allint32fields, size 8 or 16 bytesaddStruct()callsplaceRegisters()for HVAsstruct_arm64.go:73hva || hfa || size <= 16isHVA()returnstrue, it callsplaceRegisters()placeRegisters()usesplaceRegistersAArch64()logicstruct_arm64.go:89-198(on non-Darwin ARM64)int32fields (line 155-158):Bit-shifting overflow corrupts the 4th field
val = 0x0000000000000001,shift = 32val = 0x0000000200000001,shift = 64shift = 64causes flush, newval = 0x0000000000000003,shift = 32shift = 32, soval |= 4 << 32 = 0x00000004000000030x0000000600000003instead of0x0000000400000003Memory Layout Evidence
Expected (byte-level packing):
Actual (with bug):
C side receives: