golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
124.12k stars 17.68k forks source link

runtime/cgo: gcc_libinit_windows.c:136:27: error: implicit declaration of function '_beginthread' #59490

Closed vault-thirteen closed 3 days ago

vault-thirteen commented 1 year ago

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

1.20.3

Does this issue reproduce with the latest release?

Yes

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

Windows 10 Intel x86-64

What did you do?

I tried to run a simple code to execute a function from a DLL file.

package main

import "C"

import (
    "fmt"
    "log"
    "runtime"
    "syscall"
    "unsafe"
)

const (
    SDL_INIT_TIMER          = 0x00000001
    SDL_INIT_AUDIO          = 0x00000010
    SDL_INIT_VIDEO          = 0x00000020
    SDL_INIT_JOYSTICK       = 0x00000200
    SDL_INIT_HAPTIC         = 0x00001000
    SDL_INIT_GAMECONTROLLER = 0x00002000
    SDL_INIT_EVENTS         = 0x00004000
    SDL_INIT_SENSOR         = 0x00008000
    SDL_INIT_NOPARACHUTE    = 0x00100000
    SDL_INIT_EVERYTHING     = SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR
)

func main() {
    runtime.LockOSThread()

    err := x()
    if err != nil {
        log.Fatal(err)
    }
}

func x() (err error) {
    var sdl2 = syscall.NewLazyDLL("SDL2.dll")
    SDL_Init := sdl2.NewProc("SDL_Init")
    SDL_GetError := sdl2.NewProc("SDL_GetError")

    var ret uintptr
    ret, _, _ = SDL_Init.Call(SDL_INIT_EVERYTHING)
    fmt.Printf("Return: %d\n", ret)

    ret, _, _ = SDL_GetError.Call()
    fmt.Printf("Return: %d\n", ret)
    s := C.GoString(ret)
    fmt.Printf("Error: %s\n", s)
    C.free(unsafe.Pointer(ret))

    return nil
}

CGO is enabled, gcc is installed with Cygwin64 and has version 11.3.0-1. SDL is the latest version, 2.26.5.

What did you expect to see?

I expected not to see errors at compile time.

What did you see instead?

# runtime/cgo
gcc_libinit_windows.c: In function '_cgo_beginthread':
gcc_libinit_windows.c:136:27: error: implicit declaration of function '_beginthread'; did you mean '_cgo_beginthread'? [-Werror=implicit-function-declaration]
  136 |                 thandle = _beginthread(func, 0, arg);
      |                           ^~~~~~~~~~~~
      |                           _cgo_beginthread
cc1: all warnings being treated as errors

Compilation finished with exit code 1
mknyszek commented 1 year ago

CC @golang/compiler maybe?

vault-thirteen commented 1 year ago

Hello, @mknyszek.

Well, using another compiler can unveil a whole box of surprises. Take native compilers for Windows OS for example ...

By the way, is it possible to use Microsoft's compiler from Visual Studio Community 2022 together with Golang's CGO ? I am using Windows OS, and GCC compiler does not support many things which I need on the Windows platform. Using a compiler from Unix / Linux world on Windows looks very strange, to say the least.

P. S.

D.1.6 Disallowed Implicit int and Implicit Function Declarations

6.5.2.2 Function calls

Implicit declarations are no longer allowed in the 1999 C standard as they were in the 1990 C standard. Previous versions of the C compiler issued warning messages about implicit definitions only with -v (verbose). These messages and new additional warnings about implicit definitions, are now issued whenever identifiers are implicitly defined as int or functions.

This change is very likely to be noticed by nearly all users of this compiler because it can lead to a large number of warning messages. Common causes include a failure to include the appropriate system header files that declare functions being used, like printf which needs included. The 1990 C standard behavior of accepting implicit declarations silently can be restored using -xc99=none.

https://docs.oracle.com/cd/E19205-01/819-5265/bjazh/index.html

vault-thirteen commented 1 year ago

Looks like the reason was in the Cygwin64.

I deleted it and used another bundle of 64-bit MinGW. Now, a new problem arose.

# test/b .\main.go:48:2: could not determine kind of name for C.free

Compilation finished with exit code 1

vault-thirteen commented 1 year ago

OK. I have added a header file for stdlib.

// #include import "C"

Now the error is different.

.\main.go:47:18: cannot use ret (variable of type uintptr) as *_Ctype_char value in argument to (_Cfunc_GoString)

How do I cast Go's uintptr to C's pointer to char ?

vault-thirteen commented 1 year ago

s := C.GoString((C.char)(unsafe.Pointer(ret))) fmt.Printf("Error: %s\n", s) C.free((C.char)(unsafe.Pointer(ret)))

leads to an error:

cannot use _cgo0 (variable of type *_Ctype_char) as unsafe.Pointer value in argument to _Cfunc_free

Full code is following.

package main

// #include <stdlib.h>
import "C"

import (
    "fmt"
    "log"
    "runtime"
    "syscall"
    "unsafe"
)

const (
    SDL_INIT_TIMER          = 0x00000001
    SDL_INIT_AUDIO          = 0x00000010
    SDL_INIT_VIDEO          = 0x00000020
    SDL_INIT_JOYSTICK       = 0x00000200
    SDL_INIT_HAPTIC         = 0x00001000
    SDL_INIT_GAMECONTROLLER = 0x00002000
    SDL_INIT_EVENTS         = 0x00004000
    SDL_INIT_SENSOR         = 0x00008000
    SDL_INIT_NOPARACHUTE    = 0x00100000
    SDL_INIT_EVERYTHING     = SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR
)

func main() {
    runtime.LockOSThread()

    err := x()
    if err != nil {
        log.Fatal(err)
    }
}

func x() (err error) {
    var sdl2 = syscall.NewLazyDLL("SDL2.dll")
    SDL_Init := sdl2.NewProc("SDL_Init")
    SDL_GetError := sdl2.NewProc("SDL_GetError")

    var ret uintptr
    ret, _, _ = SDL_Init.Call(SDL_INIT_EVERYTHING)
    fmt.Printf("Return: %d\n", ret)

    ret, _, _ = SDL_GetError.Call()
    fmt.Printf("Return: %d\n", ret)
    s := C.GoString((*C.char)(unsafe.Pointer(ret)))
    fmt.Printf("Error: %s\n", s)
    C.free((*C.char)(unsafe.Pointer(ret)))

    return nil
}
vault-thirteen commented 1 year ago

Looks like, the C.free function expects an unsafe.Pointer type instead of C's char* type. Why ?

If I change the last statement

C.free((*C.char)(unsafe.Pointer(ret)))

to

C.free((unsafe.Pointer(ret)))

It compiles and runs.

Return: 0 Return: 140722205880947 Error:

Process finished with the exit code -1073740940 (0xC0000374)

cherrymui commented 1 year ago

https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/beginthread-beginthreadex it seems _beginthread is declared in process.h, which runtime/cgo/gcc_libinit_windows.c includes. Does your C toolchain not declare _beginthread?

By the way, is it possible to use Microsoft's compiler from Visual Studio Community 2022 together with Golang's CGO ?

Issue #20982 is about MSVC support. Is there anything you expect beyond that issue?

Thanks.

vault-thirteen commented 1 year ago

Does your C toolchain not declare _beginthread?

I have no idea about Cygwin64. This project is very strange to me. Neither do I use the GCC compiler if I have a choice.

I prefer MS Visual Studio with its MSVC compiler and LLVM with its CLang compiler, but it looks like they are both not supported by Google at this moment.

And, for some reason Golang does not provide a way to select a compiler for C language in CGO. This looks like a selection between one choice. It is weird.

Is there anything you expect beyond that issue?

I expect that Google adds a possibility to change a compiler for C language in CGO in Golang. I mean, a real possibility to choose any compiler that I want, not the fake choice between GCC and GCC.

P. S.

There is a great thing in this field. LLVM.

The LLVM project tries to unify compilers of different languages and different vendors and make it possible to use a common standard for compiling and linking executables. I think, Google should think about supporting this truly cross-platform solution.

Here is an extract from Wikipedia about it.

Due to its permissive license, many vendors release their own tuned forks of LLVM. This is officially recognized by LLVM's documentation, which suggests against using version numbers in feature checks for this reason. Some of the vendors include:

  • AMD's AMD Optimizing C/C++ Compiler is based on LLVM, Clang, and Flang.
  • Apple maintains an open-source fork for Xcode.
  • ARM maintains a fork of LLVM 9 as the "Arm Compiler".
  • Flang, Fortran project in development as of 2022.
  • IBM is adopting LLVM in its C/C++ and Fortran compilers.
  • Intel has adopted LLVM for their next generation Intel C++ Compiler.
  • The Los Alamos National Laboratory has a parallel-computing fork of LLVM 8 called "Kitsune".
  • Nvidia uses LLVM in the implementation of its NVVM CUDA Compiler. The NVVM compiler is distinct from the "NVPTX" backend mentioned in the Backends section, although both generate PTX code for Nvidia GPUs.
  • Sony has been using LLVM's primary front-end Clang compiler in the software development kit (SDK) of its PlayStation 4 console.
cherrymui commented 1 year ago

LLVM with its CLang compiler

I think we do support LLVM based C compiler for Windows. Have you tried that?

The LLVM project tries to unify compilers of different languages and different vendors

We have "gollvm" project that uses LLVM backend for Go https://go.googlesource.com/gollvm . You're welcome to try that. It doesn't support Windows yet, though. But that is beyond the scope of this issue. Thanks.

vault-thirteen commented 1 year ago

I tried to compile CGO code with CLang compiler, but it threw a lot of errors about GCC parameters passed to CLang.

It doesn't support Windows yet

Why ?

ItsOnlyBinary commented 1 year ago

I hit this issue using "Git for Windows SDK" for my mingw, turns out I had forgotten to add the environment variable "MSYSTEM" = "MINGW64" after a clean windows install. Hopefully this information can help someone else.

ianlancetaylor commented 1 year ago

I tried to compile CGO code with CLang compiler, but it threw a lot of errors about GCC parameters passed to CLang.

Can you show us the exact output? Thanks.

vault-thirteen commented 1 year ago

@ianlancetaylor

go build .

D:\Program Files\Go\pkg\tool\windows_amd64\link.exe: running D:\Program Files\LLVM\bin\clang.exe failed: exit status 1104
LINK : warning LNK4044: unrecognized option '/-tsaware'; ignored
LINK : warning LNK4044: unrecognized option '/-nxcompat'; ignored
LINK : warning LNK4044: unrecognized option '/-major-os-version=6'; ignored
LINK : warning LNK4044: unrecognized option '/-minor-os-version=1'; ignored
LINK : warning LNK4044: unrecognized option '/-major-subsystem-version=6'; ignored
LINK : warning LNK4044: unrecognized option '/-minor-subsystem-version=1'; ignored
LINK : warning LNK4044: unrecognized option '/-dynamicbase'; ignored
LINK : warning LNK4044: unrecognized option '/-high-entropy-va'; ignored
LINK : warning LNK4044: unrecognized option '/T'; ignored
LINK : warning LNK4044: unrecognized option '/-start-group'; ignored
LINK : warning LNK4044: unrecognized option '/-end-group'; ignored
LINK : fatal error LNK1104: cannot open file 'mingwex.lib'
clang: error: linker command failed with exit code 1104 (use -v to see invocation)
ianlancetaylor commented 1 year ago

Those errors are all coming from the linker. We currently expect that clang will be used with a clang linker such as lld.

vault-thirteen commented 1 year ago

Is it possible for a user to change the linker ?

ianlancetaylor commented 1 year ago

I'm sure it is, because I see that lld supports Windows. But I don't know enough about Windows to tell you how to do it. Sorry.

vault-thirteen commented 1 year ago

@ianlancetaylor , do you mean Linux's symlinks ? This does not look like a setting to me :-) I mean a variable, as CC for C compiler. CL for C linker ? I have not found anything about setting path to linker in description of cgo package – https://pkg.go.dev/cmd/cgo.

ianlancetaylor commented 1 year ago

Sorry, I don't understand what you mean about symlinks. When I mention lld I mean https://lld.llvm.org/.

The Go tools use the environment variable CC for both compiling and linking. They expect the compiler to invoke the linker when necessary. That is how clang works.

There must be some way to install clang on Windows such that it runs lld as the linker.

vault-thirteen commented 1 year ago

CMake has an interesting feature. It is possible to tell CMake how to use the linker.

cmake -DCMAKE_LINKER=/path/to/linker -DCMAKE_CXX_LINK_EXECUTABLE=" -o "

https://stackoverflow.com/questions/1867745/cmake-use-a-custom-linker

I thought about something like this.

vault-thirteen commented 1 year ago

Is it possible in CGO to pass a custom argument to the compiler specified in CC variable ?

I know how to tell the CLang compiler use the LLD (-fuse-ld=lld), but I do not know how to make CGO use it :-)

ianlancetaylor commented 1 year ago

Set CFLAGS in the environment.

vault-thirteen commented 1 year ago

I tried to run the CLang compiler with LLD (SET CGO_CFLAGS=-fuse-ld=lld), but nothing changed. Looks like CGO is using the compiler (and/or linker) not properly :)

Is there a verbose mode in CGO to print all the executed commands into stdout for debugging ? Oh. I have found it. It is go build -x ... Why the heck -x instead of normal -v ? (O.o)

ianlancetaylor commented 1 year ago

CFLAGS and CGO_CFLAGS are not the same.

vault-thirteen commented 1 year ago

What is the difference between CFLAGS and CGO_CFLAGS ? I thought that C is used in Go only via CGO.

ianlancetaylor commented 1 year ago

Bother, you're right. My apologies. It should be CGO_CFLAGS. But actually since you are linking you might have to set CGO_LDFLAGS.

vault-thirteen commented 1 year ago

Actually, it is not clear from the documentation (https://pkg.go.dev/cmd/cgo), how to use these variables. It looks like CFLAGS sets arguments and CGO_CFLAGS appends additional arguments. But what happens when this variable is set in a Go file as a // #cgo CFLAGS: ... directive ? Which order of values is used ... is not clear. What happens when all three are set ? :-)

ianlancetaylor commented 1 year ago

In general the flags from the environment variables follow the flags from a #cgo line.

mknyszek commented 1 year ago

@vault-thirteen Are you still seeing the implicit declaration error? Should we leave this issue open?

vault-thirteen commented 1 year ago

@mknyszek , if I install the Cygwin64, then I will see it, so yes, it is not working with Cygwin64.

wayneforrest commented 1 year ago

I ran into the same problem:

`
$ CGO_ENABLED=1 GOOS=windows GOARCH=arm64 go build -x -buildmode=c-shared -o ct-lib.dll main.go
WORK=C:\msys64\tmp\go-build3598144249
mkdir -p $WORK\b300\
cd C:\Program Files\Go\src\runtime\cgo
TERM='dumb' CGO_LDFLAGS='"-O2" "-g"' "C:\\Program Files\\Go\\pkg\\tool\\windows_arm64\\cgo.exe" -objdir "$WORK\\b300\\" -importpath runtime/cgo -import_runtime_cgo=false -import_syscall=false "-exportheader=$WORK\\b300\\_cgo_install.h" -- -I "$WORK\\b300\\" -O2 -g -Wall -Werror -fno-stack-protector "C:\\Program Files\\Go\\src\\runtime\\cgo\\cgo.go"
cd $WORK\b300
TERM='dumb' gcc -I "C:\\Program Files\\Go\\src\\runtime\\cgo" -mthreads -Wl,--no-gc-sections -fmessage-length=0 "-fdebug-prefix-map=$WORK\\b300=/tmp/go-build" -gno-record-gcc-switches -I "$WORK\\b300\\" -O2 -g -Wall -Werror -fno-stack-protector "-fdebug-prefix-map=C:\\Program Files\\Go\\src\\runtime\\cgo=\\\\_\\_\\runtime\\cgo" -o "$WORK\\b300\\_x001.o" -c _cgo_export.c
TERM='dumb' gcc -I "C:\\Program Files\\Go\\src\\runtime\\cgo" -mthreads -Wl,--no-gc-sections -fmessage-length=0 "-fdebug-prefix-map=$WORK\\b300=/tmp/go-build" -gno-record-gcc-switches -I "$WORK\\b300\\" -O2 -g -Wall -Werror -fno-stack-protector "-fdebug-prefix-map=C:\\Program Files\\Go\\src\\runtime\\cgo=\\\\_\\_\\runtime\\cgo" -o "$WORK\\b300\\_x002.o" -c cgo.cgo2.c
cd C:\Program Files\Go\src\runtime\cgo
TERM='dumb' gcc -I "C:\\Program Files\\Go\\src\\runtime\\cgo" -mthreads -Wl,--no-gc-sections -fmessage-length=0 "-fdebug-prefix-map=$WORK\\b300=/tmp/go-build" -gno-record-gcc-switches -I "$WORK\\b300\\" -O2 -g -Wall -Werror -fno-stack-protector "-fdebug-prefix-map=C:\\Program Files\\Go\\src\\runtime\\cgo=\\\\_\\_\\runtime\\cgo" -o "$WORK\\b300\\_x003.o" -c gcc_context.c
TERM='dumb' gcc -I "C:\\Program Files\\Go\\src\\runtime\\cgo" -mthreads -Wl,--no-gc-sections -fmessage-length=0 "-fdebug-prefix-map=$WORK\\b300=/tmp/go-build" -gno-record-gcc-switches -I "$WORK\\b300\\" -O2 -g -Wall -Werror -fno-stack-protector "-fdebug-prefix-map=C:\\Program Files\\Go\\src\\runtime\\cgo=\\\\_\\_\\runtime\\cgo" -o "$WORK\\b300\\_x004.o" -c gcc_libinit_windows.c
# runtime/cgo
gcc_libinit_windows.c: In function ‘_cgo_beginthread’:
gcc_libinit_windows.c:136:27: error: implicit declaration of function ‘_beginthread’; did you mean ‘_cgo_beginthread’? [-Werror=implicit-function-declaration]
  136 |                 thandle = _beginthread(func, 0, arg);
      |                           ^~~~~~~~~~~~
      |                           _cgo_beginthread
cc1: all warnings being treated as errors
`

I see no problem within the code for: gcc_libinit_windows.c:136:27, and not sure why the compiler has a problem with this. _beginthread is defined in process.h as: (I also see no preprocessor exclusions been made)

_ACRTIMP uintptr_t __cdecl _beginthread(
    _In_     _beginthread_proc_type _StartAddress,
    _In_     unsigned               _StackSize,
    _In_opt_ void*                  _ArgList
    );

I will be trying this out later: https://github.com/goreleaser/goreleaser-cross

wayneforrest commented 1 year ago

I further checked the location of the process.h file, on my machine it's located here: (maybe the compiler is finding another header file?) -- investigating this now.

C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt\process.h

and when I run this I can see where gcc is looking:

echo "#include <bogus.h>" > t.c; gcc -v t.c; rm t.c

It's not finding the windows sdk header... but some other header file.

wayneforrest commented 1 year ago

After using the CGO_FLAGS to specify additional search paths for gcc, I got this type of errors, and adding more paths, adds more errors... all the way up to vcruntime.h -- it's rabbit hole...

CGO_CFLAGS="-I'/c/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt/' -I'/c/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um/' -I'/c/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared'" CGO_ENABLED=1 GOOS=windows GOARCH=arm64 go build -x -buildmode=c-shared -o ct-lib.dll main.go
WORK=C:\msys64\tmp\go-build2264873858
mkdir -p $WORK\b300\
echo > $WORK\b300\preferlinkext
cd C:\Program Files\Go\src\runtime\cgo
TERM='dumb' CGO_LDFLAGS='"-O2" "-g"' "C:\\Program Files\\Go\\pkg\\tool\\windows_arm64\\cgo.exe" -objdir "$WORK\\b300\\" -importpath runtime/cgo -import_runtime_cgo=false -import_syscall=false "-exportheader=$WORK\\b300\\_cgo_install.h" -- -I "$WORK\\b300\\" -I'C:/Program Files "(x86)/Windows" Kits/10/Include/10.0.22621.0/ucrt/' -I'/c/Program Files "(x86)/Windows" Kits/10/Include/10.0.22621.0/um/' -I'/c/Program Files "(x86)/Windows" Kits/10/Include/10.0.22621.0/shared' -Wall -Werror -fno-stack-protector "C:\\Program Files\\Go\\src\\runtime\\cgo\\cgo.go"
# runtime/cgo
In file included from C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt/stddef.h:12,
                 from cgo-builtin-prolog:1:
C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt/corecrt.h:10:10: fatal error: vcruntime.h: No such file or directory
   10 | #include <vcruntime.h>
      |          ^~~~~~~~~~~~~
compilation terminated.
wayneforrest commented 1 year ago

this got me unblocked: (using mingw64 from the Windows GIT-SDK - also set the windows environment variable MSYSTEM=MINGW64 - not sure if this made any difference)

CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CXX=x86_64-w64-mingw32-g++ CC=x86_64-w64-mingw32-gcc go build -buildmode=c-shared -o ct-lib.dll main.go

Also I am not yet able to target windows arm64 - I assume I need to wait for Windows GIT-SDK / MINGW64 to support windows arm64

ASalem404 commented 1 year ago

that happen because you installed gcc through cygwin64 so, try to install MinGW. download it from this link https://github.com/niXman/mingw-builds-binaries/releases make sure to choose a compatible version if you have Windows 64 choose mingw64 and so
then extract it into Windows partition in my case was C:\ partition then change the gcc path in environment variables in my case the path after the update was "C:\mingw64\bin" then restart vs code if it opens then try the command again.

if it needs to CG_ENABELED=1 then open powershell as admin and write this command "go env -w CGO_ENABLED=1"

injuryzy commented 1 year ago

删除 其他的gcc 环境 只是用tdm-gcc ,就可以编译成功

TimurIskandarov commented 1 year ago

this got me unblocked: (using mingw64 from the Windows GIT-SDK - also set the windows environment variable MSYSTEM=MINGW64 - not sure if this made any difference)

CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CXX=x86_64-w64-mingw32-g++ CC=x86_64-w64-mingw32-gcc go build -buildmode=c-shared -o ct-lib.dll main.go

Also I am not yet able to target windows arm64 - I assume I need to wait for Windows GIT-SDK / MINGW64 to support windows arm64

In this package, the search process.h https://packages.msys2.org/package/mingw-w64-x86_64-headers-git You just need to download https://packages.msys2.org/package/mingw-w64-x86_64-gcc?repo=mingw64 and all the dependencies will catch up.

nilyang commented 7 months ago

删除 其他的gcc 环境 只是用tdm-gcc ,就可以编译成功

Thanks @injuryzy ,it works on my windows11!

gopherbot commented 3 days ago

Change https://go.dev/cl/627935 mentions this issue: runtime/cgo: report a meaningful error message when using Cygwin