Repository: go
Issue: gccgo: compile cgo got error in aarch64, clang, gold (or lld)
Labels: NeedsInvestigation

<!--
Please answer these questions before submitting your issue. Thanks!
-->

### What version of Go are you using (`go version`)?

<pre>
$ go version
</pre>

```
# go version
go version go1.19 linux/arm64

# gccgo --version
gccgo (GCC) 12.1.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```



### Does this issue reproduce with the latest release?

I didn't test golang/go master branch, but there should be.

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GO111MODULE="off"
GOARCH="arm64"
GOHOSTARCH="arm64"
GOHOSTOS="linux"
GOOS="linux"
GOROOT="/usr/GoRelease/go1.19"
GOTOOLDIR="/usr/GoRelease/go1.19/pkg/tool/linux_arm64"
GOVERSION="go1.19"
GCCGO="/usr/gcc-12.1.0-install/bin/gccgo"
CC="/usr/llvm/bin/clang"
CXX="/usr/llvm/bin/clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
GOGCCFLAGS="-fPIC -pthread -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1375830659=/tmp/go-build -gno-record-gcc-switches
</pre>
</details>

Linux kernel 4.19.90 aarch64



### What did you do?

<!--
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on go.dev/play is best.
-->

A simple cgo code:

```
// cgo.go
package main

// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s) {
//   printf("%s\n", s);
// }
import "C"
import (
	"unsafe"
)

func main() {
	_Cfunc := C.CString("Hello from stdio")
	C.myprint(_Cfunc)
	C.free(unsafe.Pointer(_Cfunc))
}
```

export GCCGO="/usr/gcc-12.1.0-install/bin/gccgo"
export CC="/usr/llvm/bin/clang"
export CXX="/usr/llvm/bin/clang++"

(LD_LIBRARY_PATH also needs to be set)

(1) If **not** use gccgo, these commands all work properly.

```bash
go build -a cgo.go
go build -a -ldflags="-extld=clang -extldflags=-fuse-ld=gold" cgo.go
go build -a -ldflags="-extld=clang -extldflags=-fuse-ld=lld" cgo.go
```

(2) If use gccgo, but use ld linker (default is ld) , these commands also work properly.

```bash
go build -a -compiler=gccgo cgo.go
go build -a -compiler=gccgo -gccgoflags="-fuse-ld=bfd" cgo.go
```

(3) If use gccgo, but use gold or lld linker, got some error:

```bash
# go build -compiler=gccgo -gccgoflags="-fuse-ld=gold" cgo.go
# command-line-arguments
/usr/bin/ld.gold: error: linker does not include stack split support required by $WORK/b001/_pkg1_.a(_cgo_defun.o)
collect2: error: ld returned 1 exit status
```

```bash
# go build -compiler=gccgo -gccgoflags="-fuse-ld=lld" cgo.go
# command-line-arguments
ld.lld: error: Mixing split-stack objects requires a definition of __morestack_non_split
ld.lld: error: Mixing split-stack objects requires a definition of __morestack_non_split
collect2: error: ld returned 1 exit status
```

I found some split-stack checks in the code.

Position 1:

In [src/cmd/go/internal/work/exec.go#L2816](https://github.com/golang/go/blob/5531838c046eb1a94b142deeb89d7d8d9be3fc14/src/cmd/go/internal/work/exec.go#L2816) :

```go
	if cfg.BuildToolchainName == "gccgo" {
		if b.gccSupportsFlag([]string{BuildToolchain.compiler()}, "-fsplit-stack") {
			cgoCFLAGS = append(cgoCFLAGS, "-fsplit-stack")
		}
		cgoflags = append(cgoflags, "-gccgo")
		if pkgpath := gccgoPkgpath(p); pkgpath != "" {
			cgoflags = append(cgoflags, "-gccgopkgpath="+pkgpath)
		}
	}
```

The `BuildToolchain.compiler()` is  `/usr/gcc-12.1.0-install/bin/gccgo`, so here is check whether `gccgo` support `-fsplit-stack`.

```bash
# /usr/gcc-12.1.0-install/bin/gccgo -fsplit-stack -c -x c - -o /dev/null
cc1: error: ‘-fsplit-stack’ is not supported by this compiler configuration
```

So, `gccgo` does not support `-fsplit-stack` in my env.

Position 2:

In [src/cmd/go/internal/work/gccgo.go#L569](https://github.com/golang/go/blob/5531838c046eb1a94b142deeb89d7d8d9be3fc14/src/cmd/go/internal/work/gccgo.go#L569):

```go
compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
	if b.gccSupportsFlag(compiler, "-fsplit-stack") {
		defs = append(defs, "-fsplit-stack")
	}
```

The variable `compiler[0]` is `${CC}`, in my env, it was `/usr/llvm/bin/clang`. so here is check whether `clang` support `-fsplit-stack`.

```bash
# /usr/llvm/bin/clang -fsplit-stack -c -x c - -o /dev/null
```

No errors, which means that `clang` supports `-fsplit-stack` in my env.

Therefore, from the above results, I guess:
The gccgo compile go code in **non**-split-stack mode.
And the clang compile c code in split-stack mode.
Finally, the gold or lld linker reports an error when linking the two object files.

I see the [comments in gollvm](https://go.googlesource.com/gollvm/+/refs/heads/master/cmake/modules/AddGollvm.cmake#35) :

```cmake
  # For amd64, as gcc, clang, ld and ld.gold all support -fsplit-stack, so all
  # going well. For arm64, the situation is very complicated, none of gcc, ld
  # and ld.gold support this option, but clang does. When using clang compiler
  # and ld linker, the test passes, but in fact ld does not support stack
  # splitting. So here we do this test with ld.gold linker.
  # FIXME: update here once one day there is a linker that supports '-fsplit-stack'
  # on arm64.
```

The preceding test results comply with the description here.

So, in aarch64, I can't use gccgo with clang and gold (or lld).

I have two ideas for a fix.

① If it is detected that the `gccgo` does not support `split-stack`, `${CC}` does not check whether it supports split-stack and directly uses the **non**-split-stack mode. On the other hand, if it is checked that `${CC}` does not support split-stack, then `gccgo` is also directly in **non**-split-stack mode.

② Modify the current split-stack checking method, which does not take the linker into account.

If a linker is used, `${CC}` may detect that split-stack is not supported so that it is consistent with `gccgo`.

What do you think?

### What did you expect to see?

When use clang, gold (or lld) in aarch64, gccgo compile and run cgo successfully in split-stack or **non**-split-stack mode.

### What did you see instead?

gold or lld reports an error.