diff options
| author | Lasse Folger <lassefolger@google.com> | 2022-04-05 09:19:57 +0200 |
|---|---|---|
| committer | Lasse Folger <lassefolger@google.com> | 2022-04-05 18:53:15 +0200 |
| commit | 4739b353bb878f29ee78e1cd7eaf3d8f32199798 (patch) | |
| tree | 40bb8ba155a8dc87b4794a4fcf224d03f555502c /src/cmd/link | |
| parent | 9d6ab825f6fe125f7ce630e103b887e580403802 (diff) | |
| parent | c18f398f32c45afe2e9a81a6d885a4e0183cd649 (diff) | |
| download | go-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.go | 28 | ||||
| -rw-r--r-- | src/cmd/link/internal/benchmark/bench.go | 20 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/ar.go | 13 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/dwarf_test.go | 1 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/elf.go | 57 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/lib.go | 191 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/outbuf.go | 12 | ||||
| -rw-r--r-- | src/cmd/link/internal/loader/loader.go | 25 | ||||
| -rw-r--r-- | src/cmd/link/internal/loadpe/ldpe.go | 242 | ||||
| -rw-r--r-- | src/cmd/link/internal/s390x/asm.go | 8 |
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) { |
