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

$ go version
go version go1.20.2 darwin/arm64

Does this issue reproduce with the latest release?

Yes

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

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/wsong/Library/Caches/go-build"
GOENV="/Users/wsong/Library/Application Support/go/env"
GOEXE=".exe"
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/wsong/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="windows"
GOPATH="/Users/wsong/go"
GOPRIVATE=""
GOPROXY="https://goproxy.cn"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.20.2"
GCCGO="gccgo"
GOAMD64="v1"
AR="x86_64-w64-mingw32-gcc"
CC="x86_64-w64-mingw32-gcc"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/wsong/Development/test/go.mod"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/var/folders/lr/s8_5072s6cs602ksv8tv5myh0000gn/T/go-build2849677897=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I have following files in my folder:

example/ go.mod   test.c   test.go
./example:
main.c
wsong@Work: ~/Development/test $ cat go.mod
module test

go 1.20
wsong@Work: ~/Development/test $ cat test.c
// Functions exported by Go.
extern int Test();

int test() {
    Test();
    return 0;
}
wsong@Work: ~/Development/test $ cat test.go
package main

//#include "errno.h"
import "C"

func main() {}

//export Test
func Test() C.int {
    return 0
}

wsong@Work: ~/Development/test $ cat example/main.c 
extern int test();

int main(void) {
    test();
    return 0;
}

I run go build like this:

CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -v -a -buildmode=c-archive .

Then go into example folder and run:

x86_64-w64-mingw32-gcc main.c ../test.a

What did you expect to see?

a.exe is generated.

What did you see instead?

/opt/homebrew/Cellar/mingw-w64/10.0.0_5/toolchain-x86_64/bin/x86_64-w64-mingw32-ld: /var/folders/lr/s8_5072s6cs602ksv8tv5myh0000gn/T//ccHcSIoc.o:main.c:(.text+0xe): undefined reference to `test' collect2: error: ld returned 1 exit status

Comment From: heschi

cc @golang/compiler

Comment From: cherrymui

What version of C toolchain do you use? Thanks.

cc @thanm

Comment From: awsong

It's mingw-w64 from homebrew, on M1 Mac.

wsong@Work: ~/Development/test/example $ x86_64-w64-mingw32-gcc -v
Using built-in specs.
COLLECT_GCC=x86_64-w64-mingw32-gcc
COLLECT_LTO_WRAPPER=/opt/homebrew/Cellar/mingw-w64/10.0.0_5/toolchain-x86_64/libexec/gcc/x86_64-w64-mingw32/12.2.0/lto-wrapper
Target: x86_64-w64-mingw32
Configured with: ../configure --target=x86_64-w64-mingw32 --with-sysroot=/opt/homebrew/Cellar/mingw-w64/10.0.0_5/toolchain-x86_64 --prefix=/opt/homebrew/Cellar/mingw-w64/10.0.0_5/toolchain-x86_64 --with-bugurl=https://github.com/Homebrew/homebrew-core/issues --enable-languages=c,c++,fortran --with-ld=/opt/homebrew/Cellar/mingw-w64/10.0.0_5/toolchain-x86_64/bin/x86_64-w64-mingw32-ld --with-as=/opt/homebrew/Cellar/mingw-w64/10.0.0_5/toolchain-x86_64/bin/x86_64-w64-mingw32-as --with-gmp=/opt/homebrew/opt/gmp --with-mpfr=/opt/homebrew/opt/mpfr --with-mpc=/opt/homebrew/opt/libmpc --with-isl=/opt/homebrew/opt/isl --with-zstd=no --disable-multilib --disable-nls --enable-threads=posix
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 12.2.0 (GCC) 

Comment From: thanm

@awsong Thanks for the report.

If you could dump out the symbols from your test.a and post it here, that would be helpful, e.g. something like

$ CGO_ENABLED=1 go build -buildmode=c-archive 
$ llvm-objdump-14 -t test.a | fgrep test | fgrep .text 
0000000000011200 l     F .text  00000000000000ef runtime.gcTrigger.test
000000000003ade0 l     F .text  00000000000001f0 runtime.testAtomic64
0000000000000000 g     F .text  000000000000000c test
$

Comment From: awsong

wsong@Work: ~/Development/test $ x86_64-w64-mingw32-objdump -t test.a | fgrep test | fgrep .text 
wsong@Work: ~/Development/test $ x86_64-w64-mingw32-objdump -t test.a | fgrep test 
In archive test.a:
[228](sec  1)(fl 0x00)(ty    0)(scl   2) (nx 0) 0x0000000000012900 runtime.gcTrigger.test
[697](sec  1)(fl 0x00)(ty    0)(scl   2) (nx 0) 0x000000000003e620 runtime.testAtomic64
[1544](sec  4)(fl 0x00)(ty    0)(scl   2) (nx 0) 0x0000000000053678 runtime.test_z64
[1545](sec  4)(fl 0x00)(ty    0)(scl   2) (nx 0) 0x0000000000053670 runtime.test_x64
[1569](sec  4)(fl 0x00)(ty    0)(scl   2) (nx 0) 0x00000000000534a9 runtime.testingWER
[  0](sec -2)(fl 0x00)(ty    0)(scl 103) (nx 1) 0x0000000000000000 test.cgo2.c
[  0](sec -2)(fl 0x00)(ty    0)(scl 103) (nx 1) 0x0000000000000000 test.c
[  2](sec  1)(fl 0x00)(ty   20)(scl   2) (nx 1) 0x0000000000000000 test

Comment From: thanm

Thanks. No red flags there, not immediately sure what the issue is. I'll work on setting up a test machine of some sort to see if I can repro. Stay tuned.

Comment From: thanm

OK, I think I see what the issue is. I'll send a CL tomorrow morning.

If you need a workaround for the time being, you can do this:

$ ARPATH=`x86_64-w64-mingw32-gcc --print-prog-name ar`
$ CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -ldflags="-extar=$ARPATH" -buildmode=c-archive .
$

Problem here is that the Go linker is invoking the system "ar" instead of the cross-compiler's "ar".

Comment From: awsong

Verified that the workaround works on my system.

Thanks

Comment From: gopherbot

Change https://go.dev/cl/479775 mentions this issue: cmd/link: use "ar" path derived from querying CC for -buildmode=c-archive

Comment From: thanm

Hmm, I've had to send a revert for https://go.dev/cl/479775 since it causes a failure on our ios-arm64-corellium builder... hopefully after I figure out what is going on I can resubmit CL 479775.

Comment From: gopherbot

Change https://go.dev/cl/480675 mentions this issue: env/corellium/ios: special case "--print-prog-name=ar" in clang wrapper

Comment From: gopherbot

Change https://go.dev/cl/488576 mentions this issue: cmd/link: use path from "cc --print-prog-name ar" for c-archive buildmode

Comment From: mknyszek

Hey @thanm, any update here? Do you still plan to do this for Go 1.21? Thanks.

Comment From: thanm

I've moved to backlog. The main sticking points here are the problems with the ios correllium builders-- needs more investigation as to why the change doesn't work with their custom wrappers.

Comment From: cherrymui

If we want to do something for this cycle, we could do it only for Windows.

Comment From: gopherbot

Change https://go.dev/cl/592375 mentions this issue: cmd/link: use path from "cc --print-prog-name ar" for c-archive buildmode