cmd/link: support MSVC clang

Currently on Windows, for cgo, we support MinGW-based C toolchain,
that is, with a -windows-gnu target. This CL makes it work with
clang with a -windows-msvc target. The LLVM toolchain bundled in
MSVC (https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild)
is such an example.

Currently it is expecting lld-link as the C linker, which is also
bundled in MSVC, can be requested with -fuse-ld=lld, but is not
the default.

This is the first step, which makes it generate a working cgo
binary. There are still more work to do, e.g. there are some
linker warnings, and the binary doesn't have symbol table.
all.bat doesn't pass with this setting.

Change-Id: I54d33f7dd5f5eeeafa0735cd52f4127fe4865636
Reviewed-on: https://go-review.googlesource.com/c/go/+/703055
Reviewed-by: Than McIntosh <thanm@golang.org>
Reviewed-by: Florian Zenker <floriank@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Cherry Mui 2025-09-11 17:42:30 -04:00
parent 9271bbbb80
commit c70713da82

View file

@ -1457,6 +1457,9 @@ func (ctxt *Link) hostlink() {
// Only macOS supports unmapped segments such as our __DWARF segment. // Only macOS supports unmapped segments such as our __DWARF segment.
combineDwarf := ctxt.IsDarwin() && !*FlagW && machoPlatform == PLATFORM_MACOS combineDwarf := ctxt.IsDarwin() && !*FlagW && machoPlatform == PLATFORM_MACOS
var isMSVC bool // used on Windows
wlPrefix := "-Wl,--"
switch ctxt.HeadType { switch ctxt.HeadType {
case objabi.Hdarwin: case objabi.Hdarwin:
if combineDwarf { if combineDwarf {
@ -1501,6 +1504,15 @@ func (ctxt *Link) hostlink() {
argv = append(argv, "-Wl,--no-execute-only") argv = append(argv, "-Wl,--no-execute-only")
} }
case objabi.Hwindows: case objabi.Hwindows:
isMSVC = ctxt.isMSVC()
if isMSVC {
// For various options, MSVC lld-link only accepts one dash.
// TODO: It seems mingw clang supports one or two dashes,
// maybe we can always use one dash, but I'm not sure about
// legacy compilers that currently work.
wlPrefix = "-Wl,-"
}
if windowsgui { if windowsgui {
argv = append(argv, "-mwindows") argv = append(argv, "-mwindows")
} else { } else {
@ -1508,15 +1520,18 @@ func (ctxt *Link) hostlink() {
} }
// Mark as having awareness of terminal services, to avoid // Mark as having awareness of terminal services, to avoid
// ancient compatibility hacks. // ancient compatibility hacks.
argv = append(argv, "-Wl,--tsaware")
argv = append(argv, wlPrefix+"tsaware")
// Enable DEP // Enable DEP
argv = append(argv, "-Wl,--nxcompat") argv = append(argv, wlPrefix+"nxcompat")
argv = append(argv, fmt.Sprintf("-Wl,--major-os-version=%d", PeMinimumTargetMajorVersion)) if !isMSVC {
argv = append(argv, fmt.Sprintf("-Wl,--minor-os-version=%d", PeMinimumTargetMinorVersion)) argv = append(argv, fmt.Sprintf("-Wl,--major-os-version=%d", PeMinimumTargetMajorVersion))
argv = append(argv, fmt.Sprintf("-Wl,--major-subsystem-version=%d", PeMinimumTargetMajorVersion)) argv = append(argv, fmt.Sprintf("-Wl,--minor-os-version=%d", PeMinimumTargetMinorVersion))
argv = append(argv, fmt.Sprintf("-Wl,--minor-subsystem-version=%d", PeMinimumTargetMinorVersion)) argv = append(argv, fmt.Sprintf("-Wl,--major-subsystem-version=%d", PeMinimumTargetMajorVersion))
argv = append(argv, fmt.Sprintf("-Wl,--minor-subsystem-version=%d", PeMinimumTargetMinorVersion))
}
case objabi.Haix: case objabi.Haix:
argv = append(argv, "-pthread") argv = append(argv, "-pthread")
// prevent ld to reorder .text functions to keep the same // prevent ld to reorder .text functions to keep the same
@ -1557,16 +1572,21 @@ func (ctxt *Link) hostlink() {
// an ancient compiler with ancient defaults. // an ancient compiler with ancient defaults.
var dbopt string var dbopt string
var heopt string var heopt string
dbon := "--dynamicbase" dbon := wlPrefix + "dynamicbase"
heon := "--high-entropy-va" heon := wlPrefix + "high-entropy-va"
dboff := "--disable-dynamicbase" dboff := wlPrefix + "disable-dynamicbase"
heoff := "--disable-high-entropy-va" heoff := wlPrefix + "disable-high-entropy-va"
if isMSVC {
heon = wlPrefix + "highentropyva"
heoff = wlPrefix + "highentropyva:no"
dboff = wlPrefix + "dynamicbase:no"
}
if val { if val {
dbopt = dbon dbopt = dbon
heopt = heon heopt = heon
} else { } else {
// Test to see whether "--disable-dynamicbase" works. // Test to see whether "--disable-dynamicbase" works.
newer := linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,"+dboff) newer := linkerFlagSupported(ctxt.Arch, argv[0], "", dboff)
if newer { if newer {
// Newer compiler, which supports both on/off options. // Newer compiler, which supports both on/off options.
dbopt = dboff dbopt = dboff
@ -1579,11 +1599,11 @@ func (ctxt *Link) hostlink() {
} }
} }
if dbopt != "" { if dbopt != "" {
argv = append(argv, "-Wl,"+dbopt) argv = append(argv, dbopt)
} }
// enable high-entropy ASLR on 64-bit. // enable high-entropy ASLR on 64-bit.
if ctxt.Arch.PtrSize >= 8 && heopt != "" { if ctxt.Arch.PtrSize >= 8 && heopt != "" {
argv = append(argv, "-Wl,"+heopt) argv = append(argv, heopt)
} }
return argv return argv
} }
@ -1927,9 +1947,11 @@ func (ctxt *Link) hostlink() {
argv = append(argv, "-lsynchronization") argv = append(argv, "-lsynchronization")
} }
} }
// libmingw32 and libmingwex have some inter-dependencies, if !isMSVC {
// so must use linker groups. // libmingw32 and libmingwex have some inter-dependencies,
argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group") // so must use linker groups.
argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group")
}
argv = append(argv, peimporteddlls()...) argv = append(argv, peimporteddlls()...)
} }
@ -2182,6 +2204,7 @@ func trimLinkerArgv(argv []string) []string {
"-isysroot", "-isysroot",
"--sysroot", "--sysroot",
"-target", "-target",
"--target",
} }
prefixesToKeep := []string{ prefixesToKeep := []string{
"-f", "-f",
@ -2192,6 +2215,7 @@ func trimLinkerArgv(argv []string) []string {
"-isysroot", "-isysroot",
"--sysroot", "--sysroot",
"-target", "-target",
"--target",
} }
var flags []string var flags []string
@ -3024,3 +3048,19 @@ func (ctxt *Link) findExtLinkTool(toolname string) string {
cmdpath := strings.TrimRight(string(out), "\r\n") cmdpath := strings.TrimRight(string(out), "\r\n")
return cmdpath return cmdpath
} }
// isMSVC reports whether the C toolchain is clang with a -msvc target,
// e.g. the clang bundled in MSVC.
func (ctxt *Link) isMSVC() bool {
extld := ctxt.extld()
name, args := extld[0], extld[1:]
args = append(args, trimLinkerArgv(flagExtldflags)...)
args = append(args, "--version")
cmd := exec.Command(name, args...)
if out, err := cmd.CombinedOutput(); err == nil {
if bytes.Contains(out, []byte("-msvc\n")) || bytes.Contains(out, []byte("-msvc\r")) {
return true
}
}
return false
}