Go: cmd/link: support msvc object files

Created on 11 Jul 2017  ·  222Comments  ·  Source: golang/go

I understand that the go linker cannot currently link msvc object files and also recognize that this issue is likely to be a low priority. However, it would be nice to support this because it would somewhat simplify windows workflow. This issue is mainly to understand how much effort this would be and/or what would be required.

Builders FeatureRequest NeedsInvestigation OS-Windows

Most helpful comment

Hey it looks from the bottom of this thread that you've got your MSVC issues sorted out. But if you run into any issues, I'm on the MSVC team. Feel free to ping me on github or email ([email protected])

All 222 comments

/cc @alexbrainman

@xoviat what is the problem that you are having? I need to be able to reproduce it here. So, please, provide all steps I will need to follow to reproduce this.

Thank you

Alex

PS: I won't have computer until end of July. I will have a look at this then.

what is the problem that you are having?

I haven't tried it yet, but I would like to specifically call c functions in msvc object files by linking them as .syso with the go linker. Everything that I have read indicates that this is not possible but I will create a procedure to reproduce the specific error that occurs.

specifically call c functions in msvc object files by linking them as .syso with the go linker

Have you tried building these into a DLL, and use them from inside of DLL?

I will create a procedure to reproduce the specific error that occurs.

Please, do. Thank you.

Alex

Have you tried building these into a DLL, and use them from inside of DLL?

That was actually my original plan. I am using swig so it is not so convenient to compile the generated c code separately and then rewrite the functions as DLL exports. It's not difficult, but it is annoying when the workflow with gcc is just go generate; go build.

Alright, I have a procedure. Start with these files:

hello.go:

package main

/*
    extern void hello();
*/
import "C"

func main() {
    C.hello()
}

hello.c:

#include <stdio.h>

extern void hello()
{
    printf("Hello World from C");
}

then run these commands (with msvc on path):

cl /c hello.c
mv hello.obj hello.syso
mv hello.c hello.c.bak
go build

Result:
Warning: corrupt .drectve at end of def file

When running the produced file:

Exception 0xc0000005 0x8 0x13 0x13
PC=0x13
signal arrived during external code execution

main._Cfunc_hello()
        _//_obj/_cgo_gotypes.go:41 +
main.main()
        C://hello.go:9 +0x27

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
        C:/Program Files/Go/src/runtime/asm_amd64.s:2197 +0x1
rax     0x4a5960
rbx     0xc042045f78
rcx     0x4a9a20
rdi     0xc042045f78
rsi     0x4adc60
rbp     0xc042045f38
rsp     0x6dfd68
r8      0xc042016340
r9      0x0
r10     0xc04204faa0
r11     0x4783c2
r12     0x0
r13     0x6
r14     0x0
r15     0xf1
rip     0x13
rflags  0x10216
cs      0x33
fs      0x53
gs      0x2b

I do not have cl command installed on my pc. How do I install msvc?

Thank you.

Alex

You need the "build tools 2017" here. Microsoft actually now allows anyone to use visual studio for free as long as it's not for commercial development. If it's too much trouble, I can just give you the object file if you want.

If only c++ was not a thing, I wouldn't have to worry about this. But it is, so the pain continues...

You need the "build tools 2017" here.

Got it. Thank you.

If it's too much trouble, I can just give you the object file if you want.

Yes, please, post hello.obj somewhere.

Thinking about this some more. You are actually using gcc to link object file compiled with msvc compiler. Can you try and do your exercise, but replacing "go build" step with gcc linking hello.obj to a C program? Perhaps it has been done before. I suspect, if we know how to do that, we might be able to do similar with Go.

Alex

AFAIK lld https://github.com/llvm-mirror/lld supports msvc object files.

Object file is here: https://github.com/xoviat/msvcgo/blob/master/hello.syso

lld https://github.com/llvm-mirror/lld supports msvc object files

Go uses gcc linker (not lld) on Windows.

Object file is here: https://github.com/xoviat/msvcgo/blob/master/hello.syso

I will try it when I get home in August. thank you.

Alex

Go uses gcc linker (not lld) on Windows.

I know. But lld is the best open source documentation of msvc object format.

I actually get same error from ld so the error is definitely coming from ld.

I actually get same error from ld so the error is definitely coming from ld.

Yes. We need to work out how to build C program by compiling part with msvc, and linking with gcc.

Alex

Forgive my ignorance, but what exactly gcc linking? Is it linking output of the go linker?

Running objconv on the object files to convert to elf:

objconv -felf hello.obj hello.syso

We now have these errors:

hello.syso: In function `__local_stdio_printf_options':
(.text$mn+0x3): undefined reference to `?_OptionsStorage@?1??__local_stdio_printf_options@@9@9'
hello.syso: In function `_vfprintf_l':
(.text$mn+0x3a): undefined reference to `__stdio_common_vfprintf'
hello.syso: In function `printf':
(.text$mn+0x28): undefined reference to `__acrt_iob_func'
collect2.exe: error: ld returned 1 exit status

Possibly stdio is off limits?

Forgive my ignorance, but what exactly gcc linking? Is it linking output of the go linker?

You use 2 programs to build your Go program:

  • compiler converts your .go files (1 package at the time) into an object file stored under %GOPATH%/pkg directory;
  • linker that builds final .exe file from object files from under %GOPATH%/pkg.

Sometimes (when one of your packages uses Cgo), the Go linker calls external linker to find all bits that are implemented in C. Imagine you call printf from your C code. The C printf compiled code needs to be part of Go executable, but Go linker does not know where to get it. So Go linker calls external linker to include that code.

Current Go uses gcc compiler/linker to compile and link C code (we use mingw gcc). If you are going to compile your C code with different compiler (by Microsoft), you would have to use correspondent linker (by Microsoft) to find all external C code that the compiler created.

So, I guess, I was wrong about suggesting to use gcc linker. For general scenario, you would have to use both Microsoft compiler and linker. We would have to figure out what Microsoft linker requires as input and match that.

You might get away without MC linker, if your C code does not have any external code. Please try some really simple C program (like the one that adds 2 integers or something). That might just work as you have described above.

Alex

Possibly stdio is off limits?

I suspect you need to call Microsoft linker to find that code.

Alex

I don't make sure but

objconv -felf hello.obj hello.syso

Maybe, you should try to do coff or omf instead of elf?

Maybe, you should try to do coff or omf instead of elf?

@xoviat yes, you should not convert .obj files to elf, Windows version of gcc generates pe/coff files just like any other Windows compiler.

Alex

the Go linker calls external linker to find all bits that are implemented in C.

What is the specific command used? If I know the mingw command, then perhaps I can proceed down a file comparison path to try to get msvc to match what mingw is putting out.

You can see the exact comment by running go build -ldflags=-v.

Okay, so from what I can tell:

ld (link -> go.obj) + (gcc -> obj files) ==> a.out.exe

Go appears to create a temporary directory with these files. Is there any way to preserve the temporary directory so that I can inspect its contents?

Try go build -work

WORK=C:\Users\mattn\AppData\Local\Temp\go-build566171254

Object files are remaining in this directory.

The -work option actually won't preserve the temporary files that the linker creates. At the moment you just have to edit the linker sources to not remove the directory. We should probably add an option for that, one way or another.

At the moment you just have to edit the linker sources to not remove the directory.

If you don't mind, I'm going to wait until this is implemented in HEAD so that I don't have to do repetitive work.

Is there any way to preserve the temporary directory so that I can inspect its contents?

cmd/link program has -tmpdir flag for that. You can use it like this:

c:\Users\Alex\dev\src\a>dir
 Volume in drive C has no label.
 Volume Serial Number is 9012-A870

 Directory of c:\Users\Alex\dev\src\a

06/08/2017  02:02 PM    <DIR>          .
06/08/2017  02:02 PM    <DIR>          ..
06/08/2017  02:02 PM                77 main.go
               1 File(s)             77 bytes
               2 Dir(s)  430,809,088,000 bytes free

c:\Users\Alex\dev\src\a>type main.go
package main

import "fmt"
import "C"

func main() {
        fmt.Println("Hello")
}

c:\Users\Alex\dev\src\a>go build -o a.exe -ldflags="-tmpdir=c:\Users\Alex\dev\src\a" main.go

c:\Users\Alex\dev\src\a>dir
 Volume in drive C has no label.
 Volume Serial Number is 9012-A870

 Directory of c:\Users\Alex\dev\src\a

06/08/2017  02:02 PM    <DIR>          .
06/08/2017  02:02 PM    <DIR>          ..
06/08/2017  02:02 PM             2,055 000000.o
06/08/2017  02:02 PM            22,376 000001.o
06/08/2017  02:02 PM         2,017,382 a.exe
06/08/2017  02:02 PM               135 fix_debug_gdb_scripts.ld
06/08/2017  02:02 PM         2,402,226 go.o
06/08/2017  02:02 PM                77 main.go
06/08/2017  02:02 PM                24 trivial.c
               7 File(s)      4,444,275 bytes
               2 Dir(s)  430,804,631,552 bytes free

c:\Users\Alex\dev\src\a>

Alex

This is just for my own reference, but this needs porting to msvc:

_cgo_sys_thread_start(ThreadStart *ts)
{
    uintptr_t thandle;

    thandle = _beginthread(threadentry, 0, ts);
    if(thandle == -1) {
        fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno);
        abort();
    }
}

static void
threadentry(void *v)
{
    ThreadStart ts;

    ts = *(ThreadStart*)v;
    free(v);

    ts.g->stackhi = (uintptr)&ts;
    ts.g->stacklo = (uintptr)&ts - STACKSIZE + 8*1024;

    /*
     * Set specific keys in thread local storage.
     */
    __asm {
          "movq %0, %%gs:0x28\n"    // MOVL tls0, 0x28(GS)
          "movq %%gs:0x28, %%rax\n" // MOVQ 0x28(GS), tmp
          "movq %1, 0(%%rax)\n" // MOVQ g, 0(GS)
          :: "r"(ts.tls), "r"(ts.g) : "%rax"
    }

    crosscall_amd64(ts.fn);
}

I would not underestimate the time that it will take me to complete this task as I am not at all familiar with assembly.

  • [x] Understand what the assembly does
  • [x] Port to MSVC assembly
  • [x] Understand _beginthread vs CreateThread
  • [x] Switch to CreateThread

Also, undefined symbols:

  • [x] timeBeginPeriod --> winmm.lib
  • [x] timeBeginPeriod
  • [x] WSAGetOverlappedResult --> Ws2_32.lib
  • [x] WSAGetOverlappedResult
  • [x] _cgo_18b6f6fc815b_Cfunc_hello
  • [x] x_cgo_init --> msvc_windows_amd64.c
  • [x] x_cgo_thread_start --> msvc_windows_amd64.c
  • [x] x_cgo_sys_thread_create --> msvc_windows_amd64.c
  • [x] x_cgo_notify_runtime_init_done --> gcc_libinit_windows.c
  • [x] x_cgo_set_context_function --> gcc_libinit_windows.c

Okay, so moving along here more quickly than expected!

All: is asm_amd64.s assembled by go's assembler (with the odd assembly) or gcc's assembler?

It seems that gcc will not assemble it, which means that it's probably go assembly. And then the question becomes: how to assemble it with the go assembler into an object.

The intent is that runtime/cgo/asm_amd64.s gets assembled into a Go object, then cmd/link links it together with all the other Go objects into a single system object, and then the system linker links that single system objects plus all the cgo dependencies into the final program.

Is there a way to assemble that object for now for testing purposes? Like gcc -c asm_amd64.s except with go?

The intent is that runtime/cgo/asm_amd64.s gets assembled into a Go object, then cmd/link links it together with all the other Go objects into a single system object, and then the system linker links that single system objects plus all the cgo dependencies into the final program.

So far I found these objects:

  • go.o : obviously from the go toolchain
  • _cgo_.o : generated by gcc, unusable
  • 000000.o : generated by gcc, unusable
  • 000001.o : Update: actually generated by the go linker, but contains gcc symbols. Unusable.

go.o is the biggest object.

@ianlancetaylor

I don't think what you said is correct. crosscall_amd64 is located in 000001.o, but that file doesn't have "all of the go code":

000001.o:     file format pe-x86-64

SYMBOL TABLE:
[201](sec  1)(fl 0x00)(ty  20)(scl   2) (nx 0) 0x0000000000000440 crosscall_amd64
[206](sec  0)(fl 0x00)(ty  20)(scl   2) (nx 0) 0x0000000000000000 free

This file has obviously been generated by gcc, which is too late in the process.

Is there a way to assemble that object for now for testing purposes? Like gcc -c asm_amd64.s except with go?

asm_amd64.s is part of runtime package. You can see how "go build" command uses asm_amd64.s file, like:

$ touch asm_amd64.s
$ GOOS=windows go build -x runtime 2>&1 | grep asm_amd64.s
/home/a/go/pkg/tool/linux_amd64/asm -trimpath $WORK -I $WORK/runtime/_obj/ -I /home/a/go/pkg/include -D GOOS_windows -D GOARCH_amd64 -o $WORK/runtime/_obj/asm_amd64.o ./asm_amd64.s
$

Alex

Thanks.

The symbol crosscall_amd64 is defined in the file runtime/cgo/gcc_amd64.s. That file is compiled by GCC (as are all the runtime/cgo/gcc_* files). So it is not combined into the single go.o file that contains all the Go code. The file we were talking about before, runtime/cgo/asm_amd64.s, defines the symbol crosscall2. You will find that symbol in go.o.

Thanks.

Okay, I've managed to link an executable that crashes on an access violation while in
go.runtime.rt0_go+5F --> go.___acrt_stdio_initializer.

That's all the time I have for now; I'll come back to this later.

@alexbrainman That's correct.

@alexbrainman You'll need MSVC to proceed. Let me know whether you need help with this.

You'll need MSVC to proceed.

I am installing this https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017

What should I do once installed?

Alex

Once you have installed that, you need to build "libgo," which is the go library. Copy the following files from the cgo folder:

  • gcc_amd64.S
  • gcc_fatalf.c
  • gcc_libinit_windows.c
  • gcc_util.c
  • gcc_windows_amd64.c
  • libcgo.h

We need to adjust gcc_windows_amd64.c to be compatible with MSVC. Modify the function as follows:

static void
threadentry(void *v)
{
    fprintf(stderr, "threadentry: started");
    abort();

    ThreadStart ts;

    ts = *(ThreadStart*)v;
    free(v);

    ts.g->stackhi = (uintptr)&ts;
    ts.g->stacklo = (uintptr)&ts - STACKSIZE + 8*1024;

    /*
     * Set specific keys in thread local storage.
     */
    __writegsqword(0x28, (unsigned long long) ts.tls);
    *(void **)ts.tls = (void *) ts.g;

    crosscall_amd64(ts.fn);
}

Create a new file in the folder with all of these files called "build.bat":

REM TMP use gcc for assmebly file, in future port to ml64
gcc -c gcc_amd64.S
cl -c gcc_fatalf.c
cl -c gcc_libinit_windows.c
cl -c gcc_windows_amd64.c
cl -c gcc_util.c

ren gcc_amd64.o gcc_amd64.obj

lib gcc_amd64.obj gcc_fatalf.obj gcc_libinit_windows.obj ^
    gcc_windows_amd64.obj gcc_util.obj ^
    /OUT:libgo.lib

Let me know when you have the folder structured as requested.

By the way, thanks for both working on this and for go. You guys have created a truly magical language.

Let me know when you have the folder structured as requested.

I managed to build libgo.lib as you have described here https://github.com/golang/go/issues/20982#issuecomment-327365063
What should I do next?

Alex

Okay, so now we need the following files:

  • libgo.lib
  • go.o
  • hello.cgo2.o
  • hello.c

hello.c is the following:

#include <stdio.h>

extern void hello()
{
    printf("Hello World from C");
}

To get hello.cgo2.o and go.o, you need to start with the following file:

package main

/*
    extern void hello();
*/
import "C"
import "fmt"

func main() {
    fmt.Println("Hello from Go!")
    C.hello()
}

called "hello.go"

Set up a powershell script that continuously copies files from $env:TMP:

while ($true) {  cp -r $env:TMP\go-* C:\Users\User\Downloads }

Then run go build with hello.c and hello.go in the folder. You should be able to recover the required files from the location that they were copied to.

Once you have the files specified above, you can build with:

cl libgo.lib go.o hello.cgo2.o hello.c Ws2_32.lib Winmm.lib -link /DEBUG:FULL

Let me know if you have any questions.

cl libgo.lib go.o hello.cgo2.o hello.c Ws2_32.lib Winmm.lib -link /DEBUG:FULL

That creates go.exe executable file, but it does not run. I don't see any sensible asm code there. First instruction just jump into nowhere. It is just a mess.

Maybe you should start with something real simple. Write an asm file (Go asm file) with single asm function that executes "INT $3" and has nothing else. And try to build an executable program out of it - the program should run your function at the very start.

Maybe build it using Go tools first (and also using gcc if needed), and then try and do the same using MSVC.

Alex

Alex, thank you for your help. I will work on that.

I'm going to attach these two articles here for my own reference. Otherwise, I don't have any further update.

I actually got a fairly complex go program working that only uses the msvc library. It turns out that both vanilla msvc linker and llvm-lld did not correctly handle the .bss section.

Once I patched the linker the program can run.

Unfortunately go build breaks when it generates _cgo_.o / _all.o. Is it possible to shed some lights on the rationales behind generating these two files in cgo?

You can run go tool cgo to run cgo directly. The sources are here: https://github.com/golang/go/tree/master/src/cmd/cgo

Also, if you could get this to run, that would be awesome. I just have not devoted time to this, so there's been no progress. Sorry about that.

I already got programs that can run correctly through a series of hand stitching -- what I'm try to do here is to see whether the process can be integrated back into go to make it less painful. :-)

If you can document the hand-stitching process, then I may be able to help.

Just from looking, _cgo_.o seems to be produced like this (simplified):

gcc [*.c] [*.cxx] -o _cgo_.o

From here: https://github.com/golang/go/blob/b4c84a1b010f012668b5e3ccaf63f609cd11c5fe/src/cmd/go/internal/work/exec.go#L1975

Ideally we would write a go program that pre-processes the object files so that they're compatible with link.exe for minimal friction, but that's kind of a stretch goal.

Thanks for the pointer -- I'm going to write down the process shortly.

Unfortunately I don't think the linking can be done via link.exe unless we change gcc to emit uninitialized data in the .data section instead of the .bss section -- but we can definitely fix llvm-lld to recognize the .bss section (which is what I have done).

We need to tackle _cgo_.o and _all.o separately. I have a few questions on them:

(1) It seems that _cgo_.o is not the final executable as it does not contain the go runtime. It looks like the compiler looks at its DWARF symbols to figure out the definition of the struct. The problem is that it is difficult to generate an executable if you are going to link with a number of libraries externally, especially the ones that are generated by msvc.

Is it possible to avoid this step?

(2) current go use GNU ld to stitch all object files into _all.o via passing the -Wl,-r in GCC. This is problematic as (1) it does not seem other linker has this feature, and (2) the command is affected by the CGO_LDFLAGS. For example, the following command generates incorrectly results:

CGO_LDFLAGS="-Wl,-T,my-linker-script"
gcc .... $CGO_LDFLAGS -Wl,-r,...

It generates an executable instead of a bundled object file.

Is it possible to avoid this step at all via just putting all object files into the generated .a directly?

@zooba What are the chances of getting MSFT to update link.exe with this patch?

Unfortunately I don't think the linking can be done via link.exe unless we change gcc to emit uninitialized data in the .data section instead of the .bss section -- but we can definitely fix llvm-lld to recognize the .bss section (which is what I have done).

So assuming that MSFT doesn't update link.exe (which is probable), it would be good to compile the object files with cl.exe rather than gcc.

It seems that _cgo_.o is not the final executable as it does not contain the go runtime.

That's correct, there are basically two sets of object files (I think) that get linked together in the end. The go.o file (IIRC) contains all the go code + runtime, and the other objects contain the C code. There are assembly routines to jump between the two sets of objects.

Also, we need clang to compile the Unix assembly files.

For our purposes, -Wl,-r is going to be the same as running lib.exe [object files] /OUT:obj. The option means "incrementally link," which means "take some input files, do some work, then spit out another object file." If we don't worry about the "do some work" part for now, we can just take the requirement as "take some input files and/or object files and spit out another object file" except in our case the object file will be a library.

You can tell GCC to avoid putting variables in the .data section rather than the .bss section by using the option -fno-zero-initialized-in-bss.

Note that on tip we no longer use -Wl,-r when building cgo code.

It should not be necessary for cmd/link to understand MSVC object files in order to use them as .syso files. Those files are just passed to the external linker anyhow. So I think that what is needed here is to invoke the MSVC linker as the external linker, which you ought to be able to do by using the -extld option. If you are already doing that, my apologies; in that case, what is failing?

If you are already doing that, my apologies; in that case, what is failing?

IIRC, link.exe barfs on the gcc generated objects.

True, you would also have to use MSVC C's compiler, by setting the CC environment variable appropriately. I don't think it's going to work to combine GCC objects and MSVC objects in a single link.

But then you run into the issue where the runtime C libraries are precompiled. And if you try to recompile them, you run into the issue that cl cannot compile unix assembly. And so you port parts of the runtime libraries and try to link them, which is what I did and failed.

Somewhere along the way, cl miscompiles a key part of the code. My idea for fixing that would be to factor out the MSVC code into a DLL with a flat C API, and then just incrementally compile more of the runtime libraries until it stops working.

@haohui's idea for fixing the problem was different. Rather than fixing the root of the problem, he worked around it by patching the linker. Obviously he had more success than I did, but that's probably because his approach is, with hindsight, more likely to work than mine.

What I would recommend at this point (IMHO) is to incrementally compile more of the runtime library with cl until it stops working or until you have no gcc code left. Then you know exactly where the problem is.

And with the linker patch, no DLL needed.

The patch of llvm-lld is available at https://bugs.llvm.org/show_bug.cgi?id=35283

I'll spend some time to provide an example.

+1 @haohui Thanks so much for taking this forward!

@haohui Which version of llvm does your patch apply to? Any instructions on how to use your patch and how to link against MSVC files?

Regarding _cgo_.o and _all.o files, I had the same issue, so maybe a fix for this issue can be a fix for the other issue too: https://github.com/golang/go/issues/17014

Thanks for all the work on this. It sounds like good progress is being made. New features are frozen until Go 1.11, so changing the milestone.

So I jumped into this last night. It is possible to build everything via msvc:cl then link everything through msvc:link. The issues, however, are legion.

So beyond the simple problems like msvc:cl not fully supporting C99 (Right now we can't handle type _Complex). There are more systemic issues with combing PE libs build by go internals and those built by msvc. Specifically msvc:link blows away the .bss segments (I think it may just throw this data into .data) which is a problem since it looks like the go assembler SB (pseudo reg) results in addressing that is incorrect if .bss gets moved.

My initial thought would be to try to have the assembler place the the things that would go into .bss and .noptrbss into .data and .noptrdata respectively. I'm not even sure if this is possible; my fiddling with it has not been successful and the addressing gets hosed completely.

I don't think it's possible to use link.exe for now. I think we should start with lld-link.exe

If the thought behind that is that we can patch lld-link to not move around the .bss data, I understand the appeal. Realistically if the goal is to support the actual msvc tool chain then go internals compilation placing data into .bss/.noptrbss for will need to be addressed.

Can't we just redistribute lld-link.exe with go? That seems like the lowest-risk path to support MSVC object files as long as LTCG is not enabled. I understand it's not ideal but we face a resource constraint.

It also allows incremental progress which is the best strategy.

Okay, some good follow up news, I was able to spend an hour working on this over lunch.

At the moment I have the example from earlier in the issue working:

- hello.go:
package main

/*
    extern void hello();
*/
import "C"

func main() {
    C.hello()
}

- extern.c
#include <stdio.h>

extern void hello()
{
    printf("Hello World from C");
}
>ac.out.exe
Hello World from C

The binary is built completely with the MSVC and Go toolchain (no GCC or other LLVM installed).

Findings:

  • Having the Go tools/link.exe output .bss data for internal assemblies to .data was ultimately fairly trivial
  • Several ASM pieces had to be adjsuted for msvc
  • A few GCC #defs were shimmed
  • Several small adjustments had to be made to some of the cgo generated .c files
  • Complex numbers will need to be shimmed, at the moment they unsupported
  • It is likely that additional flags will need to be added straight to build or link for msvc support, ldflags etc will not be sufficient

Next Steps:
If I have time this weekend I'll work on getting my changes into a fork and but the functionality behind flags in build/link. Afterwards I'll get a PR going for review so we can work out the kinds. I'm not 100% confident that moving the .bss data around wont have some kind of impact somewhere.

The only draw back that I can think of is that it will blow up the size of the binary, but it should be okay.

Okay!

Sorry it took me a couple extra days to get this put together. So the first draft at a patch can found here: https://github.com/cchamplin/go/commit/69a5cfc1dd0106fd8a2928a83e4c7001e81e89b8 :: https://github.com/cchamplin/go/tree/msvc_toolchain_support

This is still rough but I have it successfully building code with msvc.

If would be awesome if people could start testing this now and report bugs back to me so I can get this in a better spot for upstream. Also if anyone wants to chip in writing tests that would be amazing!

Usage:

go build -compiler msvc [path]
  • Complex numbers are unsupported currently until I figure out if we can make them work.
  • I have no idea what happens if you try to build the go runtime with the msvc compiler. My guess it everything breaks hard
  • gcc is still being needed and used by gco to get defines and type data when compiling it is not used for building anything). I don't know if we can get around this, msvc has nothing that can do the same dwarf and #define dumps of code... if anyone has ideas on this that'd be great

The linked patch creates a bootstrapping problem.

@alexbrainman How and where is it determined which files will be copied into pkg/boostrap/src/bootstrap during the toolchain1 boostrapping phase?

cc @alexbrainman

The linked patch creates a bootstrapping problem.

I do not know how bootstrapping works nowdays. But I am sure others (@rsc and @ianlancetaylor) can help.

I wonder how @cchamplin run make.bat if bootstrapping does not work.

Alex

@xoviat I don't know what the problem, but the list of bootstrap directories is in cmd/dist/buildtool.go.

Hey @alexbrainman, @ianlancetaylor, @xoviat sorry about that I guess I should have verified more thoroughly.

I've updated the branch here https://github.com/cchamplin/go/commit/69a5cfc1dd0106fd8a2928a83e4c7001e81e89b8 with a fix for the bootstrapping problem, I think it should bootstrap correctly now.

I did have to copy over some /src/internal/syscall directories into cmd/internal/msvc. Is that going to be a huge problem? I'm not sure how to get access to the registry without using src/internal.

I did have to copy over some /src/internal/syscall directories into cmd/internal/msvc. Is that going to be a huge problem? I'm not sure how to get access to the registry without using src/internal.

I don't know how it is done nowadays, but I suspect cmd/dist can do that without you manually copying source files. I am certain Russ or Ian will help you when you are ready to submit code.

I think it is time for @bradfitz and @ianlancetaylor to decide how to proceed here. Do we want Go to support Microsoft build tools as well as gcc? We would have to install correspondent Microsoft tools onto our builders (we would have to do that before we start accepting CLs). We would, probably, have to introduce new environment variables. New documentation.

If the answer is yes, then @cchamplin would have to submit his code changes as per https://golang.org/doc/contribute.html Would you be willing to do that? The change is quite big, so it needs to be broken into smaller CLs, so they can be reviewed and submitted separately. Each change would have to have all.bat PASS before it can be submitted. It would be nice if we could see all CLs before we start submitting first CL.

Thank you.

Alex

Do we want Go to support Microsoft build tools as well as gcc?

Just a note this patch also facilitates #17014 which I already have a patch in process for.

If the answer is yes, then @cchamplin would have to submit his code changes as per https://golang.org/doc/contribute.html Would you be willing to do that? The change is quite big, so it needs to be broken into smaller CLs, so they can be reviewed and submitted separately.

Yes, and breaking it up shouldn't be a huge deal. I just need to know whether or not this is something that is likely to go in before I go through the effort to get all the code cleaned up, documentation written, tests created etc.

Do we want Go to support Microsoft build tools as well as gcc?

In my mind, the answer is yes. The compilers themselves are free with VS Community, and not having full Windows support is really frustrating. I shouldn't have to stand up a Linux box to compile github.com/Microsoft/hcsshim (for example), because a Windows toolchain isn't supported.

What would be the reason not to support a Windows toolchain?

I think it's fine in principle to support MSVC object files. We would need to have a builder.

My main concern is how much the format might change between Windows or MSVC releases. The ELF format used on most other platforms is very stable; we very rarely have to tweak the ELF support in any way other than adding support for new processors. Is the MSVC file format similarly stable?

The formats associated with the MSVC toolchain are fairly stable. The biggest issue is going to be supporting the tools themselves. Microsoft has a tendency to move things around (files and registry entries) between releases of Visual studio. Most of this is configurable (at least in the patch I wrote) but the possibility still exists that when a new major version of visual studio is released it may not be compatible as a build toolchain without changes to Go. All supported prior versions of visual studio would continue to work however.

As far as I know, the COFF format has reasonable stability if you expose yourself to just the C ABI (not C++). It is also extensively documented and LLVM provides a reference implementation.

@cchamplin I would recommend requiring users to invoke vcvarsall.bat before linking with MSVC. This will significantly reduce maintenance to near-zero.

We would need to have a builder.

@ianlancetaylor once MSVC objects have been proven to work as intended, do you believe there would be a need for both an msys/cygwin builder and an MSVC builder, or would there be only an MSVC builder?

I'm sure some people will prefer to use the cygwin tools, and they already work and we want them to continue to work, so I think we need two builders.

Hey it looks from the bottom of this thread that you've got your MSVC issues sorted out. But if you run into any issues, I'm on the MSVC team. Feel free to ping me on github or email ([email protected])

We would need to have a builder.

Yes. And we should probable start with changing our existing builders to have MS build tools installed. So we can use them as we accept CLs for this issue.

We can install both gcc tools and microsoft tools on all our builders. Can we run all.bat that tests both gcc and microsoft tools in single go? If not, then we would need to have different builders configured for different tools. What are the parameters that control what external compilers and linkers are used?

My main concern is how much the format might change between Windows or MSVC releases.

You don't get a compiler installed with Windows. You have to install it yourself. Just like we do with gcc. We install whatever version we like. We could even run different gcc versions on different builders.

Is the MSVC file format similarly stable?

I know nothing about that. I suspect you just use tools provided.

Alex

My main concern is how much the format might change between Windows or MSVC releases. The ELF format used on most other platforms is very stable; we very rarely have to tweak the ELF support in any way other than adding support for new processors. Is the MSVC file format similarly stable?

Yes, COFF is very stable.

@bradfitz and @ianlancetaylor what do we need to get a windows builder available to test changes for this issue? See my questions at https://github.com/golang/go/issues/20982#issuecomment-370719472

Thank you

Alex

is go support msvc object now?

Change https://golang.org/cl/110555 mentions this issue: debug/pe: parse the import directory correctly

@alexbrainman @bradfitz @ianlancetaylor :

I've done most of the required work I believe for getting the patch cleaned up and broken into more digestible pieces. I believe I am close to ready for actual submission to the project. I guess I need to know if I should wait for word on the builder situation before submitting the code or just do it asap?

You can send the CLs any time, but someone does need to set up a builder. Otherwise we have no way of testing them.

@johnsonj, could you add the MSVC compiler tools to our Windows images?

I'd ideally like to not add a new host type and just make 1-3 of our existing Windows host types have the MSVC tools in addition to Cygwin. And then we can add more builder config(s) on top of those modified host type(s).

/cc @andybons @bcmills as FYI (for how Windows stuff happens... we beg Jeff for help :))

@johnsonj, could you add the MSVC compiler tools to our Windows images?

@cchamplin I suggest you try to add whatever tools are required to the builder yourself. No one else, but you knows what is required. You can look in golang.org/x/build/env/windows directory for full instructions. In particular you, probably, want to add more lines into startup.ps1. Once you know what needs to change in that director, make the changes and send your change for review via https://golang.org/doc/contribute.html Once accepted, we could update builders using these instructions.

I'd ideally like to not add a new host type and just make 1-3 of our existing Windows host types have the MSVC tools in addition to Cygwin. And then we can add more builder config(s) on top of those modified host type(s).

We always had Mingw (gcc) C compiler and linker on windows. We never has different set of C build tools. Once we add support for Microsoft C compiler, would it be possible to have both Mingw and Microsoft C functions tested with a single run of all.bat ? Or do Mingw or Microsoft C exclude each other? Would we have to set some environment variable to point to Mingw or to Microsoft, but never both? I suppose I am trying to understand how the code needs to be structured and tested. Also this will determine how many different builders we need.

Alex

I'll look into baking the binaries in the image

Change https://golang.org/cl/112036 mentions this issue: env/windows: add visual studio tools to image

@johnsonj @bradfitz : Thanks for getting that into the builder.

@alexbrainman @bradfitz @ianlancetaylor : So as far as testing strategy for this I'm not sure exactly what to go for. We can take the path of running dist test for both toolchains on window and perform every test twice, or should we only test the cgo tests twice (once for gcc, once for msvc)?

So as far as testing strategy for this I'm not sure exactly what to go for. We can take the path of running dist test for both toolchains on window and perform every test twice, or should we only test the cgo tests twice (once for gcc, once for msvc)?

@cchamplin I do not have answer to your question. I suspect Ian knows the answer.

Alex

What is the cost of running all the tests twice instead of just cgo?

@mxplusb, we can just run a new build configuration for Windows and it'll run in parallel with our existing 3. A new builder config is fine. We add them regularly.

Just tell me how to make it different: set a different/new environment variable and then something will pick up on that, I assume?

It's probably take a few things to get this working.

Either we need to run inside of in a shell/environment where the go test that dist test executes will be ran inside of after the appropriate vsvars.bat has been ran/called

Or we can have dist execute the appropriate vsvars and extract the environment variables it needs and then set/pass those along to go test.

We'll also probably want some environment variable to tell dist or make.bat that we want to run the tests with -compiler msvc set.

Either we need to run inside of in a shell/environment where the go test that dist test executes will be ran inside of after the appropriate vsvars.bat has been ran/called

Or we can have dist execute the appropriate vsvars and extract the environment variables it needs and then set/pass those along to go test.

I would prefer if we don't run batch files from dist.exe or go.exe. It is troublesome to deal with batch files.

We'll also probably want some environment variable to tell dist or make.bat that we want to run the tests with -compiler msvc set.

I was hoping Ian would know what the best approach here is. From what I know we support different C compilers on Unix too (gcc or clang). Surely our build process allows us to test for different C compilers.

Alex

I didn't notice before that you are planning to use go build -compiler msvc. That doesn't make sense. The go build -compiler option takes the name of a Go compiler (currently either gc or gccgo). It doesn't take the name of a C compiler. The C compiler is passed in the CC environment variable. The default C compiler is set by setting the CC_FOR_TARGET environment variable when running make.bat, as documented in the comments in make.bat.

I would expect that we would have a builder for which we arrange to set the CC_FOR_TARGET environment variable to msvc before running all.bat. I don't see why we would have to do anything else.

The C compiler is passed in the CC environment variable. The default C compiler is set by setting the CC_FOR_TARGET environment variable when running make.bat, as documented in the comments in make.bat.

Thank you Ian for explaining.

@cchamplin I hope you can adjust your changes to fit into existing model. Let us know, if you have any trouble. Thank you.

I would expect that we would have a builder for which we arrange to set the CC_FOR_TARGET environment variable to msvc before running all.bat.

We have 3 amd64 windows builders on https://build.golang.org. Should we replace one into MSVC one? Do we want 386 MSVC builder?

Alex

@ianlancetaylor @alexbrainman Understood about the -compiler flag. I don't think there is an easy way to make just setting CC="msvc" (or really CC="cl.exe") to work. CC still needs to point to a GCC location for MSVC builds to work. The msvc compiler toolset does not have the tooling available to allow CGO to perform the introspection it needs to (definition lookup, type resolution) so GCC is still required on MSVC builds, GCC is just not used in the actual compilation/linking phase.

Would adding a new flag to go build et all be acceptable (toolchain or something)? I've already had to add a toolchain flag to cgo I believe.

Also setting CC_FOR_TARGET before all.bat looks like it may have negative consequences because it would affect the compiler used in bootstrapping. I don't believe MSVC can be used during the bootstrapping process (I'm not 100% on this, I haven't actually tried but I highly doubt it will work)

I see, for MSVC we need to have both GCC and MSVC? Where GCC is used only by the cgo tool? That seems somewhat unfortunate, since it means we would be absolutely relying on GCC and MSVC implementing exactly the same ABI, but I guess that is fairly likely.

I guess we could add another environment variable CC_FOR_CGO, which could be set to GCC.

The CC_FOR_TARGET used by make.bat will be used to build runtime/cgo. It's not clear to me how cgo could work at all if runtime/cgo is not built by the C compiler you are using.

Perhaps we should start by getting things working when make.bat is invoked setting CC_FOR_TARGET to cl and CGO_ENABLED to 0.

Thanks @ianlancetaylor:

I see, for MSVC we need to have both GCC and MSVC? Where GCC is used only by the cgo tool? That seems somewhat unfortunate, since it means we would be absolutely relying on GCC and MSVC implementing exactly the same ABI, but I guess that is fairly likely.

I agree it is unfortunate, and would be happy to learn of some alternative, I haven't yet found one. There is certainly risk building this way (eg the cgo code being built has different type defs or defines depending on what compiler it thinks it's being built by). So builds might not always succeed or work correctly without some tweaking (there is a mechanism to handle this in my implementation, but it's a bit of a manual process). Sadly I think this is a risk people will have to take if they want to use the MSVC toolchain and all we can do is document it for now.

On a similar note there is still no real path forward for providing complex64 and complex128 support in MSVC built cgo programs simply because the MSVC compiler is not c99 compliant.

I guess we could add another environment variable CC_FOR_CGO, which could be set to GCC.

So the currently implementation I have is if go build is instructed to build with MSVC it will by default look for cl.exe on the path but this can be overridden with the environment variable MSCC. CGO will continue to use the CC environment variable falling back to gcc/whatever is sitting in the generated zdefaultcc.go

The CC_FOR_TARGET used by make.bat will be used to build runtime/cgo. It's not clear to me how cgo could work at all if runtime/cgo is not built by the C compiler you are using.

In the current implementation cgo will happily work for msvc binaries even if the entire go toolchain has been built by gcc. e.g. MSVC code is added to go/src. all.bat is ran building the go environment using gcc. After that go/bin/go.exe can be used to build with the msvc toolchain (assuming flags are supplied). I might be misunderstanding what you're saying here though.

The complexity around this because telling go we want to build with msvc is more complicated then just switching the compiler to cl. The MSVC toolchain is very different from the gcc toolchain. In MSVC an environment needs to be setup via running inside a vcvars.bat (or doing what v8 does for example which is running vcvars.bat and extracting all the information it needs from the environment variables it sets up and then using that in their build process). After we are in a MSVC environment the major difference become the tools themselves cl.exe is used to compile C and C++ files, ml.exe and ml64.exe are used to assemble files, and finally link.exe (ms) is used to link everything together. So it's more than just setting CC=cl.exe and passing it the right flags. Does that make sense/help clarify things?

To further clarify on

The CC_FOR_TARGET used by make.bat will be used to build runtime/cgo. It's not clear to me how cgo could work at all if runtime/cgo is not built by the C compiler you are using.

go build is selecting the appropriate files from runtime/cgo during the build process (eg go build/go test/go run -- not the builder process) when the msvc toolchain has been selected.

I'm open to suggestions for how this should work, but it should not require a new command line option for the go tool. Can we write a pure Go program that acts like GCC as far as the go tool is concerned but actually runs MSVC?

Can we write a pure Go program that acts like GCC as far as the go tool is concerned but actually runs MSVC?

I believe that that might be workable, let me see if I can get something going on that path.

Additionally I guess I need to clarify if adding command line flags to other tools is going to be an issue as well. At the moment the comprehensive patch for this is creating several new command line flags in various tools

  • cmd/cgo

    • -toolchain [gcc,msvc] (defaults to gcc) Toolchain to use when building cgo output files

  • cmd/link

    • -rlocbss (defaults to false) Relocate .bss to .data

    • -toolchain [gcc,msvc] (defaults to gcc) Toolchain to be used for external linking

we also create the following build options/cgo environment variables:

  • MSCXX
  • MSCC
  • MSCFLAGS
  • MSCPPFLAGS
  • MSCXXFLAGS
  • MSLDFLAGS

After a few minutes of thought there's a few hurdles that might be difficult to overcome if we just proxy the compiler commands thought a different program all together

  1. go build needs to know what the external toolchain is to determine which runtime/cgo files to build, otherwise it would end up just passing the incompatible gcc versions to our proxy command. We might be able to hack around this inside the proxy itself but it would be brittle and likely to break if additional files ever got added to runtime/cgo.
  2. cmd/cgo needs to know what kind of external toolchain is being used to pick which version of the output C code to use
  3. The proxy program would need to some mechanism of knowing whether or not it should be executing gcc (as when called by cmd/cgo) or to execute cl.exe which could be passed in but that means the caller would need to know what the toolchain is or we'd have cmd/cgo passing non-existent flags to gcc.
  4. go build calls cmd/link which also needs to know what the toolchain so that it can perform the .bss to .data relocation and so that it can pass specific flags to the linker (we might be able to handle this latter part in the proxy itself though)

Would specifying a environment variable to have go build use to determine msvc builds work? Although I think it sets the bar higher for someone that just wants to build a program with msvc, as they now have to set multiple environment variables in addition to running inside of vsvars.bat (at a minimum CC and UNDECIDED_TURN_ON_MSVC_BUILD_VAR)

Another alternative would be to have go build run a bogus noop command on the specified CC and then parse the logo/header to detect if it's msvc or gcc and proceed with the appropriate toolchain adjustments at that point...not sure how brittle this is or how it'd work with msvc compatable toolchains like clang.

Discovered this Github issue recently, and this work is very exciting to me. I am hoping this lands somewhere in the near future.

My intention is to use CGo on Windows as wrappers to some 3rd-party code that has been built using MSVC, that I cannot just rebuild from sources using mingw-w64. I have some relatively serious test-cases to run it thru, so can validate it pretty well.

Anyhow, thanks to @cchamplin and whoever else is working on it, and please let me know anyway I can help.

@cchamplin I know enough to be dangerous about the compiler toolchains in Windows and cgo that I might be able to assist. Would you be able to catch me up on some of what you've been working on? I'll see what I can do to assist. I know the use case @deadprogram has so I have a good advanced test bed when we're ready to test.

Hey @mxplusb, the patch is actually more or less ready to go. If I can find the time I'll submit the changes to gerrit tonight or tomorrow which will get us underway for getting everything reviewed, updated, and approved.

Change https://golang.org/cl/133937 mentions this issue: cmd/link: Add flag rlocbss to relocate .bss data to .data

Change https://golang.org/cl/133938 mentions this issue: runtime/cgo: MSVC toolchain support in cgo native code

Change https://golang.org/cl/133939 mentions this issue: cmd/cgo: Add toolchain flag to cgo command for MSVC support

Change https://golang.org/cl/133946 mentions this issue: cmd/compile: Add support for MSVC toolchain to go build

It looks like I typoed the issue in some of the commits. I'll let the review process get underway and determine the best way to fix.

Change https://golang.org/cl/133943 mentions this issue: cmd/cgo: Add support for CC_FOR_CGO environment variable

Change https://golang.org/cl/133942 mentions this issue: tests: Update various tests to prepare for MSVC compiler toolchain

Change https://golang.org/cl/133940 mentions this issue: misc/cgo: Adjust tests to be compatible with MSVC toolchain support

Change https://golang.org/cl/133941 mentions this issue: runtime: Add runtime.CompilerType to denote between host compiler type

Change https://golang.org/cl/133945 mentions this issue: cmd/link: Add external toolchain support for MSVC

Change https://golang.org/cl/133944 mentions this issue: cmd/compile, cgo: Add support for MSVC flags

@cchamplin can you describe what was the final decision? How does MSVC support look like? What command line flags will be added? What an user will have to do to their environment in addition to downloading MSVC? Thanks

Also, how can we try this patch? I couldn't find your commits on the repo.

@rasky It's still in a bit of flux as the changes go through review. There will likely not be any specific command line flags that need to be set, simply building cgo applications where the CC environment variable is pointed to MSVC or no CC is set an cl.exe is on the path but gcc is not.

@blizzardplus Similar to above things are in a lot of flux right now with the review, so it might be a bit early to start trying it out (up to you though). All the related changes were posted back to this issue by gopherbot, you can find them there.

@cchamplin is there support for cross compiling using either gcc or clang? If so, are they tested?

@cchamplin I don't see any activity on the CLs since 9/11, is there forward progress on this or is it in need of additional resources?

@cchamplin for this to work for c-archive the ctors section should be changed to .CRT$XCU (https://msdn.microsoft.com/en-us/library/bb918180.aspx) - If this is not done then the golang runtime will not be initialized. Also, it turns out, if you set it to this it works fine for gcc as well - gcc stil ltakes those functions, moves them to a text section, and modifies main to call them before the real main. Visual Studio can understand .a archive files and, as the interface of the pre-compiled/assembled files is C, there should be no issue just having this in the archive. There is a warning about 2 .text sections but simple cases seem to function fine (needs more testing for confirmation there are no issues).

Also, for this to work for c-shared the functions to be exported must be decorated __declspec(dllexport), but I can't seem to find where this expansion of //export occurs.

@blizzardplus There is a followup patch to this to support clang-MSVC. I'm not sure what you mean by cross compiling in this case? You will not be able to use the MSVC toolchain on non-windows systems. Other cross compiling functionality should not change.

@kshelton Unfortunately this is a very busy time of year for me as I have other projects in the works and conferences I'm presenting at. I may not be able to jump back into getting the required refactoring into the patch until December.

@kshelton Thanks. I haven't double checked the patch, I assume that I developed it with the assumption that c-archive would not work on MSVC, or specifically disabled it? I went down the path of trying to properly decorate exports when I was trying to get plugin/shared working on windows. There are other issues I believe, but I could be conflating two issues. There are real problems with how go places it's shared code into the PLT/GOT for ELF vs Windows .edata,idata and the EAT. I believe most of the issues I faced were around relocations and being unable to get them to work. If anyone has insight in this that would be wonderful. See #19282

@cchamplin I tried to see, if I can build Go on top of your CL

https://go-review.googlesource.com/c/go/+/133946/3

I used this batch file to set my enviroment

set TERM=msys
set MYHOME=c:\users\alex\dev
set GOROOT=%MYHOME%\go
set GOROOT_BOOTSTRAP=%MYHOME%\go1.4.3
set GOPATH=%MYHOME%
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
set CC=cl
set PATH=%PATH%;%MYHOME%\my\bin\;%GOROOT%\bin
cd %GOROOT%\src
CMD

and I get this error when I run make.bat

C:\Users\Alex\Desktop>set TERM=msys

C:\Users\Alex\Desktop>set MYHOME=c:\users\alex\dev

C:\Users\Alex\Desktop>set GOROOT=c:\users\alex\dev\go

C:\Users\Alex\Desktop>set GOROOT_BOOTSTRAP=c:\users\alex\dev\go1.4.3

C:\Users\Alex\Desktop>set GOPATH=c:\users\alex\dev

C:\Users\Alex\Desktop>call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.0.26730.12
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'
Microsoft Windows [Version 10.0.17134.407]
(c) 2018 Microsoft Corporation. All rights reserved.

c:\Users\Alex\dev\go\src>make
Building Go cmd/dist using c:\users\alex\dev\go1.4.3
go tool dist: cannot invoke C compiler "cl": exit status 2

Go needs a system C compiler for use with cgo.
To set a C compiler, set CC=the-compiler.
To disable cgo, set CGO_ENABLED=0.

Command output:

Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25507.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

cl : Command line warning D9002 : ignoring unknown option '--help'
cl : Command line error D8003 : missing source filename

The system cannot find the batch label specified - fail

c:\Users\Alex\dev\go\src>

What am I missing here?

Thank you.

Alex

@alexbrainman

What am I missing here?

You likely at a minimum need to set CC_FOR_CGO=gcc. I've also never personally tried building go itself with MSVC. I'm not sure if this would work/should be supported.

-C

You likely at a minimum need to set CC_FOR_CGO=gcc.

Your https://go-review.googlesource.com/c/go/+/133940 contains some tests. How do I verify the tests pass? What are exact steps?

Also Go builders https://build.golang.org run %GOROOT%\src\all.bat to verify all tests (including the ones listed in https://go-review.googlesource.com/c/go/+/133940) pass. How do you propose we modify Go code to make %GOROOT%\src\all.bat run your adjusted tests from https://go-review.googlesource.com/c/go/+/133940. Perhaps all.bat can test both gcc and msvc versions of the tests. Perhaps we can use some environment variable that tells all.bat which version of tests to run - ad then we could have two different builders run different versions of the tests. Did you think about that all?

I've also never personally tried building go itself with MSVC. I'm not sure if this would work/should be supported.

You can run all.bat to successful completion without gcc installed. Current version of Go only needs another version of Go (at least go1.4) to build. Only Cgo needs C compiler, I was under impression that your changes implement Cgo version that is build by MSVC. But, perhaps, I am mistaken. Please correct me if I am wrong.

Thank you.

Alex

@alexbrainman

Your https://go-review.googlesource.com/c/go/+/133940 contains some tests. How do I verify the tests pass? What are exact steps?

Perhaps all.bat can test both gcc and msvc versions of the tests. Perhaps we can use some environment variable that tells all.bat which version of tests to run - ad then we could have two different builders run different versions of the tests. Did you think about that all?

Yes, this exact mechanism is used in https://go-review.googlesource.com/c/go/+/133946/. Since all calls into run.bat on windows, the mechanism for calling cgo tests was placed there. So you or the builder would start with a normal setup CC=gcc. Then you would set GOTESTMSVC=1 and GOVSVARSPATH=some vsvars.bat path. After that building with all.bat should run through both gcc and msvc cgo tests.

You can run all.bat to successful completion without gcc installed. Current version of Go only needs another version of Go (at least go1.4) to build. Only Cgo needs C compiler, I was under impression that your changes implement Cgo version that is build by MSVC. But, perhaps, I am mistaken. Please correct me if I am wrong.

You are correct. Go itself should build fine. There might be issues when it gets to the test, since run.bat was modified to set some environment variables when GOTESTMSVC=1. We could probably try to detect if go is just being built with msvc, and then set the appropriate dist environment variables so tests don't fail.

Then you would set GOTESTMSVC=1 and GOVSVARSPATH=some vsvars.bat path. After that building with all.bat should run through both gcc and msvc cgo tests.

I tried that.

I used

commit e56d52f66b95b87001867a2487a11bd961f40d4d (HEAD)
Author: Caleb Champlin <[email protected]>
Date:   Sat Sep 8 00:26:32 2018 -0600

    cmd/compile: add support for MSVC toolchain to go build

    Allows building with MSVC as an external compiler/linker.

    Setting CC=cl.exe inside an MSVC environment will automatically
    build cgo executables using MSVC as the external compiler and
    linker.

    For the builders setting the environment variable GOVSVARSPATH
    to the location of a msvsvars.bat file and setting the
    environment variable GOTESTMSVC=1 will automatically cause
    all.bat to run tests and compiler with both gcc and MSVC.

    Updates #20982

    Change-Id: I44be1f43aa0d53a688c595bc8336e0364b809ced

I run this batch file first:

set TERM=msys
set MYHOME=c:\users\alex\dev
set GOROOT=%MYHOME%\go
set GOROOT_BOOTSTRAP=%MYHOME%\go1.4.3
set GOPATH=%MYHOME%
set MINGW=%MYHOME%\mingw64_4.9.1

set GOTESTMSVC=1
set GOVSVARSPATH="C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"

set PATH=%PATH%;%MINGW%\bin;%MYHOME%\my\bin\;%GOROOT%\bin
cd %GOROOT%\src
CMD

and then run all.bat. all.bat fails with

--- FAIL: TestDocsUpToDate (0.00s)
    help_test.go:26: alldocs.go is not up to date; run mkalldocs.sh to regenerate it
go test proxy starting
go test proxy running at GOPROXY=http://127.0.0.1:52023/mod
go proxy: no archive w.1 v1.2.0
go proxy: no archive x.1 v1.0.0
go proxy: no archive z.1 v1.2.0
go proxy: no archive rsc.io v1.5.0
go proxy: no archive example.com/unused v0.0.0
go proxy: no archive example.com/unused v0.0.0
go proxy: no archive sub.1 v1.0.0
go proxy: no archive badsub.1 v1.0.0
go proxy: no archive versioned.1 v1.0.0
go proxy: no archive versioned.1 v1.1.0
go proxy: no archive golang.org/x/text/language 14c0d48
go proxy: no archive golang.org/x/text/language 14c0d48
go proxy: no archive golang.org/x/text/language 14c0d48
go proxy: no archive golang.org/x/text/foo 14c0d48
go proxy: no archive golang.org/x 14c0d48
go proxy: no archive golang.org 14c0d48
go proxy: no archive example.com/split/subpkg v1.0.0
FAIL
FAIL    cmd/go  147.247s
ok      cmd/go/internal/cache   13.115s
ok      cmd/go/internal/dirhash 0.518s
ok      cmd/go/internal/generate        0.180s
ok      cmd/go/internal/get     0.761s
ok      cmd/go/internal/imports 0.212s
ok      cmd/go/internal/load    1.050s
ok      cmd/go/internal/modconv 1.596s
ok      cmd/go/internal/modfetch        0.881s
ok      cmd/go/internal/modfetch/codehost       0.179s
ok      cmd/go/internal/modfile 0.193s
ok      cmd/go/internal/modload 2.820s
ok      cmd/go/internal/module  0.860s
ok      cmd/go/internal/mvs     0.255s
ok      cmd/go/internal/par     0.107s
ok      cmd/go/internal/search  0.087s
ok      cmd/go/internal/semver  0.140s
ok      cmd/go/internal/txtar   0.249s
ok      cmd/go/internal/web2    0.136s
ok      cmd/go/internal/work    0.200s
ok      cmd/gofmt       0.216s
ok      cmd/internal/buildid    0.522s
ok      cmd/internal/dwarf      0.077s
ok      cmd/internal/edit       0.160s
ok      cmd/internal/goobj      2.430s
ok      cmd/internal/obj        0.103s
ok      cmd/internal/obj/arm64  0.190s
ok      cmd/internal/obj/x86    0.845s
ok      cmd/internal/objabi     0.063s
ok      cmd/internal/src        0.093s
ok      cmd/internal/test2json  0.253s
ok      cmd/link        6.285s
ok      cmd/link/internal/ld    24.147s
ok      cmd/link/internal/sym   0.887s
ok      cmd/nm  7.678s
ok      cmd/objdump     2.772s
ok      cmd/pack        3.256s
ok      cmd/trace       0.449s
ok      cmd/vendor/github.com/google/pprof/internal/binutils    0.479s
ok      cmd/vendor/github.com/google/pprof/internal/driver      6.103s
ok      cmd/vendor/github.com/google/pprof/internal/elfexec     0.079s
ok      cmd/vendor/github.com/google/pprof/internal/graph       0.455s
ok      cmd/vendor/github.com/google/pprof/internal/measurement 0.066s
ok      cmd/vendor/github.com/google/pprof/internal/report      0.154s
ok      cmd/vendor/github.com/google/pprof/internal/symbolizer  0.096s
ok      cmd/vendor/github.com/google/pprof/internal/symbolz     0.078s
ok      cmd/vendor/github.com/google/pprof/profile      0.527s
ok      cmd/vendor/github.com/ianlancetaylor/demangle   0.109s
ok      cmd/vendor/golang.org/x/arch/arm/armasm 0.424s
ok      cmd/vendor/golang.org/x/arch/arm64/arm64asm     0.537s
ok      cmd/vendor/golang.org/x/arch/ppc64/ppc64asm     0.155s
ok      cmd/vendor/golang.org/x/arch/x86/x86asm 0.239s
ok      cmd/vendor/golang.org/x/crypto/ssh/terminal     0.174s
ok      cmd/vendor/golang.org/x/sys/windows     0.334s
ok      cmd/vendor/golang.org/x/sys/windows/registry    0.199s
ok      cmd/vendor/golang.org/x/sys/windows/svc 0.316s
ok      cmd/vendor/golang.org/x/sys/windows/svc/eventlog        0.089s
ok      cmd/vendor/golang.org/x/sys/windows/svc/mgr     0.432s
ok      cmd/vet 6.392s
ok      cmd/vet/internal/cfg    0.102s
2018/12/16 15:55:18 Failed: exit status 1

Also setting GOTESTMSVC and GOVSVARSPATH might work for people executing all.bat. But what will other users of your change do when they need to use MSVC for Cgo? What is your plan for that?

Alex

@alexbrainman
I'm not sure what's going on. That test is failing for me on master

> git status
On branch master
Your branch is up to date with 'origin/master'

> cd src\cmd\go
> go test .\help_test.go
--- FAIL: TestDocsUpToDate (0.00s)
    help_test.go:26: alldocs.go is not up to date; run mkalldocs.sh to regenerate it

In help.go https://github.com/golang/go/blob/c040786f37246f40ae29402fbdb6e97031a21713/src/cmd/go/internal/help/help.go#L37
it iterates through base.Go.Commands. Which is initialized in main.go https://github.com/golang/go/blob/c040786f37246f40ae29402fbdb6e97031a21713/src/cmd/go/main.go#L43
but I'm not sure how that init function would ever get called in a test executable, so I have no idea how master is passing tests right now, as I would think this should be failing across the board.


Also setting GOTESTMSVC and GOVSVARSPATH might work for people executing all.bat. But what will other users of your change do when they need to use MSVC for Cgo? What is your plan for that?

So in a situation where you just want to build some Cgo code with MSVC

call vsvars64.bat
set CC_FOR_CGO=gcc
set CC=cl.exe
go build

should be all you need I think

I'm not sure how that init function would ever get called in a test executable, so I have no idea how master is passing tests right now, as I would think this should be failing across the board.

When using go test with a file in cmd/go, the go tool will build the cmd/go package and then build the tests, generate the test driver, build that, and link everything together into a new main package. This happens even though cmd/go is itself a main package. That is, the main package in cmd/go/*.go will be treated as non-main package for purposes of testing. This enables tests to call functions defined in the main package if appropriate.

Sorry, I am slow to reply. But I had no free time to look into this again.

I'm not sure what's going on. That test is failing for me on master

I did not use master. I used your e56d52f66b95b87001867a2487a11bd961f40d4d commit. Didn't you run all.bat on that commit before sending it out for review? Did all.bat succeed? Can you try to run all.bat on that commit again to see, if it is something to do with my system configuration. Thank you.

So in a situation where you just want to build some Cgo code with MSVC

call vsvars64.bat
set CC_FOR_CGO=gcc
set CC=cl.exe
go build

should be all you need I think

2 environment variables is, probably, too complicated for the average user. And CC_FOR_CGO=gcc looks strange to me - why do we use gcc to build code with MSVC?

Alex

@alexbrainman

I did not use master. I used your e56d52f66b95b87001867a2487a11bd961f40d4d commit. Didn't you run all.bat on that commit before sending it out for review? Did all.bat succeed? Can you try to run all.bat on that commit again to see, if it is something to do with my system configuration. Thank you.

I think it was something to do with my system configuration that was giving me weird results when running the tests. I think I've fixed the issue and pushed some updates to the change set.

2 environment variables is, probably, too complicated for the average user. And CC_FOR_CGO=gcc looks strange to me - why do we use gcc to build code with MSVC?

It should actually probably be CC_FOR_CGO=gcc.exe, my mistake. But to answer your question; from gerrit:

PS1, Line 1324:
cgo still needs gcc available for type analysis. If CC is set to an MSVC compiler cgo needs a means to call GCC which is provided by CC_FOR_CGO. Unfortunately the MSVC toolchain has no mechanism to provide the same type information that is being gathered from gcc. Theoretically a go specific tool could be created to gather the type information that is used by cgo but that might be out of scope and possibly considerable effort (I've personally never written anything to perform static type analysis of C/C++ code).

I also seem to have broken something with the cache clear between runs for GCC vs MSVC, I'm not sure why some tests are still acting as if they are cached.

I think I've fixed the issue and pushed some updates to the change set.

This setup https://github.com/golang/go/issues/20982#issuecomment-447618566 on

commit 5479fc9fe61fb998082fea5cb423314cc1afa649 (HEAD)
Author: Caleb Champlin <[email protected]>
Date:   Sat Sep 8 00:26:32 2018 -0600

    cmd/compile: add support for MSVC toolchain to go build

    Allows building with MSVC as an external compiler/linker.

    Setting CC=cl.exe inside an MSVC environment will automatically
    build cgo executables using MSVC as the external compiler and
    linker.

    For the builders setting the environment variable GOVSVARSPATH
    to the location of a msvsvars.bat file and setting the
    environment variable GOTESTMSVC=1 will automatically cause
    all.bat to run tests and compiler with both gcc and MSVC.

    Updates #20982

    Change-Id: I44be1f43aa0d53a688c595bc8336e0364b809ced

takes me further, but still fails with

##### API check
+pkg go/build, type Context struct, CompilerType string
+pkg go/build, type Package struct, CgoMSCFLAGS []string
+pkg go/build, type Package struct, CgoMSCPPFLAGS []string
+pkg go/build, type Package struct, CgoMSCXXFLAGS []string
+pkg go/build, type Package struct, CgoMSLDFLAGS []string
+pkg os, method (*File) SyscallConn() (syscall.RawConn, error)

ALL TESTS PASSED

**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.0.26730.12
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

# runtime/cgo
msvc_libinit_windows.c
msvc_libinit_windows.c(133): error C2220: warning treated as error - no 'object' file generated
msvc_libinit_windows.c(133): warning C4710: 'int fprintf(FILE *const ,const char *const ,...)': function not inlined
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\stdio.h(826): note: see declaration of 'fprintf'
go tool dist: FAILED: go install -gcflags=all= -ldflags=all= std cmd: exit status 2

c:\Users\Alex\dev\go\src>

It should actually probably be CC_FOR_CGO=gcc.exe, my mistake.

I am not concerned about .exe in gcc.exe. My issues are

  • you need both gcc and MSVC tools to use MSVC (it would be hard to explain it to our users), but I understand that nothing can be done about it;

  • I needed both environment variables CC_FOR_CGO=gcc and CC=cl.exe set - maybe we can just assume CC_FOR_CGO is always set to gcc?

But to answer your question; from gerrit:

Thank you for explaining.

Alex

@alexbrainman

# runtime/cgo
msvc_libinit_windows.c
msvc_libinit_windows.c(133): error C2220: warning treated as error - no 'object' file generated
msvc_libinit_windows.c(133): warning C4710: 'int fprintf(FILE *const ,const char *const ,...)': function not inlined
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\stdio.h(826): note: see declaration of 'fprintf'
go tool dist: FAILED: go install -gcflags=all= -ldflags=all= std cmd: exit status 2

I'm not sure how that warning would be occurring unless you somehow have a different/old copy of msvc_libinit_windows.c. Can you confirm it has this line in it:

// src/runtime/cgo/msvc_libinit_windows.c
#pragma warning(disable:4668 4255 4710)

C4710 should be suppressed for the stdio include. I do see that your version of MSVC is slightly different than mine, which could also be a culprit. I'm not sure why they would actively ignore that suppression in a different version though.

You could also try making a change to include the suppression globally no matter what

// src/cmd/go/internal/work/exec.go
// Line 2719
cgoMSCPPFLAGS = append(cgoMSCFLAGS, "/wd4668") // this should be cgoMSCFLAGS = append(cgoMSCFLAGS, "/wd4668") I'll fix the change set to correct
+cgoMSCFLAGS = append(cgoMSCFLAGS, "/wd4710") 

I needed both environment variables CC_FOR_CGO=gcc and CC=cl.exe set - maybe we can just assume CC_FOR_CGO is always set to gcc?

I think gco checking if gcc exists if CC_FOR_CGO is not set would be fine. CC_FOR_CGO is more for people like me who don't have gcc in their path. My CC_FOR_CGO looks like this CC_FOR_CGO=I:\Development\tmd-gcc\bingcc.exe. If we don't want to support the usecase of GCC not being on the path for MSVC builds let me know and I can get rid of the environment variable all together.

I tried again this version

commit 6741b7009d1894b5bf535d82ad46f4a379651670 (HEAD)
Author: Caleb Champlin <[email protected]>
Date:   Sat Sep 8 00:26:32 2018 -0600

    cmd/compile: add support for MSVC toolchain to go build

    Allows building with MSVC as an external compiler/linker.

    Setting CC=cl.exe inside an MSVC environment will automatically
    build cgo executables using MSVC as the external compiler and
    linker.

    For the builders setting the environment variable GOVSVARSPATH
    to the location of a msvsvars.bat file and setting the
    environment variable GOTESTMSVC=1 will automatically cause
    all.bat to run tests and compiler with both gcc and MSVC.

    Updates #20982

    Change-Id: I44be1f43aa0d53a688c595bc8336e0364b809ced

and I get the same error

##### API check
+pkg go/build, type Context struct, CompilerType string
+pkg go/build, type Package struct, CgoMSCFLAGS []string
+pkg go/build, type Package struct, CgoMSCPPFLAGS []string
+pkg go/build, type Package struct, CgoMSCXXFLAGS []string
+pkg go/build, type Package struct, CgoMSLDFLAGS []string
+pkg os, method (*File) SyscallConn() (syscall.RawConn, error)

ALL TESTS PASSED

**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.0.26730.12
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

# runtime/cgo
msvc_libinit_windows.c
msvc_libinit_windows.c(133): error C2220: warning treated as error - no 'object' file generated
msvc_libinit_windows.c(133): warning C4710: 'int fprintf(FILE *const ,const char *const ,...)': function not inlined
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\stdio.h(826): note: see declaration of 'fprintf'
go tool dist: FAILED: go install -gcflags=all= -ldflags=all= std cmd: exit status 2

c:\Users\Alex\dev\go\src>

Can you confirm it has this line in it:

// src/runtime/cgo/msvc_libinit_windows.c
#pragma warning(disable:4668 4255 4710)

This is the beginning of my src/runtime/cgo/msvc_libinit_windows.c file

// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build cgo

#define WIN64_LEAN_AND_MEAN
// Suppress MSVC specific warnings.
// C4668: symbol' is not defined as a preprocessor macro, 
// replacing with '0' for 'directives'.
// C4255: function' : no function prototype given: converting '()' 
// to '(void)'.
// C4710: function' : function not inlined
#pragma warning(disable:4668 4255 4710)
#include <windows.h>
#include <process.h>
...

You could also try making a change to include the suppression globally no matter what

// src/cmd/go/internal/work/exec.go
// Line 2719
cgoMSCPPFLAGS = append(cgoMSCFLAGS, "/wd4668") // this should be cgoMSCFLAGS = append(cgoMSCFLAGS, "/wd4668") I'll fix the change set to correct
+cgoMSCFLAGS = append(cgoMSCFLAGS, "/wd4710") 

I made this change:

c:\Users\Alex\dev\go\src>git diff
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 49d1d849f5..9fb442ab95 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -2717,6 +2717,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcMSCFLAGS, pc
        cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...)
        if cfg.BuildContext.CompilerType == "msvc" {
                cgoMSCFLAGS = append(cgoMSCFLAGS, "/wd4668")
+               cgoMSCFLAGS = append(cgoMSCFLAGS, "/wd4710")

                cgoMSCPPFLAGS = append(cgoMSCPPFLAGS, pcMSCFLAGS...)
                cgoMSCPPFLAGS = append(cgoMSCPPFLAGS, "/c")

c:\Users\Alex\dev\go\src>

but I see the same error

##### API check
+pkg go/build, type Context struct, CompilerType string
+pkg go/build, type Package struct, CgoMSCFLAGS []string
+pkg go/build, type Package struct, CgoMSCPPFLAGS []string
+pkg go/build, type Package struct, CgoMSCXXFLAGS []string
+pkg go/build, type Package struct, CgoMSLDFLAGS []string
+pkg os, method (*File) SyscallConn() (syscall.RawConn, error)

ALL TESTS PASSED

**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.0.26730.12
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

# runtime/cgo
msvc_libinit_windows.c
msvc_libinit_windows.c(133): error C2220: warning treated as error - no 'object' file generated
msvc_libinit_windows.c(133): warning C4710: 'int fprintf(FILE *const ,const char *const ,...)': function not inlined
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\stdio.h(826): note: see declaration of 'fprintf'
go tool dist: FAILED: go install -gcflags=all= -ldflags=all= std cmd: exit status 2

c:\Users\Alex\dev\go\src>

Alex

I was trying to reproduce this last night and I was unable to. I'm wondering if you are experiencing this: https://developercommunity.visualstudio.com/content/problem/35734/c-cannot-disable-specific-warnings-with-pragmas-wh.html

Is it possible for you to update to the latest SDK and Build Tools? I'm on SDK 10.0.17763.0. and version 15.9.28307.222 of the build tools/devenv

I'm trying to build a Windows DLL with MSVC, but it looks like Go attempts to disable some warning with number more than 65535 (or not a number), which causes MSVC to stop.

image

No. Werror is a GCC option, to treat every warning as an error.

For MSVC you should use WX.

@pravic good point, how would i do that? I'm not setting it, all I'm doing is:

set CC=cl.exe
go build -o next.dll .\main.go

@galich Unfortunately, I can't help because I don't follow the MSVC integration.

For example, where are the patches related to this thread? In master or somewhere else?

@pravic you can find the changes here: https://go-review.googlesource.com/c/go/+/133946/5

@mxplusb thanks for the link, it seems that my particular issue with /Werror is resolved there.
Do you know when this is coming to beta/nightly/stable?

Is it possible for you to update to the latest SDK and Build Tools? I'm on SDK 10.0.17763.0. and version 15.9.28307.222 of the build tools/devenv

@cchamplin finally I managed to update my MSVC. I have now

c:\Users\Alex\dev\go\src>%GOVSVARSPATH%
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.0
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

c:\Users\Alex\dev\go\src>cl
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27030.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

usage: cl [ option... ] filename... [ /link linkoption... ]

c:\Users\Alex\dev\go\src>

I tried again this version

commit 6741b7009d1894b5bf535d82ad46f4a379651670 (HEAD)
Author: Caleb Champlin <[email protected]>
Date:   Sat Sep 8 00:26:32 2018 -0600

    cmd/compile: add support for MSVC toolchain to go build

    Allows building with MSVC as an external compiler/linker.

    Setting CC=cl.exe inside an MSVC environment will automatically
    build cgo executables using MSVC as the external compiler and
    linker.

    For the builders setting the environment variable GOVSVARSPATH
    to the location of a msvsvars.bat file and setting the
    environment variable GOTESTMSVC=1 will automatically cause
    all.bat to run tests and compiler with both gcc and MSVC.

    Updates #20982

    Change-Id: I44be1f43aa0d53a688c595bc8336e0364b809ced

and all.bat completes successfully. But I get these warnings.

##### ../misc/cgo/stdio

##### ../misc/cgo/life

##### ../misc/cgo/test
# _/c_/Users/Alex/dev/go/misc/cgo/test
issue28896.cgo2.c
.\issue28896.go(30): warning C4820: '<anonymous-tag>': '4' bytes padding added after data member 'g1'
.\issue28896.go(36): warning C4820: '<anonymous-tag>': '4' bytes padding added after data member 'f2'
# _/c_/Users/Alex/dev/go/misc/cgo/test
cthread_windows.c
c:\users\alex\dev\go\misc\cgo\test\cthread_windows.c(39) : warning C5045: Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
c:\users\alex\dev\go\misc\cgo\test\cthread_windows.c(37) : note: index 'i' range checked by comparison on this line
c:\users\alex\dev\go\misc\cgo\test\cthread_windows.c(39) : note: feeds call on this line
PASS
scatter = 00007FF670706000
hello from C
sqrt is: 0
ok      _/c_/Users/Alex/dev/go/misc/cgo/test    3.534s
# _/c_/Users/Alex/dev/go/misc/cgo/test
issue28896.cgo2.c
.\issue28896.go(30): warning C4820: '<anonymous-tag>': '4' bytes padding added after data member 'g1'
.\issue28896.go(36): warning C4820: '<anonymous-tag>': '4' bytes padding added after data member 'f2'
# _/c_/Users/Alex/dev/go/misc/cgo/test
cthread_windows.c
c:\users\alex\dev\go\misc\cgo\test\cthread_windows.c(39) : warning C5045: Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
c:\users\alex\dev\go\misc\cgo\test\cthread_windows.c(37) : note: index 'i' range checked by comparison on this line
c:\users\alex\dev\go\misc\cgo\test\cthread_windows.c(39) : note: feeds call on this line
PASS
scatter = 00007FF7DD026000
hello from C
sqrt is: 0
ok      _/c_/Users/Alex/dev/go/misc/cgo/test    3.229s
# _/c_/Users/Alex/dev/go/misc/cgo/test
issue28896.cgo2.c
.\issue28896.go(30): warning C4820: '<anonymous-tag>': '4' bytes padding added after data member 'g1'
.\issue28896.go(36): warning C4820: '<anonymous-tag>': '4' bytes padding added after data member 'f2'
# _/c_/Users/Alex/dev/go/misc/cgo/test
cthread_windows.c
c:\users\alex\dev\go\misc\cgo\test\cthread_windows.c(39) : warning C5045: Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
c:\users\alex\dev\go\misc\cgo\test\cthread_windows.c(37) : note: index 'i' range checked by comparison on this line
c:\users\alex\dev\go\misc\cgo\test\cthread_windows.c(39) : note: feeds call on this line
PASS
scatter = 00007FF655CF6000
hello from C
sqrt is: 0
ok      _/c_/Users/Alex/dev/go/misc/cgo/test    3.172s

##### ../test/bench/go1
testing: warning: no tests to run
PASS
ok      _/c_/Users/Alex/dev/go/test/bench/go1   3.178s

##### ../test

##### API check
+pkg go/build, type Context struct, CompilerType string
+pkg go/build, type Package struct, CgoMSCFLAGS []string
+pkg go/build, type Package struct, CgoMSCPPFLAGS []string
+pkg go/build, type Package struct, CgoMSCXXFLAGS []string
+pkg go/build, type Package struct, CgoMSLDFLAGS []string
+pkg os, method (*File) SyscallConn() (syscall.RawConn, error)

ALL TESTS PASSED

---
Installed Go for windows/amd64 in c:\Users\Alex\dev\go
Installed commands in c:\Users\Alex\dev\go\bin
*** You need to add c:\Users\Alex\dev\go\bin to your PATH.

c:\Users\Alex\dev\go\src>

And also I noticed that you execute all tests twice.

...
##### API check
+pkg go/build, type Context struct, CompilerType string
+pkg go/build, type Package struct, CgoMSCFLAGS []string
+pkg go/build, type Package struct, CgoMSCPPFLAGS []string
+pkg go/build, type Package struct, CgoMSCXXFLAGS []string
+pkg go/build, type Package struct, CgoMSLDFLAGS []string
+pkg os, method (*File) SyscallConn() (syscall.RawConn, error)

ALL TESTS PASSED

**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.0
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'


##### Testing packages.
ok      archive/tar     0.256s
ok      archive/zip     2.070s
ok      bufio   (cached)
ok      bytes   1.624s
ok      compress/bzip2  (cached)
ok      compress/flate  1.067s
...

Why?

Alex

@alexbrainman The warnings can likely be suppressed for these tests, especially in this case the spectre warnings.

For the tests executing twice, I believe it was done that way for the builder, run through the tests once with gcc compilation then run through them again with MSVC (when enabled) to ensure compilation with both toolchains works. I'm not sure what's desired here, so I can change it so that when all.bat is called with MSVC enabled we only run the tests with MSVC, let me know. In that case I assume additional builders would need to be added for just MSVC, as opposed to some builders also building with MSVC?

Agreed that on Windows it will be needed to run one builder using MinGW-W64 toolchain and another with the MSVC toolchain in order to test both scenarios.

I believe it was done that way for the builder, run through the tests once with gcc compilation then run through them again with MSVC (when enabled) to ensure compilation with both toolchains works.

I agree this should be the preferred behaviour so there's a guarantee it doesn't break existing workloads if both toolchains are present.

The warnings can likely be suppressed for these tests, especially in this case the spectre warnings.

Go command does not display warnings, so warnings should not be displayed.

Also I noticed that go env command does not display CC_FOR_CGO variable. If CC_FOR_CGO affects how go build works, then CC_FOR_CGO should be displayed along with others. Do we really need CC_FOR_CGO variable? What else, but CC_FOR_CGO=gcc can it be?

For the tests executing twice, I believe it was done that way for the builder, run through the tests once with gcc compilation then run through them again with MSVC (when enabled) to ensure compilation with both toolchains works.

A lot of tests don't use gcc. It is a waste of people's / computer's time when package tests run twice for no reason. As you can see from https://github.com/golang/go/issues/20982#issuecomment-480569880 archive/zip package tests were run twice - I don't see need for second run.

I am not sure how to arrange that, but maybe you should replace your call of go tool dist test in run.bat to do just a selection of tests that are needed to test MSVC build. See, for example, go tool dist test -h on how you can select just some specific tests. @bradfitz maybe we could create some flag or environment variable that will tell dist command to run MSVC specific tests.

But in general your https://go-review.googlesource.com/c/go/+/133946/5 is building executable for me. For example, I can build executable with gcc:

c:\Users\Alex\dev\src\issue\go\20982\hello>type *

hello.c


#include <stdio.h>

extern void hello()
{
    printf("Hello World from C");
}

hello.go


package main

/*
        extern void hello();
*/
import "C"
import "fmt"

func main() {
    fmt.Println("Hello from Go!")
        C.hello()
}

c:\Users\Alex\dev\src\issue\go\20982\hello>go build

c:\Users\Alex\dev\src\issue\go\20982\hello>hello
Hello from Go!
Hello World from C
c:\Users\Alex\dev\src\issue\go\20982\hello>

and then I can build executable with MSVC

c:\Users\Alex\dev\src\issue\go\20982\hello>%GOVSVARSPATH%
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.0
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

c:\Users\Alex\dev\src\issue\go\20982\hello>set CC=cl

c:\Users\Alex\dev\src\issue\go\20982\hello>go build
# issue/go/20982/hello
hello.c
C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\ucrt\stdio.h(948): warning C4710: 'int printf(const char *const ,...)': function not inlined
C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\ucrt\stdio.h(948): note: see declaration of 'printf'

c:\Users\Alex\dev\src\issue\go\20982\hello>hello
Hello from Go!
Hello World from C
c:\Users\Alex\dev\src\issue\go\20982\hello>

So building with MSVC will require users to run MS batch file to set all environment variables (see GOVSVARSPATH in my output) - and that, I assume, standard and has nothing to do with Go. And set CC=cl. Simple enough.

The only issue I see with this approach is that MSVC users will still require to have gcc installed. gcc is used by cmd/cgo to discover all C interfaces - this part is not implemented by using MSVC. Sounds unusual for people who do not know how cmd/cgo works, but it is what it is.

@ianlancetaylor and @bradfitz does this all sounds reasonable to push this forward? Should we try and make this available in go1.13 ?

Builders that have MSVC already installed can test all new code. Other builders will just skip the tests.

Alex

gcc is used by cmd/cgo to discover all C interfaces - this part is not implemented by using MSVC.

How can that be overcome? Ideally it'd be great if there's no requirement for gcc, just MSVC.

How can that be overcome?

Parts of cmd/cgo that use gcc needs to be rewritten to use MSVC. If it is possible.

Alex

I was wondering if this feature is closer to making it into production? I see that it was tagged for 1.13, and that just came out. I'm trying to build Python extensions using a Go c-archive build which is linked to the extension code, but Python requires MSVC.

I don't know what the status of this issue is. It's been kicked forward from release to release, so I'm going to move it to unplanned. If it gets into 1.14, great.

Even with the double-compiler requirement, I'd love to see the current implementation make it into the next release. It at least makes it possible to compile executables/libraries using MSVC, which is better than nothing.

Even with the double-compiler requirement, I'd love to see the current implementation make it into the next release.

I think this would be a good "beta" implementation. It would allow bugs to be flushed out and the implementation to start to mature. What else is left to be done before it could go through a serious review?

So from my perspective and what I can recall from the last time I looked at this (~6 months ago) there were a dozen or so cleanup items that needed to be completed for the code review process. Additional work might also be needed to deduplicate some tests that were basically copied with very minor modifications to work with a different toolchain. There is also a need to review and turn off certain msvc warnings.

Major outstanding issues include:

We can't do type analysis without gcc, there is no simple or immediate solution to this.

Finalizing the setup and how everything is going to work for the builders.

I believe there have been some fairly significant changes to the compiler and linker in the last 6 months that may severely impact the current work done for this, but I haven't been been able to confirm that. Due to the complexity and high learning curve around navigating Go's compiler/linker/toolchain it may take considerable time for me to get myself up to speed enough to figure the impact if any the changes have on this body of work.

There is no support for the complex numeric types. MSCVs C lib is simply incompatible. I believe there is likely a way to shim this with C++ but I'm uncertain, perhaps someone from MS familiar with the MSVC tool chains can chime in.

Unfortunately life and other projects have gotten pretty heavily in the way of me putting more time towards this but I will try and spend some time on this in the upcoming weeks.

@manbearian this issue is about adding MSVC support to golang. Who on the MSVC team would be good to consult for MSVC questions?

@kesmit13 and @mxplusb

This

https://go-review.googlesource.com/c/go/+/133946/5

is @cchamplin latest code that works.

My current concern with the code, that it is unclear how to use it. What command do you run? How is my environment needs to be setup?

I think the next step is for someone to try and use that code, and see if they can actually use it. If it works, then the steps need to be documented, so others can use the code too. Maybe it is too complicated, or does not work. Than we need to adjust the code accordingly.

Once we are happy with current state of the code, we should rebase it onto master. There change adjusted too many different files, so it won't be easy to rebase. Maybe we could adjust the change to make less changes, so it is easier to rebase.

Alex

@mxplusb @kesmit13 The current implementation has been rebased against master and can be found here: https://go-review.googlesource.com/c/go/+/133946/6

@alexbrainman

My current concern with the code, that it is unclear how to use it. What command do you run? How is my environment needs to be setup?

I agree that documentation needs to be added on how to use this. I'm not entirely sure where to put it though, let me know if you have thoughts. Idk if it should go under the cmd/go documentation for building/running via MSVC, or if it should live under the CGO documentation, or maybe somewhere else entirely?

A lot of tests don't use gcc. It is a waste of people's / computer's time when package tests run twice for no reason. As you can see from #20982 (comment) archive/zip package tests were run twice - I don't see need for second run.

I am not sure how to arrange that, but maybe you should replace your call of go tool dist test in run.bat to do just a selection of tests that are needed to test MSVC build. See, for example, go tool dist test -h on how you can select just some specific tests. @bradfitz maybe we could create some flag or environment variable that will tell dist command to run MSVC specific tests.

The change already includes a environment variable added to dist to let it know we are doing MSVC things "GO_TEST_TESTINGMSVC" I believe. What I am not sure about is how we will filter the tests to be ran down to things that only MSVC could impact if we aren't running all tests twice. Potentially just limiting it to CGO related tests, but I believe there are CGO tests all over the place and I don't know if dist has a mechanism to filter those out?

I'm not entirely sure where to put it though, let me know if you have thoughts.

I think before updating any documentation, you should just describe how to build Cgo code with MSVC here. For @mxplusb and @kesmit13 so they can try your changes.

For example, to use gcc to build Cgo code, I

  1. adjust my %PATH% to include gcc.exe
  2. build Go first by running make.bat from %GOROOT%\src directory
  3. use standard go build and go install commands

How do I do the same with MSVC?

What I am not sure about is how we will filter the tests to be ran down to things that only MSVC could impact if we aren't running all tests twice. Potentially just limiting it to CGO related tests, but I believe there are CGO tests all over the place and I don't know if dist has a mechanism to filter those out?

I would start with %GOROOT%\misc\cgo directory. Whatever source you can build there you should build. Anything above that is up to you.

Alex

@cchamplin sorry for the delays on this, I will do my best to test it this week and let you know how it goes.

Hi, folks sorry to be late to the party... i can try and answer questions about MSVC functionality or direct you to folks who can. i read through the conversation, but unfortunately, i'm not familiar enough with go to understand the GCC functionality required that may or may not be missing from MSVC.

Hi Folks,
Is this feature got released in Go 1.14 ?
If released please share any available documents to make use of it.
If not yet released, is there any idea on when it will be released ?
Eagerly waiting for the MSVC support.
Thanks in advance :)

Hi, no one ever responded to my request for more information, so i'm not sure what is needed. Can someone fill me in please?

Unfortunately, I don't know if anybody is actively working on this issue.

Hi, no one ever responded to my request for more information, ...

Hello @manbearian

Which request is that? What information do you need?

Alex

I was tagged earlier in this thread for information, but i don't know what folks are looking for and have been unable to help. If no help is needed, that's great. Just please let me know.

Just please let me know.

@manbearian

Thanks for your offer. I don't know who tagged you. If they still need help, they will tag you again.

Alex

If not yet released, is there any idea on when it will be released ?
Eagerly waiting for the MSVC support.

@dhinesherode91

The code is there and complete; I believe the only thing really holding this back is people testing the changes and providing feedback so they can then be properly reviewed and approved. I am hesitant to rebase the changes on top of current master though. I've done it 5-6 times and it's extremely time consuming, and then I get no testing/feedback. Unless additional parties are willing to participate in testing this, the issue might be dead in the water.

Unless additional parties are willing to participate in testing this, the issue might be dead in the water.

@cchamplin count on me for testing/debugging your changes. I don't have experience with to go toolchain but I've reviewed your commits and they seem to be pretty well structured and documented.

@cchamplin Great to hear that the implementation part is completed.
I am interested to involve in the testing part, but I am a beginner in GoLang and also a good learner in windows apis(started few weeks before). I was planning to work with windows api in MS C++ as it looks like lot of official resources are available for it. But then I started looking for an language (for my system programming) where I could use it for my multi platform system programming and I am impressed with GoLang. Always ready for the contribution from my end and is in much need of guidance.

@cchamplin if you rebase I’ll try building on top of it. I’d love to drop the mingw64 requirement from some of my projects at work. My build tooling is all set up for MSVC, so I wouldn’t have to do anything other than use your patch.

@cchamplin also, I looked through your CLs and didn’t see any for the cgo tool so I started in on my own patch. Is there any prior work?

@ericlagergren

@cchamplin also, I looked through your CLs and didn’t see any for the cgo tool so I started in on my own patch. Is there any prior work?

Not sure if I understand, at https://go-review.googlesource.com/c/go/+/133946/6 many of the related commits directly touch the cgo portion of Go in some capacity.

@ccahoon okay, I must’ve missed them. If the cgo tool hadn’t been updated I was going to offer to do that.

@ericlagergren

I’d love to drop the mingw64 requirement from some of my projects at work

Just FYI. The

https://go-review.googlesource.com/c/go/+/133946/6

change still needs Mingw to work. You will need both Mingw and MSVC tools installed to use the @cchamplin change.

Alex

@cchamplin, why is Mingw still needed? What would be needed to remove this dependency?

@alexbrainman I misspoke. Right now we have to use MSYS2 (which includes mingw64) because the non-MSVC build process requires autotools. Dropping that would be fantastic. But also like @cglong said, I'm not sure why this would also require mingw64.

@ericlagergren @cglong CGO needs to gather and have available type information about the C code it's being compiled with. GCC has a flag that will output type data in a conveniently parsable format. MSVC as far as I know does not have the functionality to provide the same information. Building the tooling in go would not only require code to lex/parse C and C++ code but also to understand and evaluate pre-processor directives. It would be a fairly large undertaking to build such a thing in Go. Perhaps there is a library already available somewhere though that we could figure out how to adopt?

@cchamplin gotcha, that's what I figured. I know of third party libraries like libclang and cznic/cc. But unfortunately libclang is gigantic and cznic/cc hasn't been tested on Windows or macOS. Does PDB not provide enough information?

CGO needs to gather and have available type information about the C code it's being compiled with. GCC has a flag that will output type data in a conveniently parsable format. MSVC as far as I know does not have the functionality to provide the same information.

Perhaps @manbearian might be able to provide some pointers here?

I wouldn't say that GCC emits output type data in a special format. It emits DWARF debugging data intended for debuggers, and cgo reads that data using the debug/dwarf package. MSVC presumably also has a debug format. If that format is documented anywhere, we could read it using cgo just as we do the DWARF data.

@ianlancetaylor MSVC can emit PDB files. See https://github.com/Microsoft/microsoft-pdb

FWIW, reading MSVC’s debug output is what I was talking about when I mentioned the cgo tool in https://github.com/golang/go/issues/20982#issuecomment-648462003

I wouldn't say that GCC emits output type data in a special format. It emits DWARF debugging data intended for debuggers, and cgo reads that data using the debug/dwarf package. MSVC presumably also has a debug format. If that format is documented anywhere, we could read it using cgo just as we do the DWARF data.

@ericlagergren @ianlancetaylor
So we do need the DWARF data, which is a big part of it. However, we could theoretically get the same information out of PDB data.

What I was specifically referring to, though, was not just the DWARF data that GCC produces but the output of gcc -E -dM which outputs all the #define's after preprocessing has taken place. It is possible that PDB on its own could provide the same information...what I'm skeptical of is whether or not we'd be able to coerce MSVC into dumping debug information/type information in any capacity without it needing to actually compile the code. Right now CGO is doing just that with GCC .

From memory, /EP /d1PP should emit something similar to -E -dM.

From memory, /EP /d1PP should emit something similar to -E -dM.

If so that'd be excellent, I assume /d1PP is an undocumented flag? I've never seen it before. That would just leave getting equivalent type information out ahead of time.

@cchamplin yeah, it’s undocumented. A lot of the /dXXX flags are, apparently. Using undocumented flags is unfortunate, but (IMO) it doesn’t seem to be that bad of a sin on Windows, at least. I saw MSVC STL devs talking about it on a reddit thread, but it’s also all over the Internet, too.

Can someone give me a short summary of the current state of the MSVC-support? I do not understand very much of all this low-level-compiler-stuff. It's just that I have a go library I want to use from C#/UWP - and that is already working with go build and the buildmode c-shared. BUT: that app won't ever get accepted by the Windows Store as the generated DLL from go misses some compiler/linker-flags that (I assume) can only be set by the MSVC-toolchain.

So: how could I test this here? Any advice is appreciated! Thank you!

that app won't ever get accepted by the Windows Store as the generated DLL from go misses some compiler/linker-flags that (I assume) can only be set by the MSVC-toolchain.

@TopperDEL does Windows Store tell you which compiler flags are missing? If mingw-64 support them you can always use -extldflags with the right flags when build the dll.

@qmuntal Yes, they do:
"Apply the required linker options - SAFESEH, DYNAMICBASE, NXCOMPAT, and APPCONTAINER - when you link the app."

I already tried
go build -ldflags="-s -w '-extldflags=-Wl,--dynamicbase,--high-entropy-va'" -o storj_uplink.dll -buildmode c-shared

But that did not change anything regarding the store-submittion. And from my understanding at least SAFESEH is a MSVC-specific-thing, right?

"Apply the required linker options - SAFESEH, DYNAMICBASE, NXCOMPAT, and APPCONTAINER - when you link the app."

I don't know any easy way to enable SAFESEH nor APPCONTAINER flags in Mingw-w64, maybe this project https://github.com/status-im/mingw-windows10-uwp can give you some guidance.

On the other hand DYNAMICBASE and NXCOMPAT will be enabled by default in go 1.16 (see #41421) but can already be used with -extldflags just as mentioned, in fact I'm doing so for some projects.

@qmuntal Thanks! So at least my extldflags above should be right?
I'll have a look at the mingw for windows 10-uwp.

Was this page helpful?
0 / 5 - 0 ratings