linux/arm struct alignment seems wrong

197 views
Skip to first unread message

Jan Mercl

unread,
Sep 9, 2020, 6:20:34 AM9/9/20
to golang-nuts
Observation:

pi@raspberrypi:~/src/tmp.tmp $ go version
go version go1.15.1 linux/arm
pi@raspberrypi:~/src/tmp.tmp $ cat main.go
package main

/*

struct s {
        long long i;
} x;

size_t align() {
        return _Alignof(struct s);
}

*/
import "C"

import (
        "fmt"
        "unsafe"
)

type S struct {
        i int64
}

func main() {
        fmt.Printf(" C alignof struct s: %v\n", C.align())
        fmt.Printf("Go alignof struct s: %v\n", unsafe.Alignof(C.struct_s{}))
        fmt.Printf("Go alignof        S: %v\n", unsafe.Alignof(S{}))
}
pi@raspberrypi:~/src/tmp.tmp $ go run main.go
 C alignof struct s: 8
Go alignof struct s: 4
Go alignof        S: 4
pi@raspberrypi:~/src/tmp.tmp $ 
uname -a
Linux raspberrypi 4.19.66-v7+ #1253 SMP Thu Aug 15 11:49:46 BST 2019 armv7l GNU/Linux
pi@raspberrypi:~/src/tmp.tmp $

My code relies on all the numbers being the same, ie. that Go will report the same as C for both C.struct_s{} and S{}.
AFAICT Go and C agree on struct layout on linux/{amd64,386} perfectly in all cases I've tested (thousands probably).

Is this a bug or are my expectations ill founded?

Thanks in advance for any insights.

Dan Kortschak

unread,
Sep 9, 2020, 6:42:08 AM9/9/20
to Jan Mercl, golang-nuts
I get the following

```
C alignof struct s: 8
Go alignof struct s: 8
Go alignof S: 8
~/cznic $ go version
go version go1.15.1 linux/arm64
~/cznic $ uname -a
Linux bildr 4.19.0-10-arm64 #1 SMP Debian 4.19.132-1 (2020-07-24)
aarch64 GNU/Linux
```

I have an arm64 linux running on my pi, maybe the armv7l behaviour is
different.

Dan


Jan Mercl

unread,
Sep 9, 2020, 6:52:34 AM9/9/20
to Dan Kortschak, golang-nuts
On Wed, Sep 9, 2020 at 12:41 PM Dan Kortschak <d...@kortschak.io> wrote:

> I get the following
>
> ```
> C alignof struct s: 8
> Go alignof struct s: 8
> Go alignof S: 8
> ~/cznic $ go version
> go version go1.15.1 linux/arm64
> ~/cznic $ uname -a
> Linux bildr 4.19.0-10-arm64 #1 SMP Debian 4.19.132-1 (2020-07-24)
> aarch64 GNU/Linux
> ```
>
> I have an arm64 linux running on my pi, maybe the armv7l behaviour is
> different.

Seems to be the case as armv7l is AFAIK a 32 bit only CPU. And arm64
is next on the list of my targets so thank you for the information.

But the question is still the same. Is it a bug or is my assumption
about Go and C agreeing on alignments/offsets invalid? I realizethe
specs say nothing in this regard.

Dan Kortschak

unread,
Sep 9, 2020, 7:10:11 AM9/9/20
to golang-nuts
What does cgo -godefs give you? On my amd64 and arm64 I get this:

```
~/cznic $ cat main.go
package main

/*
struct s {
long long i;
} x;
*/
import "C"

type S struct {
i int64
}

type C_s C.struct_s

~/cznic $ go tool cgo -godefs main.go
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs main.go

package main

type S struct {
i int64
}

type C_s struct {
I int64
}
```


Jan Mercl

unread,
Sep 9, 2020, 7:22:34 AM9/9/20
to Dan Kortschak, golang-nuts


On Wed, Sep 9, 2020 at 1:09 PM 'Dan Kortschak' via golang-nuts <golan...@googlegroups.com> wrote:

> What does cgo -godefs give you? On my amd64 and arm64 I get this:
>
> ```
> ~/cznic $ cat main.go
> package main
>
> /*
> struct s {
>         long long i;
> } x;
> */
> import "C"
>
> type S struct {
>         i int64
> }
>
> type C_s C.struct_s
>
> ~/cznic $ go tool cgo -godefs main.go
> // Code generated by cmd/cgo -godefs; DO NOT EDIT.
> // cgo -godefs main.go
>
> package main
>
> type S struct {
>         i int64
> }
>
> type C_s struct {
>         I int64
> }
> ```

amd64 =========================================================================

jnml@e5-1650:~/tmp$ go version
go version go1.15.1 linux/amd64
jnml@e5-1650:~/tmp$ cat main.go
package main

/*
struct s {
        long long i;
} x;
*/
import "C"

type S struct {
        i int64
}

type C_s C.struct_s
jnml@e5-1650:~/tmp$ go tool cgo -godefs main.go

// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs main.go

package main

type S struct {
        i int64
}

type C_s struct {
        I int64
}
jnml@e5-1650:~/tmp$

arm ===========================================================================

pi@raspberrypi:~/src/tmp.tmp $ go version
go version go1.15.1 linux/arm
pi@raspberrypi:~/src/tmp.tmp $ cat main.go

package main

/*
struct s {
        long long i;
} x;
*/
import "C"

type S struct {
        i int64
}

type C_s C.struct_s
pi@raspberrypi:~/src/tmp.tmp $ go tool cgo -godefs main.go

// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs main.go

package main

type S struct {
        i int64
}

type C_s struct {
        I int64
}
pi@raspberrypi:~/src/tmp.tmp $

No difference between the outputs AFAICT. Seems good to me.


Dan Kortschak

unread,
Sep 9, 2020, 7:45:36 AM9/9/20
to golang-nuts
I think it comes down to these lines in src/cmd/internal/sys/arch.go
[1]

```
var ArchARM = &Arch{
Name: "arm",
Family: ARM,
ByteOrder: binary.LittleEndian,
PtrSize: 4,
RegSize: 4,
MinLC: 4,
}

var ArchARM64 = &Arch{
Name: "arm64",
Family: ARM64,
ByteOrder: binary.LittleEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 4,
}
```
this line in src/cmd/compile/internal/gc/main.go [2]
```
Widthreg = thearch.LinkArch.RegSize
```

and these lines in src/cmd/compile/internal/gc/align.go [3]
```
case TINT64, TUINT64, TFLOAT64:
w = 8
t.Align = uint8(Widthreg)
```

[1]https://golang.org/src/cmd/internal/sys/arch.go
[2]https://golang.org/src/cmd/compile/internal/gc/main.go
[3]https://golang.org/src/cmd/compile/internal/gc/align.go


Jan Mercl

unread,
Sep 9, 2020, 7:59:59 AM9/9/20
to Dan Kortschak, golang-nuts
Thank you very much for the thorough investigation.

If the intent is to have Go alignments/offsets of types compatible
with the C ABI then I think it's safe to say this is a bug.

And the existence of the syscall package, in some cases passing
Go-defined structs to the kernel that must be binary compatible with
their C definitions, IMO suggests that the intent is indeed there.

Ian Lance Taylor

unread,
Sep 9, 2020, 2:18:48 PM9/9/20
to Jan Mercl, Dan Kortschak, golang-nuts
On Wed, Sep 9, 2020 at 4:59 AM Jan Mercl <0xj...@gmail.com> wrote:
>
> If the intent is to have Go alignments/offsets of types compatible
> with the C ABI then I think it's safe to say this is a bug.
>
> And the existence of the syscall package, in some cases passing
> Go-defined structs to the kernel that must be binary compatible with
> their C definitions, IMO suggests that the intent is indeed there.

Exact alignment/offset compatibility with the C ABI is not a goal.
Sorry. (It's actually harder than one might think to maintain that
kind of compatibility. For example, on x86, the C ABI uses one
alignment for double variables and a different alignment for double
variables that appear as a field in a struct. Unless, of course, you
use the -malign-double option. And more generally some platforms have
multiple C ABIs, including x86 if you count the MCU psABI.)

The Go structs in the syscall package that need to match C structs are
carefully written to work correctly.

You may find the cgo -godefs option to be helpful, as it provides Go
structs that exactly match C structs, given a particular set of
compiler options.

Ian

Jan Mercl

unread,
Sep 9, 2020, 4:42:19 PM9/9/20
to Ian Lance Taylor, Dan Kortschak, golang-nuts
On Wed, Sep 9, 2020 at 8:17 PM Ian Lance Taylor <ia...@golang.org> wrote:

> Exact alignment/offset compatibility with the C ABI is not a goal.
> Sorry. (It's actually harder than one might think to maintain that
> kind of compatibility. For example, on x86, the C ABI uses one
> alignment for double variables and a different alignment for double
> variables that appear as a field in a struct. Unless, of course, you
> use the -malign-double option. And more generally some platforms have
> multiple C ABIs, including x86 if you count the MCU psABI.)
>
> The Go structs in the syscall package that need to match C structs are
> carefully written to work correctly.
>
> You may find the cgo -godefs option to be helpful, as it provides Go
> structs that exactly match C structs, given a particular set of
> compiler options.

Thanks a lot for the clarification. Assuming that IIRC, cgo -godefs
acquire the alignment/offset info by invoking the C compiler, it's
unfortunately not a good option for a project that aims to avoid CGo
in the first place. But I now think there's a feasible solution to
this problem, having not as high a cost as I feared first when reading
your answer. I shall see tomorrow.

Thanks again.
Reply all
Reply to author
Forward
0 new messages