golang/go

cmd/cgo: miscalculates C array offset when C struct has __attribute__((__packed__))

Open

#46,675 opened on Jun 9, 2021

View on GitHub
 (4 comments) (0 reactions) (0 assignees)Go (19,008 forks)batch import
NeedsFixcompiler/runtimehelp wanted

Repository metrics

Stars
 (133,883 stars)
PR merge metrics
 (No merged PRs in 30d)

Description

What version of Go are you using (go version)?

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

C compiler: gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 on Ubuntu 18.04

What did you do?

I execute go run x.go with the following input file:

package main

/*
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct P
{
	uint64_t a;
	struct {
		uint64_t b;
		uint8_t c;
	} __attribute__((__packed__));
} P;

typedef struct Q
{
	P d[3];
} Q;

void show(const Q* q)
{
	printf("sizeof(P)=%zu sizeof(Q)=%zu\n", sizeof(P), sizeof(Q));
	for (int i = 0; i < sizeof(q->d) / sizeof(q->d[0]); ++i) {
		const P* p = &q->d[i];
		printf("[%d] a=%016"PRIx64" b=%016"PRIx64" c=%02"PRIx8"\n", i, p->a, p->b, p->c);
	}
}
*/
import "C"
import (
	"fmt"
	"unsafe"
)

func main() {
	var q C.Q
	q.d[0].a = 0xA0A0A0A0A0A0A0A0
	q.d[1].a = 0xA1A1A1A1A1A1A1A1
	q.d[2].a = 0xA2A2A2A2A2A2A2A2
	fmt.Println(unsafe.Sizeof(C.P{}), unsafe.Sizeof(C.Q{}))
	fmt.Println(q)
	C.show(&q)
}

What did you expect to see?

$ go run x.go 
24 72
{[{11574427654092267680 {0 0} [0 0 0 0 0 0 0]} {11646767826930344353 {0 0} [0 0 0 0 0 0 0]} {11719107999768421026 {0 0} [0 0 0 0 0 0 0]}]}
sizeof(P)=24 sizeof(Q)=72
[0] a=a0a0a0a0a0a0a0a0 b=0000000000000000 c=00
[1] a=a1a1a1a1a1a1a1a1 b=0000000000000000 c=00
[2] a=a2a2a2a2a2a2a2a2 b=0000000000000000 c=00

What did you see instead?

$ go run x.go 
32 96
{[{11574427654092267680 {0 0} [0 0 0 0 0 0 0]} {11646767826930344353 {0 0} [0 0 0 0 0 0 0]} {11719107999768421026 {0 0} [0 0 0 0 0 0 0]}]}
sizeof(P)=24 sizeof(Q)=72
[0] a=a0a0a0a0a0a0a0a0 b=0000000000000000 c=00
[1] a=0000000000000000 b=a1a1a1a1a1a1a1a1 c=00
[2] a=0000000000000000 b=0000000000000000 c=a2

Notice that cgo and gcc reports different size for C structs.

Additional Information

If struct P is changed to:

typedef struct P
{
	uint64_t a;
	uint64_t b;
	uint8_t c;
} __attribute__((__packed__)) P;

It also causes cgo to misbehave:

$ go run x.go 
24 72
{[{11574427654092267680 0 0} {11646767826930344353 0 0} {11719107999768421026 0 0}]}
sizeof(P)=17 sizeof(Q)=51
[0] a=a0a0a0a0a0a0a0a0 b=0000000000000000 c=00
[1] a=a100000000000000 b=00a1a1a1a1a1a1a1 c=00
[2] a=0000000000000000 b=a2a2000000000000 c=a2

Contributor guide