Repository: go
Issue: cmd/cgo: miscalculates C array offset when C struct has __attribute__((__packed__))
Labels: help wanted,NeedsFix,compiler/runtime

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version go1.16.5 linux/amd64
</pre>

### Does this issue reproduce with the latest release?

Yes.

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/yoursunny/.cache/go-build"
GOENV="/home/yoursunny/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/yoursunny/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/yoursunny/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.16.5"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2656352705=/tmp/go-build -gno-record-gcc-switches"
</pre></details>

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:

```go
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?

```shell
$ 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?

```shell
$ 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:

```c
typedef struct P
{
	uint64_t a;
	uint64_t b;
	uint8_t c;
} __attribute__((__packed__)) P;
```

It also causes cgo to misbehave:

```shell
$ 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
```