aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link
diff options
context:
space:
mode:
authorLasse Folger <lassefolger@google.com>2022-04-05 09:19:57 +0200
committerLasse Folger <lassefolger@google.com>2022-04-05 18:53:15 +0200
commit4739b353bb878f29ee78e1cd7eaf3d8f32199798 (patch)
tree40bb8ba155a8dc87b4794a4fcf224d03f555502c /src/cmd/link
parent9d6ab825f6fe125f7ce630e103b887e580403802 (diff)
parentc18f398f32c45afe2e9a81a6d885a4e0183cd649 (diff)
downloadgo-4739b353bb878f29ee78e1cd7eaf3d8f32199798.tar.xz
[dev.boringcrypto] all: merge master into dev.boringcrypto
Change-Id: Iaf618444dd2d99721c19708df9ce2c1f35854efd
Diffstat (limited to 'src/cmd/link')
-rw-r--r--src/cmd/link/elf_test.go28
-rw-r--r--src/cmd/link/internal/benchmark/bench.go20
-rw-r--r--src/cmd/link/internal/ld/ar.go13
-rw-r--r--src/cmd/link/internal/ld/dwarf_test.go1
-rw-r--r--src/cmd/link/internal/ld/elf.go57
-rw-r--r--src/cmd/link/internal/ld/lib.go191
-rw-r--r--src/cmd/link/internal/ld/outbuf.go12
-rw-r--r--src/cmd/link/internal/loader/loader.go25
-rw-r--r--src/cmd/link/internal/loadpe/ldpe.go242
-rw-r--r--src/cmd/link/internal/s390x/asm.go8
10 files changed, 434 insertions, 163 deletions
diff --git a/src/cmd/link/elf_test.go b/src/cmd/link/elf_test.go
index 318bd76aba..5b7b95757b 100644
--- a/src/cmd/link/elf_test.go
+++ b/src/cmd/link/elf_test.go
@@ -469,3 +469,31 @@ func TestPIESize(t *testing.T) {
})
}
}
+
+func TestIssue51939(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+ td := t.TempDir()
+ goFile := filepath.Join(td, "issue51939.go")
+ if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil {
+ t.Fatal(err)
+ }
+ outFile := filepath.Join(td, "issue51939.exe")
+ goTool := testenv.GoToolPath(t)
+ cmd := exec.Command(goTool, "build", "-o", outFile, goFile)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ ef, err := elf.Open(outFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, s := range ef.Sections {
+ if s.Flags&elf.SHF_ALLOC == 0 && s.Addr != 0 {
+ t.Errorf("section %s should not allocated with addr %x", s.Name, s.Addr)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go
index 6c163c801e..39179515cd 100644
--- a/src/cmd/link/internal/benchmark/bench.go
+++ b/src/cmd/link/internal/benchmark/bench.go
@@ -44,16 +44,16 @@ type mark struct {
//
// Typical usage should look like:
//
-// func main() {
-// filename := "" // Set to enable per-phase pprof file output.
-// bench := benchmark.New(benchmark.GC, filename)
-// defer bench.Report(os.Stdout)
-// // etc
-// bench.Start("foo")
-// foo()
-// bench.Start("bar")
-// bar()
-// }
+// func main() {
+// filename := "" // Set to enable per-phase pprof file output.
+// bench := benchmark.New(benchmark.GC, filename)
+// defer bench.Report(os.Stdout)
+// // etc
+// bench.Start("foo")
+// foo()
+// bench.Start("bar")
+// bar()
+// }
//
// Note that a nil Metrics object won't cause any errors, so one could write
// code like:
diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
index 23915f9032..125a5d6fcb 100644
--- a/src/cmd/link/internal/ld/ar.go
+++ b/src/cmd/link/internal/ld/ar.go
@@ -38,6 +38,8 @@ import (
"internal/buildcfg"
"io"
"os"
+ "path/filepath"
+ "strings"
)
const (
@@ -65,6 +67,9 @@ type ArHdr struct {
// define them. This is used for the compiler support library
// libgcc.a.
func hostArchive(ctxt *Link, name string) {
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("hostArchive(%s)\n", name)
+ }
f, err := bio.Open(name)
if err != nil {
if os.IsNotExist(err) {
@@ -122,8 +127,12 @@ func hostArchive(ctxt *Link, name string) {
pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
l = atolwhex(arhdr.size)
- libgcc := sym.Library{Pkg: "libgcc"}
- h := ldobj(ctxt, f, &libgcc, l, pname, name)
+ pkname := filepath.Base(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 {
Errorf(nil, "%s unrecognized object file at offset %d", name, off)
continue
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
index 2f9bf25d10..1f7b37f892 100644
--- a/src/cmd/link/internal/ld/dwarf_test.go
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -1469,7 +1469,6 @@ func TestIssue42484(t *testing.T) {
// i1:0:1 f1:1:1 ~r0:2:2 ~r1:3:2
//
// where each chunk above is of the form NAME:ORDER:INOUTCLASSIFICATION
-//
func processParams(die *dwarf.Entry, ex *dwtest.Examiner) string {
// Values in the returned map are of the form <order>:<varparam>
// where order is the order within the child DIE list of the
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index cb094a373a..733f4ec00b 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -549,30 +549,31 @@ func elfMipsAbiFlags(sh *ElfShdr, startva uint64, resoff uint64) int {
return n
}
-//typedef struct
-//{
-// /* Version of flags structure. */
-// uint16_t version;
-// /* The level of the ISA: 1-5, 32, 64. */
-// uint8_t isa_level;
-// /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */
-// uint8_t isa_rev;
-// /* The size of general purpose registers. */
-// uint8_t gpr_size;
-// /* The size of co-processor 1 registers. */
-// uint8_t cpr1_size;
-// /* The size of co-processor 2 registers. */
-// uint8_t cpr2_size;
-// /* The floating-point ABI. */
-// uint8_t fp_abi;
-// /* Processor-specific extension. */
-// uint32_t isa_ext;
-// /* Mask of ASEs used. */
-// uint32_t ases;
-// /* Mask of general flags. */
-// uint32_t flags1;
-// uint32_t flags2;
-//} Elf_Internal_ABIFlags_v0;
+// Layout is given by this C definition:
+// typedef struct
+// {
+// /* Version of flags structure. */
+// uint16_t version;
+// /* The level of the ISA: 1-5, 32, 64. */
+// uint8_t isa_level;
+// /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */
+// uint8_t isa_rev;
+// /* The size of general purpose registers. */
+// uint8_t gpr_size;
+// /* The size of co-processor 1 registers. */
+// uint8_t cpr1_size;
+// /* The size of co-processor 2 registers. */
+// uint8_t cpr2_size;
+// /* The floating-point ABI. */
+// uint8_t fp_abi;
+// /* Processor-specific extension. */
+// uint32_t isa_ext;
+// /* Mask of ASEs used. */
+// uint32_t ases;
+// /* Mask of general flags. */
+// uint32_t flags1;
+// uint32_t flags2;
+// } Elf_Internal_ABIFlags_v0;
func elfWriteMipsAbiFlags(ctxt *Link) int {
sh := elfshname(".MIPS.abiflags")
ctxt.Out.SeekSet(int64(sh.Off))
@@ -1100,16 +1101,18 @@ func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr {
sh.Flags |= uint64(elf.SHF_TLS)
sh.Type = uint32(elf.SHT_NOBITS)
}
+ if linkmode != LinkExternal {
+ sh.Addr = sect.Vaddr
+ }
+
if strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") {
sh.Flags = 0
+ sh.Addr = 0
if sect.Compressed {
sh.Flags |= uint64(elf.SHF_COMPRESSED)
}
}
- if linkmode != LinkExternal {
- sh.Addr = sect.Vaddr
- }
sh.Addralign = uint64(sect.Align)
sh.Size = sect.Length
if sect.Name != ".tbss" {
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 6055d4327e..07cffcafff 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -614,25 +614,7 @@ func (ctxt *Link) loadlib() {
*flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
}
if ctxt.HeadType == objabi.Hwindows {
- 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)
- }
- // 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
- */
+ loadWindowsHostArchives(ctxt)
}
if *flagLibGCC != "none" {
hostArchive(ctxt, *flagLibGCC)
@@ -648,6 +630,67 @@ func (ctxt *Link) loadlib() {
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
+ }
+ }
+ // If needed, create the __CTOR_LIST__ and __DTOR_LIST__
+ // symbols (referenced by some of the mingw support library
+ // routines). Creation of these symbols is normally done by the
+ // linker if not already present.
+ want := []string{"__CTOR_LIST__", "__DTOR_LIST__"}
+ isunresolved := symbolsAreUnresolved(ctxt, want)
+ for k, w := range want {
+ if isunresolved[k] {
+ sb := ctxt.loader.CreateSymForUpdate(w, 0)
+ sb.SetType(sym.SDATA)
+ sb.AddUint64(ctxt.Arch, 0)
+ sb.SetReachable(true)
+ ctxt.loader.SetAttrSpecial(sb.Sym(), 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
// symbols in preparation for host object loading or use later in the link.
func (ctxt *Link) loadcgodirectives() {
@@ -1311,13 +1354,52 @@ func (ctxt *Link) hostlink() {
argv = append(argv, "-Wl,-bbigtoc")
}
- // Enable ASLR on Windows.
- addASLRargs := func(argv []string) []string {
- // Enable ASLR.
- argv = append(argv, "-Wl,--dynamicbase")
+ // Enable/disable ASLR on Windows.
+ addASLRargs := func(argv []string, val bool) []string {
+ // Old/ancient versions of GCC support "--dynamicbase" and
+ // "--high-entropy-va" but don't enable it by default. In
+ // addition, they don't accept "--disable-dynamicbase" or
+ // "--no-dynamicbase", so the only way to disable ASLR is to
+ // not pass any flags at all.
+ //
+ // More modern versions of GCC (and also clang) enable ASLR
+ // by default. With these compilers, however you can turn it
+ // off if you want using "--disable-dynamicbase" or
+ // "--no-dynamicbase".
+ //
+ // The strategy below is to try using "--disable-dynamicbase";
+ // if this succeeds, then assume we're working with more
+ // modern compilers and act accordingly. If it fails, assume
+ // an ancient compiler with ancient defaults.
+ var dbopt string
+ var heopt string
+ dbon := "--dynamicbase"
+ heon := "--high-entropy-va"
+ dboff := "--disable-dynamicbase"
+ heoff := "--disable-high-entropy-va"
+ if val {
+ dbopt = dbon
+ heopt = heon
+ } else {
+ // Test to see whether "--disable-dynamicbase" works.
+ newer := linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,"+dboff)
+ if newer {
+ // Newer compiler, which supports both on/off options.
+ dbopt = dboff
+ heopt = heoff
+ } else {
+ // older toolchain: we have to say nothing in order to
+ // get a no-ASLR binary.
+ dbopt = ""
+ heopt = ""
+ }
+ }
+ if dbopt != "" {
+ argv = append(argv, "-Wl,"+dbopt)
+ }
// enable high-entropy ASLR on 64-bit.
- if ctxt.Arch.PtrSize >= 8 {
- argv = append(argv, "-Wl,--high-entropy-va")
+ if ctxt.Arch.PtrSize >= 8 && heopt != "" {
+ argv = append(argv, "-Wl,"+heopt)
}
return argv
}
@@ -1334,7 +1416,7 @@ func (ctxt *Link) hostlink() {
switch ctxt.HeadType {
case objabi.Hdarwin, objabi.Haix:
case objabi.Hwindows:
- argv = addASLRargs(argv)
+ argv = addASLRargs(argv, *flagAslr)
default:
// ELF.
if ctxt.UseRelro() {
@@ -1351,9 +1433,7 @@ func (ctxt *Link) hostlink() {
}
argv = append(argv, "-shared")
if ctxt.HeadType == objabi.Hwindows {
- if *flagAslr {
- argv = addASLRargs(argv)
- }
+ argv = addASLRargs(argv, *flagAslr)
} else {
// Pass -z nodelete to mark the shared library as
// non-closeable: a dlclose will do nothing.
@@ -2001,6 +2081,59 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
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) {
if libfp != srcfp {
Exitf("fingerprint mismatch: %s has %x, import from %s expecting %x", lib, libfp, src, srcfp)
diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go
index 1d21dce9c5..1d1751ccdc 100644
--- a/src/cmd/link/internal/ld/outbuf.go
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -30,12 +30,12 @@ const outbufMode = 0775
// any system calls to read the value.
//
// Third, it also mmaps the output file (if available). The intended usage is:
-// - Mmap the output file
-// - Write the content
-// - possibly apply any edits in the output buffer
-// - possibly write more content to the file. These writes take place in a heap
-// backed buffer that will get synced to disk.
-// - Munmap the output file
+// - Mmap the output file
+// - Write the content
+// - possibly apply any edits in the output buffer
+// - possibly write more content to the file. These writes take place in a heap
+// backed buffer that will get synced to disk.
+// - Munmap the output file
//
// And finally, it provides a mechanism by which you can multithread the
// writing of output files. This mechanism is accomplished by copying a OutBuf,
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index d46aa41181..2cbec5d06f 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -166,21 +166,21 @@ type symAndSize struct {
//
// Notes on the layout of global symbol index space:
//
-// - Go object files are read before host object files; each Go object
-// read adds its defined package symbols to the global index space.
-// Nonpackage symbols are not yet added.
+// - Go object files are read before host object files; each Go object
+// read adds its defined package symbols to the global index space.
+// Nonpackage symbols are not yet added.
//
-// - In loader.LoadNonpkgSyms, add non-package defined symbols and
-// references in all object files to the global index space.
+// - In loader.LoadNonpkgSyms, add non-package defined symbols and
+// references in all object files to the global index space.
//
-// - Host object file loading happens; the host object loader does a
-// name/version lookup for each symbol it finds; this can wind up
-// extending the external symbol index space range. The host object
-// loader stores symbol payloads in loader.payloads using SymbolBuilder.
+// - Host object file loading happens; the host object loader does a
+// name/version lookup for each symbol it finds; this can wind up
+// extending the external symbol index space range. The host object
+// loader stores symbol payloads in loader.payloads using SymbolBuilder.
//
-// - Each symbol gets a unique global index. For duplicated and
-// overwriting/overwritten symbols, the second (or later) appearance
-// of the symbol gets the same global index as the first appearance.
+// - Each symbol gets a unique global index. For duplicated and
+// overwriting/overwritten symbols, the second (or later) appearance
+// of the symbol gets the same global index as the first appearance.
type Loader struct {
start map[*oReader]Sym // map from object file to its start index
objs []objIdx // sorted by start index (i.e. objIdx.i)
@@ -2579,7 +2579,6 @@ type ErrorReporter struct {
//
// Logging an error means that on exit cmd/link will delete any
// output file and return a non-zero error code.
-//
func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) {
if s != 0 && reporter.ldr.SymName(s) != "" {
// Note: Replace is needed here because symbol names might have % in them,
diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go
index 9cc7effe1f..bfe2e837c9 100644
--- a/src/cmd/link/internal/loadpe/ldpe.go
+++ b/src/cmd/link/internal/loadpe/ldpe.go
@@ -135,17 +135,6 @@ const (
IMAGE_REL_ARM64_REL32 = 0x0011
)
-// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld, ideally in debug/pe.
-const (
- IMAGE_SCN_CNT_CODE = 0x00000020
- IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
- IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
- IMAGE_SCN_MEM_DISCARDABLE = 0x02000000
- IMAGE_SCN_MEM_EXECUTE = 0x20000000
- IMAGE_SCN_MEM_READ = 0x40000000
- IMAGE_SCN_MEM_WRITE = 0x80000000
-)
-
// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf
// peBiobuf makes bio.Reader look like io.ReaderAt.
@@ -173,14 +162,38 @@ func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loa
return bld
}
+// peLoaderState holds various bits of useful state information needed
+// while loading a PE object file.
+type peLoaderState struct {
+ l *loader.Loader
+ arch *sys.Arch
+ f *pe.File
+ pn string
+ sectsyms map[*pe.Section]loader.Sym
+ defWithImp map[string]struct{}
+ comdats map[uint16]int64 // key is section index, val is size
+ sectdata map[*pe.Section][]byte
+ localSymVersion int
+}
+
+// comdatDefinitions records the names of symbols for which we've
+// previously seen a definition in COMDAT. Key is symbol name, value
+// is symbol size (or -1 if we're using the "any" strategy).
+var comdatDefinitions = make(map[string]int64)
+
// Load loads the PE file pn from input.
// Symbols are written into syms, and a slice of the text symbols is returned.
// If an .rsrc section or set of .rsrc$xx sections is found, its symbols are
// returned as rsrc.
func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc []loader.Sym, err error) {
- lookup := l.LookupOrCreateCgoExport
- sectsyms := make(map[*pe.Section]loader.Sym)
- sectdata := make(map[*pe.Section][]byte)
+ state := &peLoaderState{
+ l: l,
+ arch: arch,
+ sectsyms: make(map[*pe.Section]loader.Sym),
+ sectdata: make(map[*pe.Section][]byte),
+ localSymVersion: localSymVersion,
+ pn: pn,
+ }
// Some input files are archives containing multiple of
// object files, and pe.NewFile seeks to the start of
@@ -194,36 +207,37 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
return nil, nil, err
}
defer f.Close()
+ state.f = f
// TODO return error if found .cormeta
// create symbols for mapped sections
for _, sect := range f.Sections {
- if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+ if sect.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 {
continue
}
- if sect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+ if sect.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
// This has been seen for .idata sections, which we
// want to ignore. See issues 5106 and 5273.
continue
}
name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
- s := lookup(name, localSymVersion)
+ s := state.l.LookupOrCreateCgoExport(name, localSymVersion)
bld := l.MakeSymbolUpdater(s)
- switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
- case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
+ switch sect.Characteristics & (pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE | pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE) {
+ case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ: //.rdata
bld.SetType(sym.SRODATA)
- case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss
+ case pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.bss
bld.SetType(sym.SNOPTRBSS)
- case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data
+ case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.data
bld.SetType(sym.SNOPTRDATA)
- case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text
+ case pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE | pe.IMAGE_SCN_MEM_READ: //.text
bld.SetType(sym.STEXT)
default:
@@ -235,41 +249,48 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
if err != nil {
return nil, nil, err
}
- sectdata[sect] = data
+ state.sectdata[sect] = data
bld.SetData(data)
}
bld.SetSize(int64(sect.Size))
- sectsyms[sect] = s
+ state.sectsyms[sect] = s
if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") {
rsrc = append(rsrc, s)
}
}
+ // Make a prepass over the symbols to detect situations where
+ // we have both a defined symbol X and an import symbol __imp_X
+ // (needed by readpesym()).
+ if err := state.preprocessSymbols(); err != nil {
+ return nil, nil, err
+ }
+
// load relocations
for _, rsect := range f.Sections {
- if _, found := sectsyms[rsect]; !found {
+ if _, found := state.sectsyms[rsect]; !found {
continue
}
if rsect.NumberOfRelocations == 0 {
continue
}
- if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+ if rsect.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 {
continue
}
- if rsect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+ if rsect.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
// This has been seen for .idata sections, which we
// want to ignore. See issues 5106 and 5273.
continue
}
splitResources := strings.HasPrefix(rsect.Name, ".rsrc$")
- sb := l.MakeSymbolUpdater(sectsyms[rsect])
+ sb := l.MakeSymbolUpdater(state.sectsyms[rsect])
for j, r := range rsect.Relocs {
if int(r.SymbolTableIndex) >= len(f.COFFSymbols) {
return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
}
pesym := &f.COFFSymbols[r.SymbolTableIndex]
- _, gosym, err := readpesym(l, arch, lookup, f, pesym, sectsyms, localSymVersion)
+ _, gosym, err := state.readpesym(pesym)
if err != nil {
return nil, nil, err
}
@@ -292,20 +313,20 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
case sys.I386, sys.AMD64:
switch r.Type {
default:
- return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
+ return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, state.sectsyms[rsect], r.Type)
case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
IMAGE_REL_AMD64_ADDR32NB:
rType = objabi.R_PCREL
- rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+ rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
rType = objabi.R_ADDR
// load addend from image
- rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+ rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
rSize = 8
@@ -313,39 +334,39 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
rType = objabi.R_ADDR
// load addend from image
- rAdd = int64(binary.LittleEndian.Uint64(sectdata[rsect][rOff:]))
+ rAdd = int64(binary.LittleEndian.Uint64(state.sectdata[rsect][rOff:]))
}
case sys.ARM:
switch r.Type {
default:
- return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
+ return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, state.sectsyms[rsect], r.Type)
case IMAGE_REL_ARM_SECREL:
rType = objabi.R_PCREL
- rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+ rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
case IMAGE_REL_ARM_ADDR32, IMAGE_REL_ARM_ADDR32NB:
rType = objabi.R_ADDR
- rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+ rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
case IMAGE_REL_ARM_BRANCH24:
rType = objabi.R_CALLARM
- rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+ rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
}
case sys.ARM64:
switch r.Type {
default:
- return nil, nil, fmt.Errorf("%s: %v: unknown ARM64 relocation type %v", pn, sectsyms[rsect], r.Type)
+ return nil, nil, fmt.Errorf("%s: %v: unknown ARM64 relocation type %v", pn, state.sectsyms[rsect], r.Type)
case IMAGE_REL_ARM64_ADDR32, IMAGE_REL_ARM64_ADDR32NB:
rType = objabi.R_ADDR
- rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:])))
+ rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
}
}
@@ -406,12 +427,12 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
var sect *pe.Section
if pesym.SectionNumber > 0 {
sect = f.Sections[pesym.SectionNumber-1]
- if _, found := sectsyms[sect]; !found {
+ if _, found := state.sectsyms[sect]; !found {
continue
}
}
- bld, s, err := readpesym(l, arch, lookup, f, pesym, sectsyms, localSymVersion)
+ bld, s, err := state.readpesym(pesym)
if err != nil {
return nil, nil, err
}
@@ -430,7 +451,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
continue
} else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
sect = f.Sections[pesym.SectionNumber-1]
- if _, found := sectsyms[sect]; !found {
+ if _, found := state.sectsyms[sect]; !found {
return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
}
} else {
@@ -441,17 +462,27 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
return nil, nil, nil
}
+ // Check for COMDAT symbol.
+ if sz, ok1 := state.comdats[uint16(pesym.SectionNumber-1)]; ok1 {
+ if psz, ok2 := comdatDefinitions[l.SymName(s)]; ok2 {
+ if sz == psz {
+ // OK to discard, we've seen an instance
+ // already.
+ continue
+ }
+ }
+ }
if l.OuterSym(s) != 0 {
if l.AttrDuplicateOK(s) {
continue
}
outerName := l.SymName(l.OuterSym(s))
- sectName := l.SymName(sectsyms[sect])
+ sectName := l.SymName(state.sectsyms[sect])
return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
}
bld = makeUpdater(l, bld, s)
- sectsym := sectsyms[sect]
+ sectsym := state.sectsyms[sect]
bld.SetType(l.SymType(sectsym))
l.AddInteriorSym(sectsym, s)
bld.SetValue(int64(pesym.Value))
@@ -462,12 +493,20 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
}
bld.SetExternal(true)
}
+ if sz, ok := state.comdats[uint16(pesym.SectionNumber-1)]; ok {
+ // This is a COMDAT definition. Record that we're picking
+ // this instance so that we can ignore future defs.
+ if _, ok := comdatDefinitions[l.SymName(s)]; ok {
+ return nil, nil, fmt.Errorf("internal error: preexisting COMDAT definition for %q", name)
+ }
+ comdatDefinitions[l.SymName(s)] = sz
+ }
}
// Sort outer lists by address, adding to textp.
// This keeps textp in increasing address order.
for _, sect := range f.Sections {
- s := sectsyms[sect]
+ s := state.sectsyms[sect]
if s == 0 {
continue
}
@@ -490,36 +529,30 @@ func issect(s *pe.COFFSymbol) bool {
return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
}
-func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader.Sym, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]loader.Sym, localSymVersion int) (*loader.SymbolBuilder, loader.Sym, error) {
- symname, err := pesym.FullName(f.StringTable)
+func (state *peLoaderState) readpesym(pesym *pe.COFFSymbol) (*loader.SymbolBuilder, loader.Sym, error) {
+ symname, err := pesym.FullName(state.f.StringTable)
if err != nil {
return nil, 0, err
}
var name string
if issect(pesym) {
- name = l.SymName(sectsyms[f.Sections[pesym.SectionNumber-1]])
+ name = state.l.SymName(state.sectsyms[state.f.Sections[pesym.SectionNumber-1]])
} else {
name = symname
- switch arch.Family {
- case sys.AMD64:
- if name == "__imp___acrt_iob_func" {
- // Do not rename __imp___acrt_iob_func into __acrt_iob_func,
- // because __imp___acrt_iob_func symbol is real
- // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details).
- } else {
- name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
- }
- case sys.I386:
- if name == "__imp____acrt_iob_func" {
- // Do not rename __imp____acrt_iob_func into ___acrt_iob_func,
- // because __imp____acrt_iob_func symbol is real
- // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details).
+ if strings.HasPrefix(symname, "__imp_") {
+ orig := symname[len("__imp_"):]
+ if _, ok := state.defWithImp[orig]; ok {
+ // Don't rename __imp_XXX to XXX, since if we do this
+ // we'll wind up with a duplicate definition. One
+ // example is "__acrt_iob_func"; see commit b295099
+ // from git://git.code.sf.net/p/mingw-w64/mingw-w64
+ // for details.
} else {
name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
}
- if name[0] == '_' {
- name = name[1:] // _Name => Name
- }
+ }
+ if state.arch.Family == sys.I386 && name[0] == '_' {
+ name = name[1:] // _Name => Name
}
}
@@ -537,11 +570,11 @@ func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader
case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
switch pesym.StorageClass {
case IMAGE_SYM_CLASS_EXTERNAL: //global
- s = lookup(name, 0)
+ s = state.l.LookupOrCreateCgoExport(name, 0)
case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
- s = lookup(name, localSymVersion)
- bld = makeUpdater(l, bld, s)
+ s = state.l.LookupOrCreateCgoExport(name, state.localSymVersion)
+ bld = makeUpdater(state.l, bld, s)
bld.SetDuplicateOK(true)
default:
@@ -549,14 +582,81 @@ func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader
}
}
- if s != 0 && l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) {
- bld = makeUpdater(l, bld, s)
+ if s != 0 && state.l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) {
+ bld = makeUpdater(state.l, bld, s)
bld.SetType(sym.SXREF)
}
if strings.HasPrefix(symname, "__imp_") {
- bld = makeUpdater(l, bld, s)
+ bld = makeUpdater(state.l, bld, s)
bld.SetGot(-2) // flag for __imp_
}
return bld, s, nil
}
+
+// preprocessSymbols walks the COFF symbols for the PE file we're
+// reading and looks for cases where we have both a symbol definition
+// for "XXX" and an "__imp_XXX" symbol, recording these cases in a map
+// in the state struct. This information will be used in readpesym()
+// above to give such symbols special treatment. This function also
+// gathers information about COMDAT sections/symbols for later use
+// in readpesym().
+func (state *peLoaderState) preprocessSymbols() error {
+
+ // Locate comdat sections.
+ state.comdats = make(map[uint16]int64)
+ for i, s := range state.f.Sections {
+ if s.Characteristics&uint32(pe.IMAGE_SCN_LNK_COMDAT) != 0 {
+ state.comdats[uint16(i)] = int64(s.Size)
+ }
+ }
+
+ // Examine symbol defs.
+ imp := make(map[string]struct{})
+ def := make(map[string]struct{})
+ for i, numaux := 0, 0; i < len(state.f.COFFSymbols); i += numaux + 1 {
+ pesym := &state.f.COFFSymbols[i]
+ numaux = int(pesym.NumberOfAuxSymbols)
+ if pesym.SectionNumber == 0 { // extern
+ continue
+ }
+ symname, err := pesym.FullName(state.f.StringTable)
+ if err != nil {
+ return err
+ }
+ def[symname] = struct{}{}
+ if strings.HasPrefix(symname, "__imp_") {
+ imp[strings.TrimPrefix(symname, "__imp_")] = struct{}{}
+ }
+ if _, isc := state.comdats[uint16(pesym.SectionNumber-1)]; !isc {
+ continue
+ }
+ if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) {
+ continue
+ }
+ // This symbol corresponds to a COMDAT section. Read the
+ // aux data for it.
+ auxsymp, err := state.f.COFFSymbolReadSectionDefAux(i)
+ if err != nil {
+ return fmt.Errorf("unable to read aux info for section def symbol %d %s: pe.COFFSymbolReadComdatInfo returns %v", i, symname, err)
+ }
+ if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_SAME_SIZE {
+ // This is supported.
+ } else if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_ANY {
+ // Also supported.
+ state.comdats[uint16(pesym.SectionNumber-1)] = int64(-1)
+ } else {
+ // We don't support any of the other strategies at the
+ // moment. I suspect that we may need to also support
+ // "associative", we'll see.
+ return fmt.Errorf("internal error: unsupported COMDAT selection strategy found in path=%s sec=%d strategy=%d idx=%d, please file a bug", state.pn, auxsymp.SecNum, auxsymp.Selection, i)
+ }
+ }
+ state.defWithImp = make(map[string]struct{})
+ for n := range imp {
+ if _, ok := def[n]; ok {
+ state.defWithImp[n] = struct{}{}
+ }
+ }
+ return nil
+}
diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go
index 1952971dcb..f26b3f3cf2 100644
--- a/src/cmd/link/internal/s390x/asm.go
+++ b/src/cmd/link/internal/s390x/asm.go
@@ -43,10 +43,10 @@ import (
// moduledata linked list at initialization time. This is only done if the runtime
// is in a different module.
//
-// <go.link.addmoduledata>:
-// larl %r2, <local.moduledata>
-// jg <runtime.addmoduledata@plt>
-// undef
+// <go.link.addmoduledata>:
+// larl %r2, <local.moduledata>
+// jg <runtime.addmoduledata@plt>
+// undef
//
// The job of appending the moduledata is delegated to runtime.addmoduledata.
func gentext(ctxt *ld.Link, ldr *loader.Loader) {