mirror of
https://github.com/golang/go.git
synced 2025-05-25 01:11:23 +00:00
cmd/link: read crt2.o for windows internal-linking CGO
For Windows internal linking with CGO, when using more modern LLVM-based compilers, we may need to read in the object file "crt2.o" so as to have a definition of "atexit" (for example when linking the runtime/cgo test), and we also need to allow for the possibility that a given host archive might have to be looked at more than once. The goal here is to get all.bash working on Windows when using an up to date mingw C compiler (including those based on clang + LLD). This patch also adds a new "hostObject" helper routine, similar to "hostArchive" but specific to individual object files. There is also a change to hostArchive to modify the pseudo-package name assigned when reading archive elements: up until this point, a package name of "libgcc" was used (even when reading a host archive like "libmingex.a"), which led to very confusing errors messages if symbols were missing or there were duplicate definitions. Updates #35006. Change-Id: I19c17dea9cfffa9e79030fc23064c7c63a612097 Reviewed-on: https://go-review.googlesource.com/c/go/+/382838 Reviewed-by: Cherry Mui <cherryyz@google.com> Trust: Than McIntosh <thanm@google.com> Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
821420d6bb
commit
a9d13a9c23
@ -38,6 +38,8 @@ import (
|
|||||||
"internal/buildcfg"
|
"internal/buildcfg"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -65,6 +67,9 @@ type ArHdr struct {
|
|||||||
// define them. This is used for the compiler support library
|
// define them. This is used for the compiler support library
|
||||||
// libgcc.a.
|
// libgcc.a.
|
||||||
func hostArchive(ctxt *Link, name string) {
|
func hostArchive(ctxt *Link, name string) {
|
||||||
|
if ctxt.Debugvlog > 1 {
|
||||||
|
ctxt.Logf("hostArchive(%s)\n", name)
|
||||||
|
}
|
||||||
f, err := bio.Open(name)
|
f, err := bio.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@ -122,8 +127,12 @@ func hostArchive(ctxt *Link, name string) {
|
|||||||
pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
|
pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
|
||||||
l = atolwhex(arhdr.size)
|
l = atolwhex(arhdr.size)
|
||||||
|
|
||||||
libgcc := sym.Library{Pkg: "libgcc"}
|
pkname := filepath.Base(name)
|
||||||
h := ldobj(ctxt, f, &libgcc, l, pname, name)
|
if i := strings.LastIndex(pkname, ".a"); i >= 0 {
|
||||||
|
pkname = pkname[:i]
|
||||||
|
}
|
||||||
|
libar := sym.Library{Pkg: pkname}
|
||||||
|
h := ldobj(ctxt, f, &libar, l, pname, name)
|
||||||
if h.ld == nil {
|
if h.ld == nil {
|
||||||
Errorf(nil, "%s unrecognized object file at offset %d", name, off)
|
Errorf(nil, "%s unrecognized object file at offset %d", name, off)
|
||||||
continue
|
continue
|
||||||
|
@ -614,25 +614,7 @@ func (ctxt *Link) loadlib() {
|
|||||||
*flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
|
*flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
|
||||||
}
|
}
|
||||||
if ctxt.HeadType == objabi.Hwindows {
|
if ctxt.HeadType == objabi.Hwindows {
|
||||||
if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
|
loadWindowsHostArchives(ctxt)
|
||||||
hostArchive(ctxt, p)
|
|
||||||
}
|
|
||||||
if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
|
|
||||||
hostArchive(ctxt, p)
|
|
||||||
}
|
|
||||||
// Link libmsvcrt.a to resolve '__acrt_iob_func' symbol
|
|
||||||
// (see https://golang.org/issue/23649 for details).
|
|
||||||
if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" {
|
|
||||||
hostArchive(ctxt, p)
|
|
||||||
}
|
|
||||||
// TODO: maybe do something similar to peimporteddlls to collect all lib names
|
|
||||||
// and try link them all to final exe just like libmingwex.a and libmingw32.a:
|
|
||||||
/*
|
|
||||||
for:
|
|
||||||
#cgo windows LDFLAGS: -lmsvcrt -lm
|
|
||||||
import:
|
|
||||||
libmsvcrt.a libm.a
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
if *flagLibGCC != "none" {
|
if *flagLibGCC != "none" {
|
||||||
hostArchive(ctxt, *flagLibGCC)
|
hostArchive(ctxt, *flagLibGCC)
|
||||||
@ -648,6 +630,52 @@ func (ctxt *Link) loadlib() {
|
|||||||
strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
|
strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadWindowsHostArchives loads in host archives and objects when
|
||||||
|
// doing internal linking on windows. Older toolchains seem to require
|
||||||
|
// just a single pass through the various archives, but some modern
|
||||||
|
// toolchains when linking a C program with mingw pass library paths
|
||||||
|
// multiple times to the linker, e.g. "... -lmingwex -lmingw32 ...
|
||||||
|
// -lmingwex -lmingw32 ...". To accommodate this behavior, we make two
|
||||||
|
// passes over the host archives below.
|
||||||
|
func loadWindowsHostArchives(ctxt *Link) {
|
||||||
|
any := true
|
||||||
|
for i := 0; any && i < 2; i++ {
|
||||||
|
// Link crt2.o (if present) to resolve "atexit" when
|
||||||
|
// using LLVM-based compilers.
|
||||||
|
isunresolved := symbolsAreUnresolved(ctxt, []string{"atexit"})
|
||||||
|
if isunresolved[0] {
|
||||||
|
if p := ctxt.findLibPath("crt2.o"); p != "none" {
|
||||||
|
hostObject(ctxt, "crt2", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
|
||||||
|
hostArchive(ctxt, p)
|
||||||
|
}
|
||||||
|
if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
|
||||||
|
hostArchive(ctxt, p)
|
||||||
|
}
|
||||||
|
// Link libmsvcrt.a to resolve '__acrt_iob_func' symbol
|
||||||
|
// (see https://golang.org/issue/23649 for details).
|
||||||
|
if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" {
|
||||||
|
hostArchive(ctxt, p)
|
||||||
|
}
|
||||||
|
any = false
|
||||||
|
undefs := ctxt.loader.UndefinedRelocTargets(1)
|
||||||
|
if len(undefs) > 0 {
|
||||||
|
any = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: maybe do something similar to peimporteddlls to collect
|
||||||
|
// all lib names and try link them all to final exe just like
|
||||||
|
// libmingwex.a and libmingw32.a:
|
||||||
|
/*
|
||||||
|
for:
|
||||||
|
#cgo windows LDFLAGS: -lmsvcrt -lm
|
||||||
|
import:
|
||||||
|
libmsvcrt.a libm.a
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
// loadcgodirectives reads the previously discovered cgo directives, creating
|
// loadcgodirectives reads the previously discovered cgo directives, creating
|
||||||
// symbols in preparation for host object loading or use later in the link.
|
// symbols in preparation for host object loading or use later in the link.
|
||||||
func (ctxt *Link) loadcgodirectives() {
|
func (ctxt *Link) loadcgodirectives() {
|
||||||
@ -2000,6 +2028,59 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// symbolsAreUnresolved scans through the loader's list of unresolved
|
||||||
|
// symbols and checks to see whether any of them match the names of the
|
||||||
|
// symbols in 'want'. Return value is a list of bools, with list[K] set
|
||||||
|
// to true if there is an unresolved reference to the symbol in want[K].
|
||||||
|
func symbolsAreUnresolved(ctxt *Link, want []string) []bool {
|
||||||
|
returnAllUndefs := -1
|
||||||
|
undefs := ctxt.loader.UndefinedRelocTargets(returnAllUndefs)
|
||||||
|
seen := make(map[loader.Sym]struct{})
|
||||||
|
rval := make([]bool, len(want))
|
||||||
|
wantm := make(map[string]int)
|
||||||
|
for k, w := range want {
|
||||||
|
wantm[w] = k
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
for _, s := range undefs {
|
||||||
|
if _, ok := seen[s]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[s] = struct{}{}
|
||||||
|
if k, ok := wantm[ctxt.loader.SymName(s)]; ok {
|
||||||
|
rval[k] = true
|
||||||
|
count++
|
||||||
|
if count == len(want) {
|
||||||
|
return rval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rval
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostObject reads a single host object file (compare to "hostArchive").
|
||||||
|
// This is used as part of internal linking when we need to pull in
|
||||||
|
// files such as "crt?.o".
|
||||||
|
func hostObject(ctxt *Link, objname string, path string) {
|
||||||
|
if ctxt.Debugvlog > 1 {
|
||||||
|
ctxt.Logf("hostObject(%s)\n", path)
|
||||||
|
}
|
||||||
|
objlib := sym.Library{
|
||||||
|
Pkg: objname,
|
||||||
|
}
|
||||||
|
f, err := bio.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
Exitf("cannot open host object %q file %s: %v", objname, path, err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
h := ldobj(ctxt, f, &objlib, 0, path, path)
|
||||||
|
if h.ld == nil {
|
||||||
|
Exitf("unrecognized object file format in %s", path)
|
||||||
|
}
|
||||||
|
f.MustSeek(h.off, 0)
|
||||||
|
h.ld(ctxt, f, h.pkg, h.length, h.pn)
|
||||||
|
}
|
||||||
|
|
||||||
func checkFingerprint(lib *sym.Library, libfp goobj.FingerprintType, src string, srcfp goobj.FingerprintType) {
|
func checkFingerprint(lib *sym.Library, libfp goobj.FingerprintType, src string, srcfp goobj.FingerprintType) {
|
||||||
if libfp != srcfp {
|
if libfp != srcfp {
|
||||||
Exitf("fingerprint mismatch: %s has %x, import from %s expecting %x", lib, libfp, src, srcfp)
|
Exitf("fingerprint mismatch: %s has %x, import from %s expecting %x", lib, libfp, src, srcfp)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user