summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/internal
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link/internal')
-rw-r--r--src/cmd/link/internal/amd64/asm.go674
-rw-r--r--src/cmd/link/internal/amd64/l.go43
-rw-r--r--src/cmd/link/internal/amd64/obj.go123
-rw-r--r--src/cmd/link/internal/arm/asm.go694
-rw-r--r--src/cmd/link/internal/arm/l.go75
-rw-r--r--src/cmd/link/internal/arm/obj.go111
-rw-r--r--src/cmd/link/internal/arm64/asm.go1389
-rw-r--r--src/cmd/link/internal/arm64/l.go74
-rw-r--r--src/cmd/link/internal/arm64/obj.go120
-rw-r--r--src/cmd/link/internal/benchmark/bench.go195
-rw-r--r--src/cmd/link/internal/benchmark/bench_test.go54
-rw-r--r--src/cmd/link/internal/dwtest/dwtest.go197
-rw-r--r--src/cmd/link/internal/ld/ar.go247
-rw-r--r--src/cmd/link/internal/ld/asmb.go208
-rw-r--r--src/cmd/link/internal/ld/config.go307
-rw-r--r--src/cmd/link/internal/ld/data.go2956
-rw-r--r--src/cmd/link/internal/ld/data_test.go93
-rw-r--r--src/cmd/link/internal/ld/deadcode.go469
-rw-r--r--src/cmd/link/internal/ld/deadcode_test.go50
-rw-r--r--src/cmd/link/internal/ld/decodesym.go310
-rw-r--r--src/cmd/link/internal/ld/dwarf.go2328
-rw-r--r--src/cmd/link/internal/ld/dwarf_test.go1971
-rw-r--r--src/cmd/link/internal/ld/elf.go2395
-rw-r--r--src/cmd/link/internal/ld/elf_test.go127
-rw-r--r--src/cmd/link/internal/ld/errors.go67
-rw-r--r--src/cmd/link/internal/ld/execarchive.go38
-rw-r--r--src/cmd/link/internal/ld/execarchive_noexec.go14
-rw-r--r--src/cmd/link/internal/ld/fallocate_test.go65
-rw-r--r--src/cmd/link/internal/ld/go.go499
-rw-r--r--src/cmd/link/internal/ld/go_test.go116
-rw-r--r--src/cmd/link/internal/ld/heap.go54
-rw-r--r--src/cmd/link/internal/ld/heap_test.go90
-rw-r--r--src/cmd/link/internal/ld/issue33808_test.go49
-rw-r--r--src/cmd/link/internal/ld/ld.go256
-rw-r--r--src/cmd/link/internal/ld/ld_test.go343
-rw-r--r--src/cmd/link/internal/ld/lib.go2696
-rw-r--r--src/cmd/link/internal/ld/link.go161
-rw-r--r--src/cmd/link/internal/ld/macho.go1568
-rw-r--r--src/cmd/link/internal/ld/macho_combine_dwarf.go438
-rw-r--r--src/cmd/link/internal/ld/main.go444
-rw-r--r--src/cmd/link/internal/ld/msync_darwin_libc.go12
-rw-r--r--src/cmd/link/internal/ld/msync_darwin_syscall.go24
-rw-r--r--src/cmd/link/internal/ld/nooptcgolink_test.go29
-rw-r--r--src/cmd/link/internal/ld/outbuf.go325
-rw-r--r--src/cmd/link/internal/ld/outbuf_darwin.go48
-rw-r--r--src/cmd/link/internal/ld/outbuf_linux.go11
-rw-r--r--src/cmd/link/internal/ld/outbuf_mmap.go59
-rw-r--r--src/cmd/link/internal/ld/outbuf_nofallocate.go11
-rw-r--r--src/cmd/link/internal/ld/outbuf_nommap.go22
-rw-r--r--src/cmd/link/internal/ld/outbuf_notdarwin.go10
-rw-r--r--src/cmd/link/internal/ld/outbuf_test.go93
-rw-r--r--src/cmd/link/internal/ld/outbuf_windows.go79
-rw-r--r--src/cmd/link/internal/ld/pcln.go935
-rw-r--r--src/cmd/link/internal/ld/pe.go1709
-rw-r--r--src/cmd/link/internal/ld/stackcheck.go421
-rw-r--r--src/cmd/link/internal/ld/stackcheck_test.go87
-rw-r--r--src/cmd/link/internal/ld/sym.go117
-rw-r--r--src/cmd/link/internal/ld/symtab.go890
-rw-r--r--src/cmd/link/internal/ld/target.go206
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go30
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go22
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go29
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go25
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go24
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/typedesc.go16
-rw-r--r--src/cmd/link/internal/ld/testdata/httptest/main/main.go22
-rw-r--r--src/cmd/link/internal/ld/testdata/issue10978/main.go27
-rw-r--r--src/cmd/link/internal/ld/testdata/issue10978/main.s1
-rw-r--r--src/cmd/link/internal/ld/testdata/issue25459/a/a.go27
-rw-r--r--src/cmd/link/internal/ld/testdata/issue25459/main/main.go10
-rw-r--r--src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go16
-rw-r--r--src/cmd/link/internal/ld/testdata/issue26237/main/main.go16
-rw-r--r--src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m16
-rw-r--r--src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go19
-rw-r--r--src/cmd/link/internal/ld/testdata/issue32233/main/main.go11
-rw-r--r--src/cmd/link/internal/ld/testdata/issue38192/main.go11
-rw-r--r--src/cmd/link/internal/ld/testdata/issue38192/oneline.s8
-rw-r--r--src/cmd/link/internal/ld/testdata/issue39256/x.go22
-rw-r--r--src/cmd/link/internal/ld/testdata/issue39256/x.s10
-rw-r--r--src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go15
-rw-r--r--src/cmd/link/internal/ld/testdata/issue42484/main.go16
-rw-r--r--src/cmd/link/internal/ld/testdata/stackcheck/main.go20
-rw-r--r--src/cmd/link/internal/ld/testdata/stackcheck/main.s40
-rw-r--r--src/cmd/link/internal/ld/typelink.go72
-rw-r--r--src/cmd/link/internal/ld/util.go114
-rw-r--r--src/cmd/link/internal/ld/xcoff.go1815
-rw-r--r--src/cmd/link/internal/loadelf/ldelf.go1151
-rw-r--r--src/cmd/link/internal/loader/loader.go2601
-rw-r--r--src/cmd/link/internal/loader/loader_test.go455
-rw-r--r--src/cmd/link/internal/loader/symbolbuilder.go446
-rw-r--r--src/cmd/link/internal/loadmacho/ldmacho.go806
-rw-r--r--src/cmd/link/internal/loadpe/ldpe.go777
-rw-r--r--src/cmd/link/internal/loadxcoff/ldxcoff.go228
-rw-r--r--src/cmd/link/internal/loong64/asm.go240
-rw-r--r--src/cmd/link/internal/loong64/l.go17
-rw-r--r--src/cmd/link/internal/loong64/obj.go59
-rw-r--r--src/cmd/link/internal/mips/asm.go155
-rw-r--r--src/cmd/link/internal/mips/l.go74
-rw-r--r--src/cmd/link/internal/mips/obj.go92
-rw-r--r--src/cmd/link/internal/mips64/asm.go169
-rw-r--r--src/cmd/link/internal/mips64/l.go74
-rw-r--r--src/cmd/link/internal/mips64/obj.go102
-rw-r--r--src/cmd/link/internal/ppc64/asm.go1529
-rw-r--r--src/cmd/link/internal/ppc64/l.go74
-rw-r--r--src/cmd/link/internal/ppc64/obj.go113
-rw-r--r--src/cmd/link/internal/riscv64/asm.go361
-rw-r--r--src/cmd/link/internal/riscv64/l.go14
-rw-r--r--src/cmd/link/internal/riscv64/obj.go68
-rw-r--r--src/cmd/link/internal/s390x/asm.go449
-rw-r--r--src/cmd/link/internal/s390x/l.go74
-rw-r--r--src/cmd/link/internal/s390x/obj.go88
-rw-r--r--src/cmd/link/internal/sym/compilation_unit.go35
-rw-r--r--src/cmd/link/internal/sym/library.go27
-rw-r--r--src/cmd/link/internal/sym/reloc.go76
-rw-r--r--src/cmd/link/internal/sym/segment.go68
-rw-r--r--src/cmd/link/internal/sym/symbol.go42
-rw-r--r--src/cmd/link/internal/sym/symkind.go181
-rw-r--r--src/cmd/link/internal/sym/symkind_string.go81
-rw-r--r--src/cmd/link/internal/wasm/asm.go604
-rw-r--r--src/cmd/link/internal/wasm/obj.go35
-rw-r--r--src/cmd/link/internal/x86/asm.go490
-rw-r--r--src/cmd/link/internal/x86/l.go43
-rw-r--r--src/cmd/link/internal/x86/obj.go117
123 files changed, 41665 insertions, 0 deletions
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go
new file mode 100644
index 0000000..f4832ef
--- /dev/null
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -0,0 +1,674 @@
+// Inferno utils/6l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package amd64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "log"
+)
+
+func PADDR(x uint32) uint32 {
+ return x &^ 0x80000000
+}
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op ...uint8) {
+ for _, op1 := range op {
+ initfunc.AddUint8(op1)
+ }
+ }
+
+ // 0000000000000000 <local.dso_init>:
+ // 0: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 7 <local.dso_init+0x7>
+ // 3: R_X86_64_PC32 runtime.firstmoduledata-0x4
+ o(0x48, 0x8d, 0x3d)
+ initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 0)
+ // 7: e8 00 00 00 00 callq c <local.dso_init+0xc>
+ // 8: R_X86_64_PLT32 runtime.addmoduledata-0x4
+ o(0xe8)
+ initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_CALL, 4)
+ // c: c3 retq
+ o(0xc3)
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch rt := r.Type(); rt {
+ default:
+ if rt >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_X86_64_PC32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_X86_64_PC64 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+8)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PLT32):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCREL),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCRELX),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_REX_GOTPCRELX):
+ su := ldr.MakeSymbolUpdater(s)
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ sData := ldr.Data(s)
+ if r.Off() >= 2 && sData[r.Off()-2] == 0x8b {
+ su.MakeWritable()
+ // turn MOVQ of GOT entry into LEAQ of symbol itself
+ writeableData := su.Data()
+ writeableData[r.Off()-2] = 0x8d
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+ }
+ }
+
+ // fall back to using GOT and hope for the best (CMOV*)
+ // TODO: just needs relocation, no need to put in .dynsym
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_X86_64_GLOB_DAT))
+
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+4+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_X86_64_64 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if target.IsPIE() && target.IsInternal() {
+ // For internal linking PIE, this R_ADDR relocation cannot
+ // be resolved statically. We need to generate a dynamic
+ // relocation. Let the code below handle it.
+ break
+ }
+ return true
+
+ // Handle relocations found in Mach-O object files.
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0:
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if target.IsPIE() && target.IsInternal() {
+ // For internal linking PIE, this R_ADDR relocation cannot
+ // be resolved statically. We need to generate a dynamic
+ // relocation. Let the code below handle it.
+ if rt == objabi.MachoRelocOffset+ld.MACHO_X86_64_RELOC_UNSIGNED*2 {
+ break
+ } else {
+ // MACHO_X86_64_RELOC_SIGNED or MACHO_X86_64_RELOC_BRANCH
+ // Can this happen? The object is expected to be PIC.
+ ldr.Errorf(s, "unsupported relocation for PIE: %v", rt)
+ }
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1:
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ return true
+ }
+ fallthrough
+
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 1,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 1,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_1*2 + 1,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_2*2 + 1,
+ objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_4*2 + 1:
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected pc-relative reloc for dynamic symbol %s", ldr.SymName(targ))
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1:
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ // turn MOVQ of GOT entry into LEAQ of symbol itself
+ sdata := ldr.Data(s)
+ if r.Off() < 2 || sdata[r.Off()-2] != 0x8b {
+ ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+
+ su := ldr.MakeSymbolUpdater(s)
+ su.MakeWritable()
+ sdata = su.Data()
+ sdata[r.Off()-2] = 0x8d
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+ }
+ fallthrough
+
+ case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT*2 + 1:
+ if targType != sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ }
+ ld.AddGotSym(target, ldr, syms, targ, 0)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+ }
+
+ // Reread the reloc to incorporate any changes in type above.
+ relocs := ldr.Relocs(s)
+ r = relocs.At(rIdx)
+
+ switch r.Type() {
+ case objabi.R_CALL,
+ objabi.R_PCREL:
+ if targType != sym.SDYNIMPORT {
+ // nothing to do, the relocation will be laid out in reloc
+ return true
+ }
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ // Internal linking, for both ELF and Mach-O.
+ // Build a PLT entry and change the relocation target to that entry.
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ return true
+
+ case objabi.R_ADDR:
+ if ldr.SymType(s) == sym.STEXT && target.IsElf() {
+ su := ldr.MakeSymbolUpdater(s)
+ if target.IsSolaris() {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ return true
+ }
+ // The code is asking for the address of an external
+ // function. We provide it with the address of the
+ // correspondent GOT symbol.
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_X86_64_GLOB_DAT))
+
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+ }
+
+ // Process dynamic relocations for the data sections.
+ if target.IsPIE() && target.IsInternal() {
+ // When internally linking, generate dynamic relocations
+ // for all typical R_ADDR relocations. The exception
+ // are those R_ADDR that are created as part of generating
+ // the dynamic relocations and must be resolved statically.
+ //
+ // There are three phases relevant to understanding this:
+ //
+ // dodata() // we are here
+ // address() // symbol address assignment
+ // reloc() // resolution of static R_ADDR relocs
+ //
+ // At this point symbol addresses have not been
+ // assigned yet (as the final size of the .rela section
+ // will affect the addresses), and so we cannot write
+ // the Elf64_Rela.r_offset now. Instead we delay it
+ // until after the 'address' phase of the linker is
+ // complete. We do this via Addaddrplus, which creates
+ // a new R_ADDR relocation which will be resolved in
+ // the 'reloc' phase.
+ //
+ // These synthetic static R_ADDR relocs must be skipped
+ // now, or else we will be caught in an infinite loop
+ // of generating synthetic relocs for our synthetic
+ // relocs.
+ //
+ // Furthermore, the rela sections contain dynamic
+ // relocations with R_ADDR relocations on
+ // Elf64_Rela.r_offset. This field should contain the
+ // symbol offset as determined by reloc(), not the
+ // final dynamically linked address as a dynamic
+ // relocation would provide.
+ switch ldr.SymName(s) {
+ case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic":
+ return false
+ }
+ } else {
+ // Either internally linking a static executable,
+ // in which case we can resolve these relocations
+ // statically in the 'reloc' phase, or externally
+ // linking, in which case the relocation will be
+ // prepared in the 'reloc' phase and passed to the
+ // external linker in the 'asmb' phase.
+ if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA {
+ break
+ }
+ }
+
+ if target.IsElf() {
+ // Generate R_X86_64_RELATIVE relocations for best
+ // efficiency in the dynamic linker.
+ //
+ // As noted above, symbol addresses have not been
+ // assigned yet, so we can't generate the final reloc
+ // entry yet. We ultimately want:
+ //
+ // r_offset = s + r.Off
+ // r_info = R_X86_64_RELATIVE
+ // r_addend = targ + r.Add
+ //
+ // The dynamic linker will set *offset = base address +
+ // addend.
+ //
+ // AddAddrPlus is used for r_offset and r_addend to
+ // generate new R_ADDR relocations that will update
+ // these fields in the 'reloc' phase.
+ rela := ldr.MakeSymbolUpdater(syms.Rela)
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ if r.Siz() == 8 {
+ rela.AddUint64(target.Arch, elf.R_INFO(0, uint32(elf.R_X86_64_RELATIVE)))
+ } else {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ rela.AddAddrPlus(target.Arch, targ, int64(r.Add()))
+ // Not mark r done here. So we still apply it statically,
+ // so in the file content we'll also have the right offset
+ // to the relocation target. So it can be examined statically
+ // (e.g. go version).
+ return true
+ }
+
+ if target.IsDarwin() {
+ // Mach-O relocations are a royal pain to lay out.
+ // They use a compact stateful bytecode representation.
+ // Here we record what are needed and encode them later.
+ ld.MachoAddRebase(s, int64(r.Off()))
+ // Not mark r done here. So we still apply it statically,
+ // so in the file content we'll also have the right offset
+ // to the relocation target. So it can be examined statically
+ // (e.g. go version).
+ return true
+ }
+ }
+
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_X86_64_32) | uint64(elfsym)<<32)
+ } else if siz == 8 {
+ out.Write64(uint64(elf.R_X86_64_64) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ case objabi.R_TLS_LE:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_X86_64_TPOFF32) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ case objabi.R_TLS_IE:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_X86_64_GOTTPOFF) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ case objabi.R_CALL:
+ if siz == 4 {
+ if ldr.SymType(r.Xsym) == sym.SDYNIMPORT {
+ out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32)
+ } else {
+ out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_PCREL:
+ if siz == 4 {
+ if ldr.SymType(r.Xsym) == sym.SDYNIMPORT && ldr.SymElfType(r.Xsym) == elf.STT_FUNC {
+ out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32)
+ } else {
+ out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_GOTPCREL:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_X86_64_GOTPCREL) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ }
+
+ out.Write64(uint64(r.Xadd))
+ return true
+}
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ var v uint32
+
+ rs := r.Xsym
+ rt := r.Type
+
+ if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_PCREL || rt == objabi.R_GOTPCREL || rt == objabi.R_CALL {
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ v = uint32(ldr.SymDynid(rs))
+ v |= 1 << 27 // external relocation
+ } else {
+ v = uint32(ldr.SymSect(rs).Extnum)
+ if v == 0 {
+ ldr.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymSect(rs).Name, ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+ }
+
+ switch rt {
+ default:
+ return false
+
+ case objabi.R_ADDR:
+ v |= ld.MACHO_X86_64_RELOC_UNSIGNED << 28
+
+ case objabi.R_CALL:
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_X86_64_RELOC_BRANCH << 28
+
+ // NOTE: Only works with 'external' relocation. Forced above.
+ case objabi.R_PCREL:
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_X86_64_RELOC_SIGNED << 28
+ case objabi.R_GOTPCREL:
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_X86_64_RELOC_GOT_LOAD << 28
+ }
+
+ switch r.Size {
+ default:
+ return false
+
+ case 1:
+ v |= 0 << 25
+
+ case 2:
+ v |= 1 << 25
+
+ case 4:
+ v |= 2 << 25
+
+ case 8:
+ v |= 3 << 25
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(v)
+ return true
+}
+
+func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ var v uint32
+
+ rs := r.Xsym
+ rt := r.Type
+
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(ldr.SymDynid(rs)))
+
+ switch rt {
+ default:
+ return false
+
+ case objabi.R_DWARFSECREF:
+ v = ld.IMAGE_REL_AMD64_SECREL
+
+ case objabi.R_ADDR:
+ if r.Size == 8 {
+ v = ld.IMAGE_REL_AMD64_ADDR64
+ } else {
+ v = ld.IMAGE_REL_AMD64_ADDR32
+ }
+
+ case objabi.R_CALL,
+ objabi.R_PCREL:
+ v = ld.IMAGE_REL_AMD64_REL32
+ }
+
+ out.Write16(uint16(v))
+
+ return true
+}
+
+func archreloc(*ld.Target, *loader.Loader, *ld.ArchSyms, loader.Reloc, loader.Sym, int64) (int64, int, bool) {
+ return -1, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
+ log.Fatalf("unexpected relocation variant")
+ return -1
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // pushq got+8(IP)
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x35)
+ plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 8)
+
+ // jmpq got+16(IP)
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x25)
+ plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 16)
+
+ // nopl 0(AX)
+ plt.AddUint32(ctxt.Arch, 0x00401f0f)
+
+ // assume got->size == 0 too
+ got.AddAddrPlus(ctxt.Arch, dynamic, 0)
+
+ got.AddUint64(ctxt.Arch, 0)
+ got.AddUint64(ctxt.Arch, 0)
+ }
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ got := ldr.MakeSymbolUpdater(syms.GOTPLT)
+ rela := ldr.MakeSymbolUpdater(syms.RelaPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // jmpq *got+size(IP)
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x25)
+ plt.AddPCRelPlus(target.Arch, got.Sym(), got.Size())
+
+ // add to got: pointer to current pos in plt
+ got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size())
+
+ // pushq $x
+ plt.AddUint8(0x68)
+
+ plt.AddUint32(target.Arch, uint32((got.Size()-24-8)/8))
+
+ // jmpq .plt
+ plt.AddUint8(0xe9)
+
+ plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4)))
+
+ // rela
+ rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8)
+
+ sDynid := ldr.SymDynid(s)
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_X86_64_JMP_SLOT)))
+ rela.AddUint64(target.Arch, 0)
+
+ ldr.SetPlt(s, int32(plt.Size()-16))
+ } else if target.IsDarwin() {
+ ld.AddGotSym(target, ldr, syms, s, 0)
+
+ sDynid := ldr.SymDynid(s)
+ lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT)
+ lep.AddUint32(target.Arch, uint32(sDynid))
+
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ ldr.SetPlt(s, int32(plt.Size()))
+
+ // jmpq *got+size(IP)
+ plt.AddUint8(0xff)
+ plt.AddUint8(0x25)
+ plt.AddPCRelPlus(target.Arch, syms.GOT, int64(ldr.SymGot(s)))
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
+
+func tlsIEtoLE(P []byte, off, size int) {
+ // Transform the PC-relative instruction into a constant load.
+ // That is,
+ //
+ // MOVQ X(IP), REG -> MOVQ $Y, REG
+ //
+ // To determine the instruction and register, we study the op codes.
+ // Consult an AMD64 instruction encoding guide to decipher this.
+ if off < 3 {
+ log.Fatal("R_X86_64_GOTTPOFF reloc not preceded by MOVQ or ADDQ instruction")
+ }
+ op := P[off-3 : off]
+ reg := op[2] >> 3
+
+ if op[1] == 0x8b || reg == 4 {
+ // MOVQ
+ if op[0] == 0x4c {
+ op[0] = 0x49
+ } else if size == 4 && op[0] == 0x44 {
+ op[0] = 0x41
+ }
+ if op[1] == 0x8b {
+ op[1] = 0xc7
+ } else {
+ op[1] = 0x81 // special case for SP
+ }
+ op[2] = 0xc0 | reg
+ } else {
+ // An alternate op is ADDQ. This is handled by GNU gold,
+ // but right now is not generated by the Go compiler:
+ // ADDQ X(IP), REG -> ADDQ $Y, REG
+ // Consider adding support for it here.
+ log.Fatalf("expected TLS IE op to be MOVQ, got %v", op)
+ }
+}
diff --git a/src/cmd/link/internal/amd64/l.go b/src/cmd/link/internal/amd64/l.go
new file mode 100644
index 0000000..c9ea90a
--- /dev/null
+++ b/src/cmd/link/internal/amd64/l.go
@@ -0,0 +1,43 @@
+// Inferno utils/6l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package amd64
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 32
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 7
+ dwarfRegLR = 16
+)
diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go
new file mode 100644
index 0000000..c5e2117
--- /dev/null
+++ b/src/cmd/link/internal/amd64/obj.go
@@ -0,0 +1,123 @@
+// Inferno utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package amd64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchAMD64
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ // 0xCC is INT $3 - breakpoint instruction
+ CodePad: []byte{0xCC},
+
+ Plan9Magic: uint32(4*26*26 + 7),
+ Plan9_64Bit: true,
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+ MachorelocSize: 8,
+ PEreloc1: pereloc1,
+ TLSIEtoLE: tlsIEtoLE,
+
+ Linuxdynld: "/lib64/ld-linux-x86-64.so.2",
+ LinuxdynldMusl: "/lib/ld-musl-x86_64.so.1",
+ Freebsddynld: "/libexec/ld-elf.so.1",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "/libexec/ld.elf_so",
+ Dragonflydynld: "/usr/libexec/ld-elf.so.2",
+ Solarisdynld: "/lib/amd64/ld.so.1",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32 + 8
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x200000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x200000
+ }
+
+ case objabi.Hdarwin: /* apple MACH */
+ ld.HEADR = ld.INITIAL_MACHO_HEADR
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x1000000 + int64(ld.HEADR)
+ }
+
+ case objabi.Hlinux, /* elf64 executable */
+ objabi.Hfreebsd, /* freebsd */
+ objabi.Hnetbsd, /* netbsd */
+ objabi.Hopenbsd, /* openbsd */
+ objabi.Hdragonfly, /* dragonfly */
+ objabi.Hsolaris: /* solaris */
+ ld.Elfinit(ctxt)
+
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = (1 << 22) + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hwindows: /* PE executable */
+ // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+ return
+ }
+}
diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go
new file mode 100644
index 0000000..4574f2d
--- /dev/null
+++ b/src/cmd/link/internal/arm/asm.go
@@ -0,0 +1,694 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package arm
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "log"
+)
+
+// This assembler:
+//
+// .align 2
+// local.dso_init:
+// ldr r0, .Lmoduledata
+// .Lloadfrom:
+// ldr r0, [r0]
+// b runtime.addmoduledata@plt
+// .align 2
+// .Lmoduledata:
+// .word local.moduledata(GOT_PREL) + (. - (.Lloadfrom + 4))
+// assembles to:
+//
+// 00000000 <local.dso_init>:
+// 0: e59f0004 ldr r0, [pc, #4] ; c <local.dso_init+0xc>
+// 4: e5900000 ldr r0, [r0]
+// 8: eafffffe b 0 <runtime.addmoduledata>
+// 8: R_ARM_JUMP24 runtime.addmoduledata
+// c: 00000004 .word 0x00000004
+// c: R_ARM_GOT_PREL local.moduledata
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op uint32) {
+ initfunc.AddUint32(ctxt.Arch, op)
+ }
+ o(0xe59f0004)
+ o(0xe08f0000)
+
+ o(0xeafffffe)
+ rel, _ := initfunc.AddRel(objabi.R_CALLARM)
+ rel.SetOff(8)
+ rel.SetSiz(4)
+ rel.SetSym(addmoduledata)
+ rel.SetAdd(0xeafffffe) // vomit
+
+ o(0x00000000)
+
+ rel2, _ := initfunc.AddRel(objabi.R_PCREL)
+ rel2.SetOff(12)
+ rel2.SetSiz(4)
+ rel2.SetSym(ctxt.Moduledata)
+ rel2.SetAdd(4)
+}
+
+// Preserve highest 8 bits of a, and do addition to lower 24-bit
+// of a and b; used to adjust ARM branch instruction's target.
+func braddoff(a int32, b int32) int32 {
+ return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b))
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PLT32):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM)
+
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4)))
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_THM_PC22): // R_ARM_THM_CALL
+ ld.Exitf("R_ARM_THM_CALL, are you using -marm?")
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT32): // R_ARM_GOT_BREL
+ if targType != sym.SDYNIMPORT {
+ addgotsyminternal(target, ldr, syms, targ)
+ } else {
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT))
+ }
+
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
+ su.SetRelocSym(rIdx, 0)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT_PREL): // GOT(nil) + A - nil
+ if targType != sym.SDYNIMPORT {
+ addgotsyminternal(target, ldr, syms, targ)
+ } else {
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+4+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTOFF): // R_ARM_GOTOFF32
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_GOTOFF)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTPC): // R_ARM_BASE_PREL
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4)))
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_REL32): // R_ARM_REL32
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_ABS32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_ARM_ABS32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4)))
+ }
+
+ return true
+ }
+
+ // Handle references to ELF symbols from our own object files.
+ if targType != sym.SDYNIMPORT {
+ return true
+ }
+
+ // Reread the reloc to incorporate any changes in type above.
+ relocs := ldr.Relocs(s)
+ r = relocs.At(rIdx)
+
+ switch r.Type() {
+ case objabi.R_CALLARM:
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) // TODO: don't use r.Add for instruction bytes (issue 19811)
+ return true
+
+ case objabi.R_ADDR:
+ if ldr.SymType(s) != sym.SDATA {
+ break
+ }
+ if target.IsElf() {
+ ld.Adddynsym(ldr, target, syms, targ)
+ rel := ldr.MakeSymbolUpdater(syms.Rel)
+ rel.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(targ)), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
+ su.SetRelocSym(rIdx, 0)
+ return true
+ }
+
+ case objabi.R_GOTPCREL:
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ if targType != sym.SDYNIMPORT {
+ ldr.Errorf(s, "R_GOTPCREL target is not SDYNIMPORT symbol: %v", ldr.SymName(targ))
+ }
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT))
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+ }
+
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write32(uint32(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_ARM_ABS32) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_PCREL:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_ARM_REL32) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_CALLARM:
+ if siz == 4 {
+ relocs := ldr.Relocs(s)
+ r := relocs.At(ri)
+ if r.Add()&0xff000000 == 0xeb000000 { // BL // TODO: using r.Add here is bad (issue 19811)
+ out.Write32(uint32(elf.R_ARM_CALL) | uint32(elfsym)<<8)
+ } else {
+ out.Write32(uint32(elf.R_ARM_JUMP24) | uint32(elfsym)<<8)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_TLS_LE:
+ out.Write32(uint32(elf.R_ARM_TLS_LE32) | uint32(elfsym)<<8)
+ case objabi.R_TLS_IE:
+ out.Write32(uint32(elf.R_ARM_TLS_IE32) | uint32(elfsym)<<8)
+ case objabi.R_GOTPCREL:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_ARM_GOT_PREL) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ }
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // str lr, [sp, #-4]!
+ plt.AddUint32(ctxt.Arch, 0xe52de004)
+
+ // ldr lr, [pc, #4]
+ plt.AddUint32(ctxt.Arch, 0xe59fe004)
+
+ // add lr, pc, lr
+ plt.AddUint32(ctxt.Arch, 0xe08fe00e)
+
+ // ldr pc, [lr, #8]!
+ plt.AddUint32(ctxt.Arch, 0xe5bef008)
+
+ // .word &GLOBAL_OFFSET_TABLE[0] - .
+ plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 4)
+
+ // the first .plt entry requires 3 .plt.got entries
+ got.AddUint32(ctxt.Arch, 0)
+
+ got.AddUint32(ctxt.Arch, 0)
+ got.AddUint32(ctxt.Arch, 0)
+ }
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ rs := r.Xsym
+ rt := r.Type
+
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(ldr.SymDynid(rs)))
+
+ var v uint32
+ switch rt {
+ default:
+ // unsupported relocation type
+ return false
+
+ case objabi.R_DWARFSECREF:
+ v = ld.IMAGE_REL_ARM_SECREL
+
+ case objabi.R_ADDR:
+ v = ld.IMAGE_REL_ARM_ADDR32
+ }
+
+ out.Write16(uint16(v))
+
+ return true
+}
+
+// sign extend a 24-bit integer.
+func signext24(x int64) int32 {
+ return (int32(x) << 8) >> 8
+}
+
+// encode an immediate in ARM's imm12 format. copied from ../../../internal/obj/arm/asm5.go
+func immrot(v uint32) uint32 {
+ for i := 0; i < 16; i++ {
+ if v&^0xff == 0 {
+ return uint32(i<<8) | v | 1<<25
+ }
+ v = v<<2 | v>>30
+ }
+ return 0
+}
+
+// Convert the direct jump relocation r to refer to a trampoline if the target is too far.
+func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
+ relocs := ldr.Relocs(s)
+ r := relocs.At(ri)
+ switch r.Type() {
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24):
+ // Host object relocations that will be turned into a PLT call.
+ // The PLT may be too far. Insert a trampoline for them.
+ fallthrough
+ case objabi.R_CALLARM:
+ var t int64
+ // ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet
+ // laid out. Conservatively use a trampoline. This should be rare, as we lay out packages
+ // in dependency order.
+ if ldr.SymValue(rs) != 0 {
+ // Workaround for issue #58425: it appears that the
+ // external linker doesn't always take into account the
+ // relocation addend when doing reachability checks. This
+ // means that if you have a call from function XYZ at
+ // offset 8 to runtime.duffzero with addend 800 (for
+ // example), where the distance between the start of XYZ
+ // and the start of runtime.duffzero is just over the
+ // limit (by 100 bytes, say), you can get "relocation
+ // doesn't fit" errors from the external linker. To deal
+ // with this, ignore the addend when performing the
+ // distance calculation (this assumes that we're only
+ // handling backward jumps; ideally we might want to check
+ // both with and without the addend).
+ if ctxt.IsExternal() {
+ t = (ldr.SymValue(rs) - (ldr.SymValue(s) + int64(r.Off()))) / 4
+ } else {
+ // r.Add is the instruction
+ // low 24-bit encodes the target address
+ t = (ldr.SymValue(rs) + int64(signext24(r.Add()&0xffffff)*4) - (ldr.SymValue(s) + int64(r.Off()))) / 4
+ }
+ }
+ if t > 0x7fffff || t <= -0x800000 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && ldr.SymPkg(s) != ldr.SymPkg(rs)) {
+ // direct call too far, need to insert trampoline.
+ // look up existing trampolines first. if we found one within the range
+ // of direct call, we can reuse it. otherwise create a new one.
+ offset := (signext24(r.Add()&0xffffff) + 2) * 4
+ var tramp loader.Sym
+ for i := 0; ; i++ {
+ oName := ldr.SymName(rs)
+ name := oName + fmt.Sprintf("%+d-tramp%d", offset, i)
+ tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
+ ldr.SetAttrReachable(tramp, true)
+ if ldr.SymType(tramp) == sym.SDYNIMPORT {
+ // don't reuse trampoline defined in other module
+ continue
+ }
+ if oName == "runtime.deferreturn" {
+ ldr.SetIsDeferReturnTramp(tramp, true)
+ }
+ if ldr.SymValue(tramp) == 0 {
+ // either the trampoline does not exist -- we need to create one,
+ // or found one the address which is not assigned -- this will be
+ // laid down immediately after the current function. use this one.
+ break
+ }
+
+ t = (ldr.SymValue(tramp) - 8 - (ldr.SymValue(s) + int64(r.Off()))) / 4
+ if t >= -0x800000 && t < 0x7fffff {
+ // found an existing trampoline that is not too far
+ // we can just use it
+ break
+ }
+ }
+ if ldr.SymType(tramp) == 0 {
+ // trampoline does not exist, create one
+ trampb := ldr.MakeSymbolUpdater(tramp)
+ ctxt.AddTramp(trampb)
+ if ctxt.DynlinkingGo() || ldr.SymType(rs) == sym.SDYNIMPORT {
+ if immrot(uint32(offset)) == 0 {
+ ctxt.Errorf(s, "odd offset in dynlink direct call: %v+%d", ldr.SymName(rs), offset)
+ }
+ gentrampdyn(ctxt.Arch, trampb, rs, int64(offset))
+ } else if ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE {
+ gentramppic(ctxt.Arch, trampb, rs, int64(offset))
+ } else {
+ gentramp(ctxt.Arch, ctxt.LinkMode, ldr, trampb, rs, int64(offset))
+ }
+ }
+ // modify reloc to point to tramp, which will be resolved later
+ sb := ldr.MakeSymbolUpdater(s)
+ relocs := sb.Relocs()
+ r := relocs.At(ri)
+ r.SetSym(tramp)
+ r.SetAdd(r.Add()&0xff000000 | 0xfffffe) // clear the offset embedded in the instruction
+ }
+ default:
+ ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
+ }
+}
+
+// generate a trampoline to target+offset.
+func gentramp(arch *sys.Arch, linkmode ld.LinkMode, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.SetSize(12) // 3 instructions
+ P := make([]byte, tramp.Size())
+ t := ldr.SymValue(target) + offset
+ o1 := uint32(0xe5900000 | 12<<12 | 15<<16) // MOVW (R15), R12 // R15 is actual pc + 8
+ o2 := uint32(0xe12fff10 | 12) // JMP (R12)
+ o3 := uint32(t) // WORD $target
+ arch.ByteOrder.PutUint32(P, o1)
+ arch.ByteOrder.PutUint32(P[4:], o2)
+ arch.ByteOrder.PutUint32(P[8:], o3)
+ tramp.SetData(P)
+
+ if linkmode == ld.LinkExternal || ldr.SymValue(target) == 0 {
+ r, _ := tramp.AddRel(objabi.R_ADDR)
+ r.SetOff(8)
+ r.SetSiz(4)
+ r.SetSym(target)
+ r.SetAdd(offset)
+ }
+}
+
+// generate a trampoline to target+offset in position independent code.
+func gentramppic(arch *sys.Arch, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.SetSize(16) // 4 instructions
+ P := make([]byte, tramp.Size())
+ o1 := uint32(0xe5900000 | 12<<12 | 15<<16 | 4) // MOVW 4(R15), R12 // R15 is actual pc + 8
+ o2 := uint32(0xe0800000 | 12<<12 | 15<<16 | 12) // ADD R15, R12, R12
+ o3 := uint32(0xe12fff10 | 12) // JMP (R12)
+ o4 := uint32(0) // WORD $(target-pc) // filled in with relocation
+ arch.ByteOrder.PutUint32(P, o1)
+ arch.ByteOrder.PutUint32(P[4:], o2)
+ arch.ByteOrder.PutUint32(P[8:], o3)
+ arch.ByteOrder.PutUint32(P[12:], o4)
+ tramp.SetData(P)
+
+ r, _ := tramp.AddRel(objabi.R_PCREL)
+ r.SetOff(12)
+ r.SetSiz(4)
+ r.SetSym(target)
+ r.SetAdd(offset + 4)
+}
+
+// generate a trampoline to target+offset in dynlink mode (using GOT).
+func gentrampdyn(arch *sys.Arch, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.SetSize(20) // 5 instructions
+ o1 := uint32(0xe5900000 | 12<<12 | 15<<16 | 8) // MOVW 8(R15), R12 // R15 is actual pc + 8
+ o2 := uint32(0xe0800000 | 12<<12 | 15<<16 | 12) // ADD R15, R12, R12
+ o3 := uint32(0xe5900000 | 12<<12 | 12<<16) // MOVW (R12), R12
+ o4 := uint32(0xe12fff10 | 12) // JMP (R12)
+ o5 := uint32(0) // WORD $target@GOT // filled in with relocation
+ o6 := uint32(0)
+ if offset != 0 {
+ // insert an instruction to add offset
+ tramp.SetSize(24) // 6 instructions
+ o6 = o5
+ o5 = o4
+ o4 = 0xe2800000 | 12<<12 | 12<<16 | immrot(uint32(offset)) // ADD $offset, R12, R12
+ o1 = uint32(0xe5900000 | 12<<12 | 15<<16 | 12) // MOVW 12(R15), R12
+ }
+ P := make([]byte, tramp.Size())
+ arch.ByteOrder.PutUint32(P, o1)
+ arch.ByteOrder.PutUint32(P[4:], o2)
+ arch.ByteOrder.PutUint32(P[8:], o3)
+ arch.ByteOrder.PutUint32(P[12:], o4)
+ arch.ByteOrder.PutUint32(P[16:], o5)
+ if offset != 0 {
+ arch.ByteOrder.PutUint32(P[20:], o6)
+ }
+ tramp.SetData(P)
+
+ r, _ := tramp.AddRel(objabi.R_GOTPCREL)
+ r.SetOff(16)
+ r.SetSiz(4)
+ r.SetSym(target)
+ r.SetAdd(8)
+ if offset != 0 {
+ // increase reloc offset by 4 as we inserted an ADD instruction
+ r.SetOff(20)
+ r.SetAdd(12)
+ }
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ rs := r.Sym()
+ if target.IsExternal() {
+ switch r.Type() {
+ case objabi.R_CALLARM:
+ // set up addend for eventual relocation via outer symbol.
+ _, off := ld.FoldSubSymbolOffset(ldr, rs)
+ xadd := int64(signext24(r.Add()&0xffffff))*4 + off
+ if xadd/4 > 0x7fffff || xadd/4 < -0x800000 {
+ ldr.Errorf(s, "direct call too far %d", xadd/4)
+ }
+ return int64(braddoff(int32(0xff000000&uint32(r.Add())), int32(0xffffff&uint32(xadd/4)))), 1, true
+ }
+ return -1, 0, false
+ }
+
+ const isOk = true
+ const noExtReloc = 0
+ switch r.Type() {
+ // The following three arch specific relocations are only for generation of
+ // Linux/ARM ELF's PLT entry (3 assembler instruction)
+ case objabi.R_PLT0: // add ip, pc, #0xXX00000
+ if ldr.SymValue(syms.GOTPLT) < ldr.SymValue(syms.PLT) {
+ ldr.Errorf(s, ".got.plt should be placed after .plt section.")
+ }
+ return 0xe28fc600 + (0xff & (int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add())) >> 20)), noExtReloc, isOk
+ case objabi.R_PLT1: // add ip, ip, #0xYY000
+ return 0xe28cca00 + (0xff & (int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add()+4)) >> 12)), noExtReloc, isOk
+ case objabi.R_PLT2: // ldr pc, [ip, #0xZZZ]!
+ return 0xe5bcf000 + (0xfff & int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add()+8))), noExtReloc, isOk
+ case objabi.R_CALLARM: // bl XXXXXX or b YYYYYY
+ // r.Add is the instruction
+ // low 24-bit encodes the target address
+ t := (ldr.SymValue(rs) + int64(signext24(r.Add()&0xffffff)*4) - (ldr.SymValue(s) + int64(r.Off()))) / 4
+ if t > 0x7fffff || t < -0x800000 {
+ ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t)
+ }
+ return int64(braddoff(int32(0xff000000&uint32(r.Add())), int32(0xffffff&t))), noExtReloc, isOk
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
+ log.Fatalf("unexpected relocation variant")
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ rs := r.Sym()
+ var rr loader.ExtReloc
+ switch r.Type() {
+ case objabi.R_CALLARM:
+ // set up addend for eventual relocation via outer symbol.
+ rs, off := ld.FoldSubSymbolOffset(ldr, rs)
+ rr.Xadd = int64(signext24(r.Add()&0xffffff))*4 + off
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil {
+ ldr.Errorf(s, "missing section for %s", ldr.SymName(rs))
+ }
+ rr.Xsym = rs
+ rr.Type = r.Type()
+ rr.Size = r.Siz()
+ return rr, true
+ }
+ return rr, false
+}
+
+func addpltreloc(ldr *loader.Loader, plt *loader.SymbolBuilder, got *loader.SymbolBuilder, s loader.Sym, typ objabi.RelocType) {
+ r, _ := plt.AddRel(typ)
+ r.SetSym(got.Sym())
+ r.SetOff(int32(plt.Size()))
+ r.SetSiz(4)
+ r.SetAdd(int64(ldr.SymGot(s)) - 8)
+
+ plt.SetReachable(true)
+ plt.SetSize(plt.Size() + 4)
+ plt.Grow(plt.Size())
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ got := ldr.MakeSymbolUpdater(syms.GOTPLT)
+ rel := ldr.MakeSymbolUpdater(syms.RelPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // .got entry
+ ldr.SetGot(s, int32(got.Size()))
+
+ // In theory, all GOT should point to the first PLT entry,
+ // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's
+ // dynamic linker won't, so we'd better do it ourselves.
+ got.AddAddrPlus(target.Arch, plt.Sym(), 0)
+
+ // .plt entry, this depends on the .got entry
+ ldr.SetPlt(s, int32(plt.Size()))
+
+ addpltreloc(ldr, plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000
+ addpltreloc(ldr, plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000
+ addpltreloc(ldr, plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]!
+
+ // rel
+ rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
+
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), uint32(elf.R_ARM_JUMP_SLOT)))
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
+
+func addgotsyminternal(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymGot(s) >= 0 {
+ return
+ }
+
+ got := ldr.MakeSymbolUpdater(syms.GOT)
+ ldr.SetGot(s, int32(got.Size()))
+ got.AddAddrPlus(target.Arch, s, 0)
+
+ if target.IsElf() {
+ } else {
+ ldr.Errorf(s, "addgotsyminternal: unsupported binary format")
+ }
+}
diff --git a/src/cmd/link/internal/arm/l.go b/src/cmd/link/internal/arm/l.go
new file mode 100644
index 0000000..d16dc27
--- /dev/null
+++ b/src/cmd/link/internal/arm/l.go
@@ -0,0 +1,75 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package arm
+
+// Writing object files.
+
+// Inferno utils/5l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/l.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+const (
+ maxAlign = 8 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 4 // single-instruction alignment
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 13
+ dwarfRegLR = 14
+)
diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go
new file mode 100644
index 0000000..6da0c77
--- /dev/null
+++ b/src/cmd/link/internal/arm/obj.go
@@ -0,0 +1,111 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package arm
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchARM
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ TrampLimit: 0x1c00000, // 24-bit signed offset * 4, leave room for PLT etc.
+
+ Plan9Magic: 0x647,
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Trampoline: trampoline,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 8,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+ PEreloc1: pereloc1,
+
+ Linuxdynld: "/lib/ld-linux.so.3", // 2 for OABI, 3 for EABI
+ LinuxdynldMusl: "/lib/ld-musl-arm.so.1",
+ Freebsddynld: "/usr/libexec/ld-elf.so.1",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "/libexec/ld.elf_so",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4128
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hlinux, /* arm elf */
+ objabi.Hfreebsd,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd:
+ *ld.FlagD = false
+ // with dynamic linking
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+
+ case objabi.Hwindows: /* PE executable */
+ // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+ return
+ }
+}
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
new file mode 100644
index 0000000..7109d84
--- /dev/null
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -0,0 +1,1389 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package arm64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "log"
+)
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op uint32) {
+ initfunc.AddUint32(ctxt.Arch, op)
+ }
+ // 0000000000000000 <local.dso_init>:
+ // 0: 90000000 adrp x0, 0 <runtime.firstmoduledata>
+ // 0: R_AARCH64_ADR_PREL_PG_HI21 local.moduledata
+ // 4: 91000000 add x0, x0, #0x0
+ // 4: R_AARCH64_ADD_ABS_LO12_NC local.moduledata
+ o(0x90000000)
+ o(0x91000000)
+ rel, _ := initfunc.AddRel(objabi.R_ADDRARM64)
+ rel.SetOff(0)
+ rel.SetSiz(8)
+ rel.SetSym(ctxt.Moduledata)
+
+ // 8: 14000000 b 0 <runtime.addmoduledata>
+ // 8: R_AARCH64_CALL26 runtime.addmoduledata
+ o(0x14000000)
+ rel2, _ := initfunc.AddRel(objabi.R_CALLARM64)
+ rel2.SetOff(8)
+ rel2.SetSiz(4)
+ rel2.SetSym(addmoduledata)
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ const pcrel = 1
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_AARCH64_PREL32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_AARCH64_PREL64 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+8)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26):
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s in callarm64", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM64)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_GOT_PAGE),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LD64_GOT_LO12_NC):
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ // TODO: turn LDR of GOT entry into ADR of symbol itself
+ }
+
+ // fall back to using GOT
+ // TODO: just needs relocation, no need to put in .dynsym
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_AARCH64_GLOB_DAT))
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_GOT)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_PREL_PG_HI21),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADD_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_PCREL)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ABS64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_AARCH64_ABS64 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if target.IsPIE() && target.IsInternal() {
+ // For internal linking PIE, this R_ADDR relocation cannot
+ // be resolved statically. We need to generate a dynamic
+ // relocation. Let the code below handle it.
+ break
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST8_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST8)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST16_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST16)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST32_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST32)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST64_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST64)
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST128_ABS_LO12_NC):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_LDST128)
+ return true
+
+ // Handle relocations found in Mach-O object files.
+ case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_UNSIGNED*2:
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if target.IsPIE() && target.IsInternal() {
+ // For internal linking PIE, this R_ADDR relocation cannot
+ // be resolved statically. We need to generate a dynamic
+ // relocation. Let the code below handle it.
+ break
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLARM64)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_PAGE21*2 + pcrel,
+ objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_PAGEOFF12*2:
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_PCREL)
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21*2 + pcrel,
+ objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12*2:
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ // turn MOVD sym@GOT (adrp+ldr) into MOVD $sym (adrp+add)
+ data := ldr.Data(s)
+ off := r.Off()
+ if int(off+3) >= len(data) {
+ ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+ o := target.Arch.ByteOrder.Uint32(data[off:])
+ su := ldr.MakeSymbolUpdater(s)
+ switch {
+ case (o>>24)&0x9f == 0x90: // adrp
+ // keep instruction unchanged, change relocation type below
+ case o>>24 == 0xf9: // ldr
+ // rewrite to add
+ o = (0x91 << 24) | (o & (1<<22 - 1))
+ su.MakeWritable()
+ su.SetUint32(target.Arch, int64(off), o)
+ default:
+ ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+ su.SetRelocType(rIdx, objabi.R_ARM64_PCREL)
+ return true
+ }
+ ld.AddGotSym(target, ldr, syms, targ, 0)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ARM64_GOT)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymGot(targ)))
+ return true
+ }
+
+ // Reread the reloc to incorporate any changes in type above.
+ relocs := ldr.Relocs(s)
+ r = relocs.At(rIdx)
+
+ switch r.Type() {
+ case objabi.R_CALL,
+ objabi.R_PCREL,
+ objabi.R_CALLARM64:
+ if targType != sym.SDYNIMPORT {
+ // nothing to do, the relocation will be laid out in reloc
+ return true
+ }
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ // Internal linking.
+ if r.Add() != 0 {
+ ldr.Errorf(s, "PLT call with non-zero addend (%v)", r.Add())
+ }
+ // Build a PLT entry and change the relocation target to that entry.
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ return true
+
+ case objabi.R_ADDR:
+ if ldr.SymType(s) == sym.STEXT && target.IsElf() {
+ // The code is asking for the address of an external
+ // function. We provide it with the address of the
+ // correspondent GOT symbol.
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_AARCH64_GLOB_DAT))
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+ }
+
+ // Process dynamic relocations for the data sections.
+ if target.IsPIE() && target.IsInternal() {
+ // When internally linking, generate dynamic relocations
+ // for all typical R_ADDR relocations. The exception
+ // are those R_ADDR that are created as part of generating
+ // the dynamic relocations and must be resolved statically.
+ //
+ // There are three phases relevant to understanding this:
+ //
+ // dodata() // we are here
+ // address() // symbol address assignment
+ // reloc() // resolution of static R_ADDR relocs
+ //
+ // At this point symbol addresses have not been
+ // assigned yet (as the final size of the .rela section
+ // will affect the addresses), and so we cannot write
+ // the Elf64_Rela.r_offset now. Instead we delay it
+ // until after the 'address' phase of the linker is
+ // complete. We do this via Addaddrplus, which creates
+ // a new R_ADDR relocation which will be resolved in
+ // the 'reloc' phase.
+ //
+ // These synthetic static R_ADDR relocs must be skipped
+ // now, or else we will be caught in an infinite loop
+ // of generating synthetic relocs for our synthetic
+ // relocs.
+ //
+ // Furthermore, the rela sections contain dynamic
+ // relocations with R_ADDR relocations on
+ // Elf64_Rela.r_offset. This field should contain the
+ // symbol offset as determined by reloc(), not the
+ // final dynamically linked address as a dynamic
+ // relocation would provide.
+ switch ldr.SymName(s) {
+ case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic":
+ return false
+ }
+ } else {
+ // Either internally linking a static executable,
+ // in which case we can resolve these relocations
+ // statically in the 'reloc' phase, or externally
+ // linking, in which case the relocation will be
+ // prepared in the 'reloc' phase and passed to the
+ // external linker in the 'asmb' phase.
+ if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA {
+ break
+ }
+ }
+
+ if target.IsElf() {
+ // Generate R_AARCH64_RELATIVE relocations for best
+ // efficiency in the dynamic linker.
+ //
+ // As noted above, symbol addresses have not been
+ // assigned yet, so we can't generate the final reloc
+ // entry yet. We ultimately want:
+ //
+ // r_offset = s + r.Off
+ // r_info = R_AARCH64_RELATIVE
+ // r_addend = targ + r.Add
+ //
+ // The dynamic linker will set *offset = base address +
+ // addend.
+ //
+ // AddAddrPlus is used for r_offset and r_addend to
+ // generate new R_ADDR relocations that will update
+ // these fields in the 'reloc' phase.
+ rela := ldr.MakeSymbolUpdater(syms.Rela)
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ if r.Siz() == 8 {
+ rela.AddUint64(target.Arch, elf.R_INFO(0, uint32(elf.R_AARCH64_RELATIVE)))
+ } else {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ rela.AddAddrPlus(target.Arch, targ, int64(r.Add()))
+ // Not mark r done here. So we still apply it statically,
+ // so in the file content we'll also have the right offset
+ // to the relocation target. So it can be examined statically
+ // (e.g. go version).
+ return true
+ }
+
+ if target.IsDarwin() {
+ // Mach-O relocations are a royal pain to lay out.
+ // They use a compact stateful bytecode representation.
+ // Here we record what are needed and encode them later.
+ ld.MachoAddRebase(s, int64(r.Off()))
+ // Not mark r done here. So we still apply it statically,
+ // so in the file content we'll also have the right offset
+ // to the relocation target. So it can be examined statically
+ // (e.g. go version).
+ return true
+ }
+
+ case objabi.R_ARM64_GOTPCREL:
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ if targType != sym.SDYNIMPORT {
+ ldr.Errorf(s, "R_ARM64_GOTPCREL target is not SDYNIMPORT symbol: %v", ldr.SymName(targ))
+ }
+ if r.Add() != 0 {
+ ldr.Errorf(s, "R_ARM64_GOTPCREL with non-zero addend (%v)", r.Add())
+ }
+ if target.IsElf() {
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_AARCH64_GLOB_DAT))
+ } else {
+ ld.AddGotSym(target, ldr, syms, targ, 0)
+ }
+ // turn into two relocations, one for each instruction.
+ su := ldr.MakeSymbolUpdater(s)
+ r.SetType(objabi.R_ARM64_GOT)
+ r.SetSiz(4)
+ r.SetSym(syms.GOT)
+ r.SetAdd(int64(ldr.SymGot(targ)))
+ r2, _ := su.AddRel(objabi.R_ARM64_GOT)
+ r2.SetSiz(4)
+ r2.SetOff(r.Off() + 4)
+ r2.SetSym(syms.GOT)
+ r2.SetAdd(int64(ldr.SymGot(targ)))
+ return true
+ }
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ switch siz {
+ case 4:
+ out.Write64(uint64(elf.R_AARCH64_ABS32) | uint64(elfsym)<<32)
+ case 8:
+ out.Write64(uint64(elf.R_AARCH64_ABS64) | uint64(elfsym)<<32)
+ default:
+ return false
+ }
+ case objabi.R_ADDRARM64:
+ // two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_ADD_ABS_LO12_NC
+ out.Write64(uint64(elf.R_AARCH64_ADR_PREL_PG_HI21) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_AARCH64_ADD_ABS_LO12_NC) | uint64(elfsym)<<32)
+
+ case objabi.R_ARM64_PCREL_LDST8,
+ objabi.R_ARM64_PCREL_LDST16,
+ objabi.R_ARM64_PCREL_LDST32,
+ objabi.R_ARM64_PCREL_LDST64:
+ // two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_LDST{64/32/16/8}_ABS_LO12_NC
+ out.Write64(uint64(elf.R_AARCH64_ADR_PREL_PG_HI21) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ var ldstType elf.R_AARCH64
+ switch r.Type {
+ case objabi.R_ARM64_PCREL_LDST8:
+ ldstType = elf.R_AARCH64_LDST8_ABS_LO12_NC
+ case objabi.R_ARM64_PCREL_LDST16:
+ ldstType = elf.R_AARCH64_LDST16_ABS_LO12_NC
+ case objabi.R_ARM64_PCREL_LDST32:
+ ldstType = elf.R_AARCH64_LDST32_ABS_LO12_NC
+ case objabi.R_ARM64_PCREL_LDST64:
+ ldstType = elf.R_AARCH64_LDST64_ABS_LO12_NC
+ }
+ out.Write64(uint64(ldstType) | uint64(elfsym)<<32)
+
+ case objabi.R_ARM64_TLS_LE:
+ out.Write64(uint64(elf.R_AARCH64_TLSLE_MOVW_TPREL_G0) | uint64(elfsym)<<32)
+ case objabi.R_ARM64_TLS_IE:
+ out.Write64(uint64(elf.R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) | uint64(elfsym)<<32)
+ case objabi.R_ARM64_GOTPCREL:
+ out.Write64(uint64(elf.R_AARCH64_ADR_GOT_PAGE) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_AARCH64_LD64_GOT_LO12_NC) | uint64(elfsym)<<32)
+ case objabi.R_CALLARM64:
+ if siz != 4 {
+ return false
+ }
+ out.Write64(uint64(elf.R_AARCH64_CALL26) | uint64(elfsym)<<32)
+
+ }
+ out.Write64(uint64(r.Xadd))
+
+ return true
+}
+
+// sign-extends from 21, 24-bit.
+func signext21(x int64) int64 { return x << (64 - 21) >> (64 - 21) }
+func signext24(x int64) int64 { return x << (64 - 24) >> (64 - 24) }
+
+func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ var v uint32
+
+ rs := r.Xsym
+ rt := r.Type
+ siz := r.Size
+ xadd := r.Xadd
+
+ if xadd != signext24(xadd) {
+ // If the relocation target would overflow the addend, then target
+ // a linker-manufactured label symbol with a smaller addend instead.
+ label := ldr.Lookup(offsetLabelName(ldr, rs, xadd/machoRelocLimit*machoRelocLimit), ldr.SymVersion(rs))
+ if label != 0 {
+ xadd = ldr.SymValue(rs) + xadd - ldr.SymValue(label)
+ rs = label
+ }
+ if xadd != signext24(xadd) {
+ ldr.Errorf(s, "internal error: relocation addend overflow: %s+0x%x", ldr.SymName(rs), xadd)
+ }
+ }
+ if rt == objabi.R_CALLARM64 && xadd != 0 {
+ label := ldr.Lookup(offsetLabelName(ldr, rs, xadd), ldr.SymVersion(rs))
+ if label != 0 {
+ xadd = ldr.SymValue(rs) + xadd - ldr.SymValue(label) // should always be 0 (checked below)
+ rs = label
+ }
+ }
+
+ if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 ||
+ rt == objabi.R_ARM64_PCREL_LDST8 || rt == objabi.R_ARM64_PCREL_LDST16 ||
+ rt == objabi.R_ARM64_PCREL_LDST32 || rt == objabi.R_ARM64_PCREL_LDST64 ||
+ rt == objabi.R_ADDRARM64 || rt == objabi.R_ARM64_GOTPCREL {
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ v = uint32(ldr.SymDynid(rs))
+ v |= 1 << 27 // external relocation
+ } else {
+ v = uint32(ldr.SymSect(rs).Extnum)
+ if v == 0 {
+ ldr.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymSect(rs).Name, ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+ }
+
+ switch rt {
+ default:
+ return false
+ case objabi.R_ADDR:
+ v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28
+ case objabi.R_CALLARM64:
+ if xadd != 0 {
+ // Addend should be handled above via label symbols.
+ ldr.Errorf(s, "unexpected non-zero addend: %s+%d", ldr.SymName(rs), xadd)
+ }
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_ARM64_RELOC_BRANCH26 << 28
+ case objabi.R_ADDRARM64,
+ objabi.R_ARM64_PCREL_LDST8,
+ objabi.R_ARM64_PCREL_LDST16,
+ objabi.R_ARM64_PCREL_LDST32,
+ objabi.R_ARM64_PCREL_LDST64:
+ siz = 4
+ // Two relocation entries: MACHO_ARM64_RELOC_PAGEOFF12 MACHO_ARM64_RELOC_PAGE21
+ // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff + 4))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
+ }
+ out.Write32(uint32(sectoff + 4))
+ out.Write32(v | (ld.MACHO_ARM64_RELOC_PAGEOFF12 << 28) | (2 << 25))
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
+ }
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28
+ case objabi.R_ARM64_GOTPCREL:
+ siz = 4
+ // Two relocation entries: MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 MACHO_ARM64_RELOC_GOT_LOAD_PAGE21
+ // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff + 4))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
+ }
+ out.Write32(uint32(sectoff + 4))
+ out.Write32(v | (ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28) | (2 << 25))
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff))
+ }
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28
+ }
+
+ switch siz {
+ default:
+ return false
+ case 1:
+ v |= 0 << 25
+ case 2:
+ v |= 1 << 25
+ case 4:
+ v |= 2 << 25
+ case 8:
+ v |= 3 << 25
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(v)
+ return true
+}
+
+func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ rs := r.Xsym
+ rt := r.Type
+
+ if (rt == objabi.R_ADDRARM64 || rt == objabi.R_ARM64_PCREL_LDST8 || rt == objabi.R_ARM64_PCREL_LDST16 ||
+ rt == objabi.R_ARM64_PCREL_LDST32 || rt == objabi.R_ARM64_PCREL_LDST64) && r.Xadd != signext21(r.Xadd) {
+ // If the relocation target would overflow the addend, then target
+ // a linker-manufactured label symbol with a smaller addend instead.
+ label := ldr.Lookup(offsetLabelName(ldr, rs, r.Xadd/peRelocLimit*peRelocLimit), ldr.SymVersion(rs))
+ if label == 0 {
+ ldr.Errorf(s, "invalid relocation: %v %s+0x%x", rt, ldr.SymName(rs), r.Xadd)
+ return false
+ }
+ rs = label
+ }
+ if rt == objabi.R_CALLARM64 && r.Xadd != 0 {
+ label := ldr.Lookup(offsetLabelName(ldr, rs, r.Xadd), ldr.SymVersion(rs))
+ if label == 0 {
+ ldr.Errorf(s, "invalid relocation: %v %s+0x%x", rt, ldr.SymName(rs), r.Xadd)
+ return false
+ }
+ rs = label
+ }
+ symdynid := ldr.SymDynid(rs)
+ if symdynid < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ switch rt {
+ default:
+ return false
+
+ case objabi.R_DWARFSECREF:
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(symdynid))
+ out.Write16(ld.IMAGE_REL_ARM64_SECREL)
+
+ case objabi.R_ADDR:
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(symdynid))
+ if r.Size == 8 {
+ out.Write16(ld.IMAGE_REL_ARM64_ADDR64)
+ } else {
+ out.Write16(ld.IMAGE_REL_ARM64_ADDR32)
+ }
+
+ case objabi.R_ADDRARM64:
+ // Note: r.Xadd has been taken care of below, in archreloc.
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(symdynid))
+ out.Write16(ld.IMAGE_REL_ARM64_PAGEBASE_REL21)
+
+ out.Write32(uint32(sectoff + 4))
+ out.Write32(uint32(symdynid))
+ out.Write16(ld.IMAGE_REL_ARM64_PAGEOFFSET_12A)
+
+ case objabi.R_ARM64_PCREL_LDST8,
+ objabi.R_ARM64_PCREL_LDST16,
+ objabi.R_ARM64_PCREL_LDST32,
+ objabi.R_ARM64_PCREL_LDST64:
+ // Note: r.Xadd has been taken care of below, in archreloc.
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(symdynid))
+ out.Write16(ld.IMAGE_REL_ARM64_PAGEBASE_REL21)
+
+ out.Write32(uint32(sectoff + 4))
+ out.Write32(uint32(symdynid))
+ out.Write16(ld.IMAGE_REL_ARM64_PAGEOFFSET_12L)
+
+ case objabi.R_CALLARM64:
+ // Note: r.Xadd has been taken care of above, by using a label pointing into the middle of the function.
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(symdynid))
+ out.Write16(ld.IMAGE_REL_ARM64_BRANCH26)
+ }
+
+ return true
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (int64, int, bool) {
+ const noExtReloc = 0
+ const isOk = true
+
+ rs := r.Sym()
+
+ if target.IsExternal() {
+ nExtReloc := 0
+ switch rt := r.Type(); rt {
+ default:
+ case objabi.R_ARM64_GOTPCREL,
+ objabi.R_ARM64_PCREL_LDST8,
+ objabi.R_ARM64_PCREL_LDST16,
+ objabi.R_ARM64_PCREL_LDST32,
+ objabi.R_ARM64_PCREL_LDST64,
+ objabi.R_ADDRARM64:
+
+ // set up addend for eventual relocation via outer symbol.
+ rs, off := ld.FoldSubSymbolOffset(ldr, rs)
+ xadd := r.Add() + off
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil {
+ ldr.Errorf(s, "missing section for %s", ldr.SymName(rs))
+ }
+
+ nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1
+ if target.IsDarwin() && xadd != 0 {
+ nExtReloc = 4 // need another two relocations for non-zero addend
+ }
+
+ if target.IsWindows() {
+ var o0, o1 uint32
+ if target.IsBigEndian() {
+ o0 = uint32(val >> 32)
+ o1 = uint32(val)
+ } else {
+ o0 = uint32(val)
+ o1 = uint32(val >> 32)
+ }
+
+ // The first instruction (ADRP) has a 21-bit immediate field,
+ // and the second (ADD or LD/ST) has a 12-bit immediate field.
+ // The first instruction is only for high bits, but to get the carry bits right we have
+ // to put the full addend, including the bottom 12 bits again.
+ // That limits the distance of any addend to only 21 bits.
+ // But we assume that ADRP's top bit will be interpreted as a sign bit,
+ // so we only use 20 bits.
+ // pereloc takes care of introducing new symbol labels
+ // every megabyte for longer relocations.
+ xadd := uint32(xadd)
+ o0 |= (xadd&3)<<29 | (xadd&0xffffc)<<3
+ switch rt {
+ case objabi.R_ARM64_PCREL_LDST8, objabi.R_ADDRARM64:
+ o1 |= (xadd & 0xfff) << 10
+ case objabi.R_ARM64_PCREL_LDST16:
+ if xadd&0x1 != 0 {
+ ldr.Errorf(s, "offset for 16-bit load/store has unaligned value %d", xadd&0xfff)
+ }
+ o1 |= ((xadd & 0xfff) >> 1) << 10
+ case objabi.R_ARM64_PCREL_LDST32:
+ if xadd&0x3 != 0 {
+ ldr.Errorf(s, "offset for 32-bit load/store has unaligned value %d", xadd&0xfff)
+ }
+ o1 |= ((xadd & 0xfff) >> 2) << 10
+ case objabi.R_ARM64_PCREL_LDST64:
+ if xadd&0x7 != 0 {
+ ldr.Errorf(s, "offset for 64-bit load/store has unaligned value %d", xadd&0xfff)
+ }
+ o1 |= ((xadd & 0xfff) >> 3) << 10
+ }
+
+ if target.IsBigEndian() {
+ val = int64(o0)<<32 | int64(o1)
+ } else {
+ val = int64(o1)<<32 | int64(o0)
+ }
+ }
+
+ return val, nExtReloc, isOk
+
+ case objabi.R_CALLARM64:
+ nExtReloc = 1
+ return val, nExtReloc, isOk
+
+ case objabi.R_ARM64_TLS_LE:
+ nExtReloc = 1
+ return val, nExtReloc, isOk
+
+ case objabi.R_ARM64_TLS_IE:
+ nExtReloc = 2 // need two ELF relocations. see elfreloc1
+ return val, nExtReloc, isOk
+
+ case objabi.R_ADDR:
+ if target.IsWindows() && r.Add() != 0 {
+ if r.Siz() == 8 {
+ val = r.Add()
+ } else if target.IsBigEndian() {
+ val = int64(uint32(val)) | int64(r.Add())<<32
+ } else {
+ val = val>>32<<32 | int64(uint32(r.Add()))
+ }
+ return val, 1, true
+ }
+ }
+ }
+
+ switch rt := r.Type(); rt {
+ case objabi.R_ADDRARM64,
+ objabi.R_ARM64_PCREL_LDST8,
+ objabi.R_ARM64_PCREL_LDST16,
+ objabi.R_ARM64_PCREL_LDST32,
+ objabi.R_ARM64_PCREL_LDST64:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t >= 1<<32 || t < -1<<32 {
+ ldr.Errorf(s, "program too large, address relocation distance = %d", t)
+ }
+
+ var o0, o1 uint32
+
+ if target.IsBigEndian() {
+ o0 = uint32(val >> 32)
+ o1 = uint32(val)
+ } else {
+ o0 = uint32(val)
+ o1 = uint32(val >> 32)
+ }
+
+ o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5)
+ switch rt {
+ case objabi.R_ARM64_PCREL_LDST8, objabi.R_ADDRARM64:
+ o1 |= uint32(t&0xfff) << 10
+ case objabi.R_ARM64_PCREL_LDST16:
+ if t&0x1 != 0 {
+ ldr.Errorf(s, "offset for 16-bit load/store has unaligned value %d", t&0xfff)
+ }
+ o1 |= (uint32(t&0xfff) >> 1) << 10
+ case objabi.R_ARM64_PCREL_LDST32:
+ if t&0x3 != 0 {
+ ldr.Errorf(s, "offset for 32-bit load/store has unaligned value %d", t&0xfff)
+ }
+ o1 |= (uint32(t&0xfff) >> 2) << 10
+ case objabi.R_ARM64_PCREL_LDST64:
+ if t&0x7 != 0 {
+ ldr.Errorf(s, "offset for 64-bit load/store has unaligned value %d", t&0xfff)
+ }
+ o1 |= (uint32(t&0xfff) >> 3) << 10
+ }
+
+ // when laid out, the instruction order must always be o1, o2.
+ if target.IsBigEndian() {
+ return int64(o0)<<32 | int64(o1), noExtReloc, true
+ }
+ return int64(o1)<<32 | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_TLS_LE:
+ if target.IsDarwin() {
+ ldr.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType)
+ }
+ // The TCB is two pointers. This is not documented anywhere, but is
+ // de facto part of the ABI.
+ v := ldr.SymValue(rs) + int64(2*target.Arch.PtrSize)
+ if v < 0 || v >= 32678 {
+ ldr.Errorf(s, "TLS offset out of range %d", v)
+ }
+ return val | (v << 5), noExtReloc, true
+
+ case objabi.R_ARM64_TLS_IE:
+ if target.IsPIE() && target.IsElf() {
+ // We are linking the final executable, so we
+ // can optimize any TLS IE relocation to LE.
+
+ if !target.IsLinux() {
+ ldr.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType)
+ }
+
+ // The TCB is two pointers. This is not documented anywhere, but is
+ // de facto part of the ABI.
+ v := ldr.SymAddr(rs) + int64(2*target.Arch.PtrSize) + r.Add()
+ if v < 0 || v >= 32678 {
+ ldr.Errorf(s, "TLS offset out of range %d", v)
+ }
+
+ var o0, o1 uint32
+ if target.IsBigEndian() {
+ o0 = uint32(val >> 32)
+ o1 = uint32(val)
+ } else {
+ o0 = uint32(val)
+ o1 = uint32(val >> 32)
+ }
+
+ // R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21
+ // turn ADRP to MOVZ
+ o0 = 0xd2a00000 | uint32(o0&0x1f) | (uint32((v>>16)&0xffff) << 5)
+ // R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC
+ // turn LD64 to MOVK
+ if v&3 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC", v)
+ }
+ o1 = 0xf2800000 | uint32(o1&0x1f) | (uint32(v&0xffff) << 5)
+
+ // when laid out, the instruction order must always be o0, o1.
+ if target.IsBigEndian() {
+ return int64(o0)<<32 | int64(o1), noExtReloc, isOk
+ }
+ return int64(o1)<<32 | int64(o0), noExtReloc, isOk
+ } else {
+ log.Fatalf("cannot handle R_ARM64_TLS_IE (sym %s) when linking internally", ldr.SymName(s))
+ }
+
+ case objabi.R_CALLARM64:
+ var t int64
+ if ldr.SymType(rs) == sym.SDYNIMPORT {
+ t = (ldr.SymAddr(syms.PLT) + r.Add()) - (ldr.SymValue(s) + int64(r.Off()))
+ } else {
+ t = (ldr.SymAddr(rs) + r.Add()) - (ldr.SymValue(s) + int64(r.Off()))
+ }
+ if t >= 1<<27 || t < -1<<27 {
+ ldr.Errorf(s, "program too large, call relocation distance = %d", t)
+ }
+ return val | ((t >> 2) & 0x03ffffff), noExtReloc, true
+
+ case objabi.R_ARM64_GOT:
+ if (val>>24)&0x9f == 0x90 {
+ // R_AARCH64_ADR_GOT_PAGE
+ // patch instruction: adrp
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t >= 1<<32 || t < -1<<32 {
+ ldr.Errorf(s, "program too large, address relocation distance = %d", t)
+ }
+ var o0 uint32
+ o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5)
+ return val | int64(o0), noExtReloc, isOk
+ } else if val>>24 == 0xf9 {
+ // R_AARCH64_LD64_GOT_LO12_NC
+ // patch instruction: ldr
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&7 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LD64_GOT_LO12_NC", t)
+ }
+ var o1 uint32
+ o1 |= uint32(t&0xfff) << (10 - 3)
+ return val | int64(uint64(o1)), noExtReloc, isOk
+ } else {
+ ldr.Errorf(s, "unsupported instruction for %x R_GOTARM64", val)
+ }
+
+ case objabi.R_ARM64_PCREL:
+ if (val>>24)&0x9f == 0x90 {
+ // R_AARCH64_ADR_PREL_PG_HI21
+ // patch instruction: adrp
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t >= 1<<32 || t < -1<<32 {
+ ldr.Errorf(s, "program too large, address relocation distance = %d", t)
+ }
+ o0 := (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5)
+ return val | int64(o0), noExtReloc, isOk
+ } else if (val>>24)&0x9f == 0x91 {
+ // ELF R_AARCH64_ADD_ABS_LO12_NC or Mach-O ARM64_RELOC_PAGEOFF12
+ // patch instruction: add
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ o1 := uint32(t&0xfff) << 10
+ return val | int64(o1), noExtReloc, isOk
+ } else if (val>>24)&0x3b == 0x39 {
+ // Mach-O ARM64_RELOC_PAGEOFF12
+ // patch ldr/str(b/h/w/d/q) (integer or vector) instructions, which have different scaling factors.
+ // Mach-O uses same relocation type for them.
+ shift := uint32(val) >> 30
+ if shift == 0 && (val>>20)&0x048 == 0x048 { // 128-bit vector load
+ shift = 4
+ }
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&(1<<shift-1) != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: ARM64_RELOC_PAGEOFF12", t)
+ }
+ o1 := (uint32(t&0xfff) >> shift) << 10
+ return val | int64(o1), noExtReloc, isOk
+ } else {
+ ldr.Errorf(s, "unsupported instruction for %x R_ARM64_PCREL", val)
+ }
+
+ case objabi.R_ARM64_LDST8:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ o0 := uint32(t&0xfff) << 10
+ return val | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_LDST16:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&1 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST16_ABS_LO12_NC", t)
+ }
+ o0 := (uint32(t&0xfff) >> 1) << 10
+ return val | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_LDST32:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&3 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST32_ABS_LO12_NC", t)
+ }
+ o0 := (uint32(t&0xfff) >> 2) << 10
+ return val | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_LDST64:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&7 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST64_ABS_LO12_NC", t)
+ }
+ o0 := (uint32(t&0xfff) >> 3) << 10
+ return val | int64(o0), noExtReloc, true
+
+ case objabi.R_ARM64_LDST128:
+ t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff)
+ if t&15 != 0 {
+ ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST128_ABS_LO12_NC", t)
+ }
+ o0 := (uint32(t&0xfff) >> 4) << 10
+ return val | int64(o0), noExtReloc, true
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
+ log.Fatalf("unexpected relocation variant")
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch rt := r.Type(); rt {
+ case objabi.R_ARM64_GOTPCREL,
+ objabi.R_ARM64_PCREL_LDST8,
+ objabi.R_ARM64_PCREL_LDST16,
+ objabi.R_ARM64_PCREL_LDST32,
+ objabi.R_ARM64_PCREL_LDST64,
+ objabi.R_ADDRARM64:
+ rr := ld.ExtrelocViaOuterSym(ldr, r, s)
+ return rr, true
+ case objabi.R_CALLARM64,
+ objabi.R_ARM64_TLS_LE,
+ objabi.R_ARM64_TLS_IE:
+ return ld.ExtrelocSimple(ldr, r), true
+ }
+ return loader.ExtReloc{}, false
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // stp x16, x30, [sp, #-16]!
+ // identifying information
+ plt.AddUint32(ctxt.Arch, 0xa9bf7bf0)
+
+ // the following two instructions (adrp + ldr) load *got[2] into x17
+ // adrp x16, &got[0]
+ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4)
+ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x90000010)
+
+ // <imm> is the offset value of &got[2] to &got[0], the same below
+ // ldr x17, [x16, <imm>]
+ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4)
+ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0xf9400211)
+
+ // add x16, x16, <imm>
+ plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_PCREL, 4)
+ plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x91000210)
+
+ // br x17
+ plt.AddUint32(ctxt.Arch, 0xd61f0220)
+
+ // 3 nop for place holder
+ plt.AddUint32(ctxt.Arch, 0xd503201f)
+ plt.AddUint32(ctxt.Arch, 0xd503201f)
+ plt.AddUint32(ctxt.Arch, 0xd503201f)
+
+ // check gotplt.size == 0
+ if gotplt.Size() != 0 {
+ ctxt.Errorf(gotplt.Sym(), "got.plt is not empty at the very beginning")
+ }
+ gotplt.AddAddrPlus(ctxt.Arch, dynamic, 0)
+
+ gotplt.AddUint64(ctxt.Arch, 0)
+ gotplt.AddUint64(ctxt.Arch, 0)
+ }
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ gotplt := ldr.MakeSymbolUpdater(syms.GOTPLT)
+ rela := ldr.MakeSymbolUpdater(syms.RelaPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // adrp x16, &got.plt[0]
+ plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size())
+ plt.SetUint32(target.Arch, plt.Size()-4, 0x90000010)
+ relocs := plt.Relocs()
+ plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_GOT)
+
+ // <offset> is the offset value of &got.plt[n] to &got.plt[0]
+ // ldr x17, [x16, <offset>]
+ plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size())
+ plt.SetUint32(target.Arch, plt.Size()-4, 0xf9400211)
+ relocs = plt.Relocs()
+ plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_GOT)
+
+ // add x16, x16, <offset>
+ plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size())
+ plt.SetUint32(target.Arch, plt.Size()-4, 0x91000210)
+ relocs = plt.Relocs()
+ plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_PCREL)
+
+ // br x17
+ plt.AddUint32(target.Arch, 0xd61f0220)
+
+ // add to got.plt: pointer to plt[0]
+ gotplt.AddAddrPlus(target.Arch, plt.Sym(), 0)
+
+ // rela
+ rela.AddAddrPlus(target.Arch, gotplt.Sym(), gotplt.Size()-8)
+ sDynid := ldr.SymDynid(s)
+
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_AARCH64_JUMP_SLOT)))
+ rela.AddUint64(target.Arch, 0)
+
+ ldr.SetPlt(s, int32(plt.Size()-16))
+ } else if target.IsDarwin() {
+ ld.AddGotSym(target, ldr, syms, s, 0)
+
+ sDynid := ldr.SymDynid(s)
+ lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT)
+ lep.AddUint32(target.Arch, uint32(sDynid))
+
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ ldr.SetPlt(s, int32(plt.Size()))
+
+ // adrp x16, GOT
+ plt.AddUint32(target.Arch, 0x90000010)
+ r, _ := plt.AddRel(objabi.R_ARM64_GOT)
+ r.SetOff(int32(plt.Size() - 4))
+ r.SetSiz(4)
+ r.SetSym(syms.GOT)
+ r.SetAdd(int64(ldr.SymGot(s)))
+
+ // ldr x17, [x16, <offset>]
+ plt.AddUint32(target.Arch, 0xf9400211)
+ r, _ = plt.AddRel(objabi.R_ARM64_GOT)
+ r.SetOff(int32(plt.Size() - 4))
+ r.SetSiz(4)
+ r.SetSym(syms.GOT)
+ r.SetAdd(int64(ldr.SymGot(s)))
+
+ // br x17
+ plt.AddUint32(target.Arch, 0xd61f0220)
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
+
+const (
+ machoRelocLimit = 1 << 23
+ peRelocLimit = 1 << 20
+)
+
+func gensymlate(ctxt *ld.Link, ldr *loader.Loader) {
+ // When external linking on darwin, Mach-O relocation has only signed 24-bit
+ // addend. For large symbols, we generate "label" symbols in the middle, so
+ // that relocations can target them with smaller addends.
+ // On Windows, we only get 21 bits, again (presumably) signed.
+ // Also, on Windows (always) and Darwin (for very large binaries), the external
+ // linker does't support CALL relocations with addend, so we generate "label"
+ // symbols for functions of which we can target the middle (Duff's devices).
+ if !ctxt.IsDarwin() && !ctxt.IsWindows() || !ctxt.IsExternal() {
+ return
+ }
+
+ limit := int64(machoRelocLimit)
+ if ctxt.IsWindows() {
+ limit = peRelocLimit
+ }
+
+ // addLabelSyms adds "label" symbols at s+limit, s+2*limit, etc.
+ addLabelSyms := func(s loader.Sym, limit, sz int64) {
+ v := ldr.SymValue(s)
+ for off := limit; off < sz; off += limit {
+ p := ldr.LookupOrCreateSym(offsetLabelName(ldr, s, off), ldr.SymVersion(s))
+ ldr.SetAttrReachable(p, true)
+ ldr.SetSymValue(p, v+off)
+ ldr.SetSymSect(p, ldr.SymSect(s))
+ if ctxt.IsDarwin() {
+ ld.AddMachoSym(ldr, p)
+ } else if ctxt.IsWindows() {
+ ld.AddPELabelSym(ldr, p)
+ } else {
+ panic("missing case in gensymlate")
+ }
+ // fmt.Printf("gensymlate %s %x\n", ldr.SymName(p), ldr.SymValue(p))
+ }
+ }
+
+ // Generate symbol names for every offset we need in duffcopy/duffzero (only 64 each).
+ if s := ldr.Lookup("runtime.duffcopy", sym.SymVerABIInternal); s != 0 && ldr.AttrReachable(s) {
+ addLabelSyms(s, 8, 8*64)
+ }
+ if s := ldr.Lookup("runtime.duffzero", sym.SymVerABIInternal); s != 0 && ldr.AttrReachable(s) {
+ addLabelSyms(s, 4, 4*64)
+ }
+
+ if ctxt.IsDarwin() {
+ big := false
+ for _, seg := range ld.Segments {
+ if seg.Length >= machoRelocLimit {
+ big = true
+ break
+ }
+ }
+ if !big {
+ return // skip work if nothing big
+ }
+ }
+
+ for s, n := loader.Sym(1), loader.Sym(ldr.NSym()); s < n; s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ t := ldr.SymType(s)
+ if t == sym.STEXT {
+ // Except for Duff's devices (handled above), we don't
+ // target the middle of a function.
+ continue
+ }
+ if t >= sym.SDWARFSECT {
+ continue // no need to add label for DWARF symbols
+ }
+ sz := ldr.SymSize(s)
+ if sz <= limit {
+ continue
+ }
+ addLabelSyms(s, limit, sz)
+ }
+
+ // Also for carrier symbols (for which SymSize is 0)
+ for _, ss := range ld.CarrierSymByType {
+ if ss.Sym != 0 && ss.Size > limit {
+ addLabelSyms(ss.Sym, limit, ss.Size)
+ }
+ }
+}
+
+// offsetLabelName returns the name of the "label" symbol used for a
+// relocation targeting s+off. The label symbols is used on Darwin/Windows
+// when external linking, so that the addend fits in a Mach-O/PE relocation.
+func offsetLabelName(ldr *loader.Loader, s loader.Sym, off int64) string {
+ if off>>20<<20 == off {
+ return fmt.Sprintf("%s+%dMB", ldr.SymExtname(s), off>>20)
+ }
+ return fmt.Sprintf("%s+%d", ldr.SymExtname(s), off)
+}
+
+// Convert the direct jump relocation r to refer to a trampoline if the target is too far.
+func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
+ relocs := ldr.Relocs(s)
+ r := relocs.At(ri)
+ const pcrel = 1
+ switch r.Type() {
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26),
+ objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
+ // Host object relocations that will be turned into a PLT call.
+ // The PLT may be too far. Insert a trampoline for them.
+ fallthrough
+ case objabi.R_CALLARM64:
+ var t int64
+ // ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet
+ // laid out. Conservatively use a trampoline. This should be rare, as we lay out packages
+ // in dependency order.
+ if ldr.SymValue(rs) != 0 {
+ t = ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
+ }
+ if t >= 1<<27 || t < -1<<27 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && (ldr.SymPkg(s) == "" || ldr.SymPkg(s) != ldr.SymPkg(rs))) {
+ // direct call too far, need to insert trampoline.
+ // look up existing trampolines first. if we found one within the range
+ // of direct call, we can reuse it. otherwise create a new one.
+ var tramp loader.Sym
+ for i := 0; ; i++ {
+ oName := ldr.SymName(rs)
+ name := oName + fmt.Sprintf("%+x-tramp%d", r.Add(), i)
+ tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
+ ldr.SetAttrReachable(tramp, true)
+ if ldr.SymType(tramp) == sym.SDYNIMPORT {
+ // don't reuse trampoline defined in other module
+ continue
+ }
+ if oName == "runtime.deferreturn" {
+ ldr.SetIsDeferReturnTramp(tramp, true)
+ }
+ if ldr.SymValue(tramp) == 0 {
+ // either the trampoline does not exist -- we need to create one,
+ // or found one the address which is not assigned -- this will be
+ // laid down immediately after the current function. use this one.
+ break
+ }
+
+ t = ldr.SymValue(tramp) - (ldr.SymValue(s) + int64(r.Off()))
+ if t >= -1<<27 && t < 1<<27 {
+ // found an existing trampoline that is not too far
+ // we can just use it
+ break
+ }
+ }
+ if ldr.SymType(tramp) == 0 {
+ // trampoline does not exist, create one
+ trampb := ldr.MakeSymbolUpdater(tramp)
+ ctxt.AddTramp(trampb)
+ if ldr.SymType(rs) == sym.SDYNIMPORT {
+ if r.Add() != 0 {
+ ctxt.Errorf(s, "nonzero addend for DYNIMPORT call: %v+%d", ldr.SymName(rs), r.Add())
+ }
+ gentrampgot(ctxt, ldr, trampb, rs)
+ } else {
+ gentramp(ctxt, ldr, trampb, rs, r.Add())
+ }
+ }
+ // modify reloc to point to tramp, which will be resolved later
+ sb := ldr.MakeSymbolUpdater(s)
+ relocs := sb.Relocs()
+ r := relocs.At(ri)
+ r.SetSym(tramp)
+ r.SetAdd(0) // clear the offset embedded in the instruction
+ }
+ default:
+ ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
+ }
+}
+
+// generate a trampoline to target+offset.
+func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.SetSize(12) // 3 instructions
+ P := make([]byte, tramp.Size())
+ o1 := uint32(0x90000010) // adrp x16, target
+ o2 := uint32(0x91000210) // add x16, pc-relative-offset
+ o3 := uint32(0xd61f0200) // br x16
+ ctxt.Arch.ByteOrder.PutUint32(P, o1)
+ ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
+ ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
+ tramp.SetData(P)
+
+ r, _ := tramp.AddRel(objabi.R_ADDRARM64)
+ r.SetSiz(8)
+ r.SetSym(target)
+ r.SetAdd(offset)
+}
+
+// generate a trampoline to target+offset for a DYNIMPORT symbol via GOT.
+func gentrampgot(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym) {
+ tramp.SetSize(12) // 3 instructions
+ P := make([]byte, tramp.Size())
+ o1 := uint32(0x90000010) // adrp x16, target@GOT
+ o2 := uint32(0xf9400210) // ldr x16, [x16, offset]
+ o3 := uint32(0xd61f0200) // br x16
+ ctxt.Arch.ByteOrder.PutUint32(P, o1)
+ ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
+ ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
+ tramp.SetData(P)
+
+ r, _ := tramp.AddRel(objabi.R_ARM64_GOTPCREL)
+ r.SetSiz(8)
+ r.SetSym(target)
+}
diff --git a/src/cmd/link/internal/arm64/l.go b/src/cmd/link/internal/arm64/l.go
new file mode 100644
index 0000000..4aa2708
--- /dev/null
+++ b/src/cmd/link/internal/arm64/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package arm64
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 16
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 31
+ dwarfRegLR = 30
+)
diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go
new file mode 100644
index 0000000..a47be0b
--- /dev/null
+++ b/src/cmd/link/internal/arm64/obj.go
@@ -0,0 +1,120 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package arm64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchARM64
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ TrampLimit: 0x7c00000, // 26-bit signed offset * 4, leave room for PLT etc.
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ GenSymsLate: gensymlate,
+ Machoreloc1: machoreloc1,
+ MachorelocSize: 8,
+ PEreloc1: pereloc1,
+ Trampoline: trampoline,
+
+ Androiddynld: "/system/bin/linker64",
+ Linuxdynld: "/lib/ld-linux-aarch64.so.1",
+ LinuxdynldMusl: "/lib/ld-musl-aarch64.so.1",
+
+ Freebsddynld: "/usr/libexec/ld-elf.so.1",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "/libexec/ld.elf_so",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hlinux, /* arm64 elf */
+ objabi.Hfreebsd,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd:
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+
+ case objabi.Hdarwin: /* apple MACH */
+ ld.HEADR = ld.INITIAL_MACHO_HEADR
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 1<<32 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 16384 // 16K page alignment
+ }
+
+ case objabi.Hwindows: /* PE executable */
+ // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+ return
+ }
+}
diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go
new file mode 100644
index 0000000..7c6f278
--- /dev/null
+++ b/src/cmd/link/internal/benchmark/bench.go
@@ -0,0 +1,195 @@
+// Copyright 2020 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.
+
+// Package benchmark provides a Metrics object that enables memory and CPU
+// profiling for the linker. The Metrics objects can be used to mark stages
+// of the code, and name the measurements during that stage. There is also
+// optional GCs that can be performed at the end of each stage, so you
+// can get an accurate measurement of how each stage changes live memory.
+package benchmark
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "time"
+ "unicode"
+)
+
+type Flags int
+
+const (
+ GC = 1 << iota
+ NoGC Flags = 0
+)
+
+type Metrics struct {
+ gc Flags
+ marks []*mark
+ curMark *mark
+ filebase string
+ pprofFile *os.File
+}
+
+type mark struct {
+ name string
+ startM, endM, gcM runtime.MemStats
+ startT, endT time.Time
+}
+
+// New creates a new Metrics object.
+//
+// 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()
+// }
+//
+// Note that a nil Metrics object won't cause any errors, so one could write
+// code like:
+//
+// func main() {
+// enableBenchmarking := flag.Bool("enable", true, "enables benchmarking")
+// flag.Parse()
+// var bench *benchmark.Metrics
+// if *enableBenchmarking {
+// bench = benchmark.New(benchmark.GC)
+// }
+// bench.Start("foo")
+// // etc.
+// }
+func New(gc Flags, filebase string) *Metrics {
+ if gc == GC {
+ runtime.GC()
+ }
+ return &Metrics{gc: gc, filebase: filebase}
+}
+
+// Report reports the metrics.
+// Closes the currently Start(ed) range, and writes the report to the given io.Writer.
+func (m *Metrics) Report(w io.Writer) {
+ if m == nil {
+ return
+ }
+
+ m.closeMark()
+
+ gcString := ""
+ if m.gc == GC {
+ gcString = "_GC"
+ }
+
+ var totTime time.Duration
+ for _, curMark := range m.marks {
+ dur := curMark.endT.Sub(curMark.startT)
+ totTime += dur
+ fmt.Fprintf(w, "%s 1 %d ns/op", makeBenchString(curMark.name+gcString), dur.Nanoseconds())
+ fmt.Fprintf(w, "\t%d B/op", curMark.endM.TotalAlloc-curMark.startM.TotalAlloc)
+ fmt.Fprintf(w, "\t%d allocs/op", curMark.endM.Mallocs-curMark.startM.Mallocs)
+ if m.gc == GC {
+ fmt.Fprintf(w, "\t%d live-B", curMark.gcM.HeapAlloc)
+ } else {
+ fmt.Fprintf(w, "\t%d heap-B", curMark.endM.HeapAlloc)
+ }
+ fmt.Fprintf(w, "\n")
+ }
+ fmt.Fprintf(w, "%s 1 %d ns/op\n", makeBenchString("total time"+gcString), totTime.Nanoseconds())
+}
+
+// Starts marks the beginning of a new measurement phase.
+// Once a metric is started, it continues until either a Report is issued, or another Start is called.
+func (m *Metrics) Start(name string) {
+ if m == nil {
+ return
+ }
+ m.closeMark()
+ m.curMark = &mark{name: name}
+ // Unlikely we need to a GC here, as one was likely just done in closeMark.
+ if m.shouldPProf() {
+ f, err := os.Create(makePProfFilename(m.filebase, name, "cpuprof"))
+ if err != nil {
+ panic(err)
+ }
+ m.pprofFile = f
+ if err = pprof.StartCPUProfile(m.pprofFile); err != nil {
+ panic(err)
+ }
+ }
+ runtime.ReadMemStats(&m.curMark.startM)
+ m.curMark.startT = time.Now()
+}
+
+func (m *Metrics) closeMark() {
+ if m == nil || m.curMark == nil {
+ return
+ }
+ m.curMark.endT = time.Now()
+ if m.shouldPProf() {
+ pprof.StopCPUProfile()
+ m.pprofFile.Close()
+ m.pprofFile = nil
+ }
+ runtime.ReadMemStats(&m.curMark.endM)
+ if m.gc == GC {
+ runtime.GC()
+ runtime.ReadMemStats(&m.curMark.gcM)
+ if m.shouldPProf() {
+ // Collect a profile of the live heap. Do a
+ // second GC to force sweep completion so we
+ // get a complete snapshot of the live heap at
+ // the end of this phase.
+ runtime.GC()
+ f, err := os.Create(makePProfFilename(m.filebase, m.curMark.name, "memprof"))
+ if err != nil {
+ panic(err)
+ }
+ err = pprof.WriteHeapProfile(f)
+ if err != nil {
+ panic(err)
+ }
+ err = f.Close()
+ if err != nil {
+ panic(err)
+ }
+ }
+ }
+ m.marks = append(m.marks, m.curMark)
+ m.curMark = nil
+}
+
+// shouldPProf returns true if we should be doing pprof runs.
+func (m *Metrics) shouldPProf() bool {
+ return m != nil && len(m.filebase) > 0
+}
+
+// makeBenchString makes a benchmark string consumable by Go's benchmarking tools.
+func makeBenchString(name string) string {
+ needCap := true
+ ret := []rune("Benchmark")
+ for _, r := range name {
+ if unicode.IsSpace(r) {
+ needCap = true
+ continue
+ }
+ if needCap {
+ r = unicode.ToUpper(r)
+ needCap = false
+ }
+ ret = append(ret, r)
+ }
+ return string(ret)
+}
+
+func makePProfFilename(filebase, name, typ string) string {
+ return fmt.Sprintf("%s_%s.%s", filebase, makeBenchString(name), typ)
+}
diff --git a/src/cmd/link/internal/benchmark/bench_test.go b/src/cmd/link/internal/benchmark/bench_test.go
new file mode 100644
index 0000000..419dc55
--- /dev/null
+++ b/src/cmd/link/internal/benchmark/bench_test.go
@@ -0,0 +1,54 @@
+// Copyright 2020 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.
+
+package benchmark
+
+import (
+ "testing"
+)
+
+func TestMakeBenchString(t *testing.T) {
+ tests := []struct {
+ have, want string
+ }{
+ {"foo", "BenchmarkFoo"},
+ {" foo ", "BenchmarkFoo"},
+ {"foo bar", "BenchmarkFooBar"},
+ }
+ for i, test := range tests {
+ if v := makeBenchString(test.have); test.want != v {
+ t.Errorf("test[%d] makeBenchString(%q) == %q, want %q", i, test.have, v, test.want)
+ }
+ }
+}
+
+func TestPProfFlag(t *testing.T) {
+ tests := []struct {
+ name string
+ want bool
+ }{
+ {"", false},
+ {"foo", true},
+ }
+ for i, test := range tests {
+ b := New(GC, test.name)
+ if v := b.shouldPProf(); test.want != v {
+ t.Errorf("test[%d] shouldPProf() == %v, want %v", i, v, test.want)
+ }
+ }
+}
+
+func TestPProfNames(t *testing.T) {
+ want := "foo_BenchmarkTest.cpuprof"
+ if v := makePProfFilename("foo", "test", "cpuprof"); v != want {
+ t.Errorf("makePProfFilename() == %q, want %q", v, want)
+ }
+}
+
+// Ensure that public APIs work with a nil Metrics object.
+func TestNilBenchmarkObject(t *testing.T) {
+ var b *Metrics
+ b.Start("TEST")
+ b.Report(nil)
+}
diff --git a/src/cmd/link/internal/dwtest/dwtest.go b/src/cmd/link/internal/dwtest/dwtest.go
new file mode 100644
index 0000000..c68edf4
--- /dev/null
+++ b/src/cmd/link/internal/dwtest/dwtest.go
@@ -0,0 +1,197 @@
+// Copyright 2021 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.
+
+package dwtest
+
+import (
+ "debug/dwarf"
+ "errors"
+ "fmt"
+ "os"
+)
+
+// Helper type for supporting queries on DIEs within a DWARF
+// .debug_info section. Invoke the populate() method below passing in
+// a dwarf.Reader, which will read in all DIEs and keep track of
+// parent/child relationships. Queries can then be made to ask for
+// DIEs by name or by offset. This will hopefully reduce boilerplate
+// for future test writing.
+
+type Examiner struct {
+ dies []*dwarf.Entry
+ idxByOffset map[dwarf.Offset]int
+ kids map[int][]int
+ parent map[int]int
+ byname map[string][]int
+}
+
+// Populate the Examiner using the DIEs read from rdr.
+func (ex *Examiner) Populate(rdr *dwarf.Reader) error {
+ ex.idxByOffset = make(map[dwarf.Offset]int)
+ ex.kids = make(map[int][]int)
+ ex.parent = make(map[int]int)
+ ex.byname = make(map[string][]int)
+ var nesting []int
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ return err
+ }
+ if entry.Tag == 0 {
+ // terminator
+ if len(nesting) == 0 {
+ return errors.New("nesting stack underflow")
+ }
+ nesting = nesting[:len(nesting)-1]
+ continue
+ }
+ idx := len(ex.dies)
+ ex.dies = append(ex.dies, entry)
+ if _, found := ex.idxByOffset[entry.Offset]; found {
+ return errors.New("DIE clash on offset")
+ }
+ ex.idxByOffset[entry.Offset] = idx
+ if name, ok := entry.Val(dwarf.AttrName).(string); ok {
+ ex.byname[name] = append(ex.byname[name], idx)
+ }
+ if len(nesting) > 0 {
+ parent := nesting[len(nesting)-1]
+ ex.kids[parent] = append(ex.kids[parent], idx)
+ ex.parent[idx] = parent
+ }
+ if entry.Children {
+ nesting = append(nesting, idx)
+ }
+ }
+ if len(nesting) > 0 {
+ return errors.New("unterminated child sequence")
+ }
+ return nil
+}
+
+func (e *Examiner) DIEs() []*dwarf.Entry {
+ return e.dies
+}
+
+func indent(ilevel int) {
+ for i := 0; i < ilevel; i++ {
+ fmt.Printf(" ")
+ }
+}
+
+// For debugging new tests
+func (ex *Examiner) DumpEntry(idx int, dumpKids bool, ilevel int) {
+ if idx >= len(ex.dies) {
+ fmt.Fprintf(os.Stderr, "DumpEntry: bad DIE %d: index out of range\n", idx)
+ return
+ }
+ entry := ex.dies[idx]
+ indent(ilevel)
+ fmt.Printf("0x%x: %v\n", idx, entry.Tag)
+ for _, f := range entry.Field {
+ indent(ilevel)
+ fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val)
+ }
+ if dumpKids {
+ ksl := ex.kids[idx]
+ for _, k := range ksl {
+ ex.DumpEntry(k, true, ilevel+2)
+ }
+ }
+}
+
+// Given a DIE offset, return the previously read dwarf.Entry, or nil
+func (ex *Examiner) EntryFromOffset(off dwarf.Offset) *dwarf.Entry {
+ if idx, found := ex.idxByOffset[off]; found && idx != -1 {
+ return ex.entryFromIdx(idx)
+ }
+ return nil
+}
+
+// Return the ID that Examiner uses to refer to the DIE at offset off
+func (ex *Examiner) IdxFromOffset(off dwarf.Offset) int {
+ if idx, found := ex.idxByOffset[off]; found {
+ return idx
+ }
+ return -1
+}
+
+// Return the dwarf.Entry pointer for the DIE with id 'idx'
+func (ex *Examiner) entryFromIdx(idx int) *dwarf.Entry {
+ if idx >= len(ex.dies) || idx < 0 {
+ return nil
+ }
+ return ex.dies[idx]
+}
+
+// Returns a list of child entries for a die with ID 'idx'
+func (ex *Examiner) Children(idx int) []*dwarf.Entry {
+ sl := ex.kids[idx]
+ ret := make([]*dwarf.Entry, len(sl))
+ for i, k := range sl {
+ ret[i] = ex.entryFromIdx(k)
+ }
+ return ret
+}
+
+// Returns parent DIE for DIE 'idx', or nil if the DIE is top level
+func (ex *Examiner) Parent(idx int) *dwarf.Entry {
+ p, found := ex.parent[idx]
+ if !found {
+ return nil
+ }
+ return ex.entryFromIdx(p)
+}
+
+// ParentCU returns the enclosing compilation unit DIE for the DIE
+// with a given index, or nil if for some reason we can't establish a
+// parent.
+func (ex *Examiner) ParentCU(idx int) *dwarf.Entry {
+ for {
+ parentDie := ex.Parent(idx)
+ if parentDie == nil {
+ return nil
+ }
+ if parentDie.Tag == dwarf.TagCompileUnit {
+ return parentDie
+ }
+ idx = ex.IdxFromOffset(parentDie.Offset)
+ }
+}
+
+// FileRef takes a given DIE by index and a numeric file reference
+// (presumably from a decl_file or call_file attribute), looks up the
+// reference in the .debug_line file table, and returns the proper
+// string for it. We need to know which DIE is making the reference
+// so as to find the right compilation unit.
+func (ex *Examiner) FileRef(dw *dwarf.Data, dieIdx int, fileRef int64) (string, error) {
+
+ // Find the parent compilation unit DIE for the specified DIE.
+ cuDie := ex.ParentCU(dieIdx)
+ if cuDie == nil {
+ return "", fmt.Errorf("no parent CU DIE for DIE with idx %d?", dieIdx)
+ }
+ // Construct a line reader and then use it to get the file string.
+ lr, lrerr := dw.LineReader(cuDie)
+ if lrerr != nil {
+ return "", fmt.Errorf("d.LineReader: %v", lrerr)
+ }
+ files := lr.Files()
+ if fileRef < 0 || int(fileRef) > len(files)-1 {
+ return "", fmt.Errorf("Examiner.FileRef: malformed file reference %d", fileRef)
+ }
+ return files[fileRef].Name, nil
+}
+
+// Return a list of all DIEs with name 'name'. When searching for DIEs
+// by name, keep in mind that the returned results will include child
+// DIEs such as params/variables. For example, asking for all DIEs named
+// "p" for even a small program will give you 400-500 entries.
+func (ex *Examiner) Named(name string) []*dwarf.Entry {
+ sl := ex.byname[name]
+ ret := make([]*dwarf.Entry, len(sl))
+ for i, k := range sl {
+ ret[i] = ex.entryFromIdx(k)
+ }
+ return ret
+}
diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
new file mode 100644
index 0000000..73c1cd3
--- /dev/null
+++ b/src/cmd/link/internal/ld/ar.go
@@ -0,0 +1,247 @@
+// Inferno utils/include/ar.h
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/include/ar.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "cmd/internal/bio"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "encoding/binary"
+ "fmt"
+ "internal/buildcfg"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+const (
+ SARMAG = 8
+ SAR_HDR = 16 + 44
+)
+
+const (
+ ARMAG = "!<arch>\n"
+)
+
+type ArHdr struct {
+ name string
+ date string
+ uid string
+ gid string
+ mode string
+ size string
+ fmag string
+}
+
+// pruneUndefsForWindows trims the list "undefs" of currently
+// outstanding unresolved symbols to remove references to DLL import
+// symbols (e.g. "__imp_XXX"). In older versions of the linker, we
+// would just immediately forward references from the import sym
+// (__imp_XXX) to the DLL sym (XXX), but with newer compilers this
+// strategy falls down in certain cases. We instead now do this
+// forwarding later on as a post-processing step, and meaning that
+// during the middle part of host object loading we can see a lot of
+// unresolved (SXREF) import symbols. We do not, however, want to
+// trigger the inclusion of an object from a host archive if the
+// reference is going to be eventually forwarded to the corresponding
+// SDYNIMPORT symbol, so here we strip out such refs from the undefs
+// list.
+func pruneUndefsForWindows(ldr *loader.Loader, undefs, froms []loader.Sym) ([]loader.Sym, []loader.Sym) {
+ var newundefs []loader.Sym
+ var newfroms []loader.Sym
+ for _, s := range undefs {
+ sname := ldr.SymName(s)
+ if strings.HasPrefix(sname, "__imp_") {
+ dname := sname[len("__imp_"):]
+ ds := ldr.Lookup(dname, 0)
+ if ds != 0 && ldr.SymType(ds) == sym.SDYNIMPORT {
+ // Don't try to pull things out of a host archive to
+ // satisfy this symbol.
+ continue
+ }
+ }
+ newundefs = append(newundefs, s)
+ newfroms = append(newfroms, s)
+ }
+ return newundefs, newfroms
+}
+
+// hostArchive reads an archive file holding host objects and links in
+// required objects. The general format is the same as a Go archive
+// file, but it has an armap listing symbols and the objects that
+// 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) {
+ // It's OK if we don't have a libgcc file at all.
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("skipping libgcc file: %v\n", err)
+ }
+ return
+ }
+ Exitf("cannot open file %s: %v", name, err)
+ }
+ defer f.Close()
+
+ var magbuf [len(ARMAG)]byte
+ if _, err := io.ReadFull(f, magbuf[:]); err != nil {
+ Exitf("file %s too short", name)
+ }
+
+ if string(magbuf[:]) != ARMAG {
+ Exitf("%s is not an archive file", name)
+ }
+
+ var arhdr ArHdr
+ l := nextar(f, f.Offset(), &arhdr)
+ if l <= 0 {
+ Exitf("%s missing armap", name)
+ }
+
+ var armap archiveMap
+ if arhdr.name == "/" || arhdr.name == "/SYM64/" {
+ armap = readArmap(name, f, arhdr)
+ } else {
+ Exitf("%s missing armap", name)
+ }
+
+ loaded := make(map[uint64]bool)
+ any := true
+ for any {
+ var load []uint64
+ returnAllUndefs := -1
+ undefs, froms := ctxt.loader.UndefinedRelocTargets(returnAllUndefs)
+ if buildcfg.GOOS == "windows" {
+ undefs, froms = pruneUndefsForWindows(ctxt.loader, undefs, froms)
+ }
+ for k, symIdx := range undefs {
+ sname := ctxt.loader.SymName(symIdx)
+ if off := armap[sname]; off != 0 && !loaded[off] {
+ load = append(load, off)
+ loaded[off] = true
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("hostArchive(%s): selecting object at offset %x to resolve %s [%d] reference from %s [%d]\n", name, off, sname, symIdx, ctxt.loader.SymName(froms[k]), froms[k])
+ }
+ }
+ }
+
+ for _, off := range load {
+ l := nextar(f, int64(off), &arhdr)
+ if l <= 0 {
+ Exitf("%s missing archive entry at offset %d", name, off)
+ }
+ pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
+ l = atolwhex(arhdr.size)
+
+ 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
+ }
+ f.MustSeek(h.off, 0)
+ h.ld(ctxt, f, h.pkg, h.length, h.pn)
+ if *flagCaptureHostObjs != "" {
+ captureHostObj(h)
+ }
+ }
+
+ any = len(load) > 0
+ }
+}
+
+// archiveMap is an archive symbol map: a mapping from symbol name to
+// offset within the archive file.
+type archiveMap map[string]uint64
+
+// readArmap reads the archive symbol map.
+func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap {
+ is64 := arhdr.name == "/SYM64/"
+ wordSize := 4
+ if is64 {
+ wordSize = 8
+ }
+
+ contents := make([]byte, atolwhex(arhdr.size))
+ if _, err := io.ReadFull(f, contents); err != nil {
+ Exitf("short read from %s", filename)
+ }
+
+ var c uint64
+ if is64 {
+ c = binary.BigEndian.Uint64(contents)
+ } else {
+ c = uint64(binary.BigEndian.Uint32(contents))
+ }
+ contents = contents[wordSize:]
+
+ ret := make(archiveMap)
+
+ names := contents[c*uint64(wordSize):]
+ for i := uint64(0); i < c; i++ {
+ n := 0
+ for names[n] != 0 {
+ n++
+ }
+ name := string(names[:n])
+ names = names[n+1:]
+
+ // For Mach-O and PE/386 files we strip a leading
+ // underscore from the symbol name.
+ if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" || (buildcfg.GOOS == "windows" && buildcfg.GOARCH == "386") {
+ if name[0] == '_' && len(name) > 1 {
+ name = name[1:]
+ }
+ }
+
+ var off uint64
+ if is64 {
+ off = binary.BigEndian.Uint64(contents)
+ } else {
+ off = uint64(binary.BigEndian.Uint32(contents))
+ }
+ contents = contents[wordSize:]
+
+ ret[name] = off
+ }
+
+ return ret
+}
diff --git a/src/cmd/link/internal/ld/asmb.go b/src/cmd/link/internal/ld/asmb.go
new file mode 100644
index 0000000..cd8927b
--- /dev/null
+++ b/src/cmd/link/internal/ld/asmb.go
@@ -0,0 +1,208 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "fmt"
+ "runtime"
+ "sync"
+)
+
+// Assembling the binary is broken into two steps:
+// - writing out the code/data/dwarf Segments, applying relocations on the fly
+// - writing out the architecture specific pieces.
+//
+// This function handles the first part.
+func asmb(ctxt *Link) {
+ // TODO(jfaller): delete me.
+ if thearch.Asmb != nil {
+ thearch.Asmb(ctxt, ctxt.loader)
+ return
+ }
+
+ if ctxt.IsELF {
+ Asmbelfsetup()
+ }
+
+ var wg sync.WaitGroup
+ f := func(ctxt *Link, out *OutBuf, start, length int64) {
+ pad := thearch.CodePad
+ if pad == nil {
+ pad = zeros[:]
+ }
+ CodeblkPad(ctxt, out, start, length, pad)
+ }
+
+ for _, sect := range Segtext.Sections {
+ offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff
+ // Handle text sections with Codeblk
+ if sect.Name == ".text" {
+ writeParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length)
+ } else {
+ writeParallel(&wg, datblk, ctxt, offset, sect.Vaddr, sect.Length)
+ }
+ }
+
+ if Segrodata.Filelen > 0 {
+ writeParallel(&wg, datblk, ctxt, Segrodata.Fileoff, Segrodata.Vaddr, Segrodata.Filelen)
+ }
+
+ if Segrelrodata.Filelen > 0 {
+ writeParallel(&wg, datblk, ctxt, Segrelrodata.Fileoff, Segrelrodata.Vaddr, Segrelrodata.Filelen)
+ }
+
+ writeParallel(&wg, datblk, ctxt, Segdata.Fileoff, Segdata.Vaddr, Segdata.Filelen)
+
+ writeParallel(&wg, dwarfblk, ctxt, Segdwarf.Fileoff, Segdwarf.Vaddr, Segdwarf.Filelen)
+
+ wg.Wait()
+}
+
+// Assembling the binary is broken into two steps:
+// - writing out the code/data/dwarf Segments
+// - writing out the architecture specific pieces.
+//
+// This function handles the second part.
+func asmb2(ctxt *Link) {
+ if thearch.Asmb2 != nil {
+ thearch.Asmb2(ctxt, ctxt.loader)
+ return
+ }
+
+ symSize = 0
+ spSize = 0
+ lcSize = 0
+
+ switch ctxt.HeadType {
+ default:
+ panic("unknown platform")
+
+ // Macho
+ case objabi.Hdarwin:
+ asmbMacho(ctxt)
+
+ // Plan9
+ case objabi.Hplan9:
+ asmbPlan9(ctxt)
+
+ // PE
+ case objabi.Hwindows:
+ asmbPe(ctxt)
+
+ // Xcoff
+ case objabi.Haix:
+ asmbXcoff(ctxt)
+
+ // Elf
+ case objabi.Hdragonfly,
+ objabi.Hfreebsd,
+ objabi.Hlinux,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd,
+ objabi.Hsolaris:
+ asmbElf(ctxt)
+ }
+
+ if *FlagC {
+ fmt.Printf("textsize=%d\n", Segtext.Filelen)
+ fmt.Printf("datsize=%d\n", Segdata.Filelen)
+ fmt.Printf("bsssize=%d\n", Segdata.Length-Segdata.Filelen)
+ fmt.Printf("symsize=%d\n", symSize)
+ fmt.Printf("lcsize=%d\n", lcSize)
+ fmt.Printf("total=%d\n", Segtext.Filelen+Segdata.Length+uint64(symSize)+uint64(lcSize))
+ }
+}
+
+// writePlan9Header writes out the plan9 header at the present position in the OutBuf.
+func writePlan9Header(buf *OutBuf, magic uint32, entry int64, is64Bit bool) {
+ if is64Bit {
+ magic |= 0x00008000
+ }
+ buf.Write32b(magic)
+ buf.Write32b(uint32(Segtext.Filelen))
+ buf.Write32b(uint32(Segdata.Filelen))
+ buf.Write32b(uint32(Segdata.Length - Segdata.Filelen))
+ buf.Write32b(uint32(symSize))
+ if is64Bit {
+ buf.Write32b(uint32(entry &^ 0x80000000))
+ } else {
+ buf.Write32b(uint32(entry))
+ }
+ buf.Write32b(uint32(spSize))
+ buf.Write32b(uint32(lcSize))
+ // amd64 includes the entry at the beginning of the symbol table.
+ if is64Bit {
+ buf.Write64b(uint64(entry))
+ }
+}
+
+// asmbPlan9 assembles a plan 9 binary.
+func asmbPlan9(ctxt *Link) {
+ if !*FlagS {
+ *FlagS = true
+ symo := int64(Segdata.Fileoff + Segdata.Filelen)
+ ctxt.Out.SeekSet(symo)
+ asmbPlan9Sym(ctxt)
+ }
+ ctxt.Out.SeekSet(0)
+ writePlan9Header(ctxt.Out, thearch.Plan9Magic, Entryvalue(ctxt), thearch.Plan9_64Bit)
+}
+
+// sizeExtRelocs precomputes the size needed for the reloc records,
+// sets the size and offset for relocation records in each section,
+// and mmap the output buffer with the proper size.
+func sizeExtRelocs(ctxt *Link, relsize uint32) {
+ if relsize == 0 {
+ panic("sizeExtRelocs: relocation size not set")
+ }
+ var sz int64
+ for _, seg := range Segments {
+ for _, sect := range seg.Sections {
+ sect.Reloff = uint64(ctxt.Out.Offset() + sz)
+ sect.Rellen = uint64(relsize * sect.Relcount)
+ sz += int64(sect.Rellen)
+ }
+ }
+ filesz := ctxt.Out.Offset() + sz
+ err := ctxt.Out.Mmap(uint64(filesz))
+ if err != nil {
+ Exitf("mapping output file failed: %v", err)
+ }
+}
+
+// relocSectFn wraps the function writing relocations of a section
+// for parallel execution. Returns the wrapped function and a wait
+// group for which the caller should wait.
+func relocSectFn(ctxt *Link, relocSect func(*Link, *OutBuf, *sym.Section, []loader.Sym)) (func(*Link, *sym.Section, []loader.Sym), *sync.WaitGroup) {
+ var fn func(ctxt *Link, sect *sym.Section, syms []loader.Sym)
+ var wg sync.WaitGroup
+ var sem chan int
+ if ctxt.Out.isMmapped() {
+ // Write sections in parallel.
+ sem = make(chan int, 2*runtime.GOMAXPROCS(0))
+ fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) {
+ wg.Add(1)
+ sem <- 1
+ out, err := ctxt.Out.View(sect.Reloff)
+ if err != nil {
+ panic(err)
+ }
+ go func() {
+ relocSect(ctxt, out, sect, syms)
+ wg.Done()
+ <-sem
+ }()
+ }
+ } else {
+ // We cannot Mmap. Write sequentially.
+ fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) {
+ relocSect(ctxt, ctxt.Out, sect, syms)
+ }
+ }
+ return fn, &wg
+}
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
new file mode 100644
index 0000000..ba74b6f
--- /dev/null
+++ b/src/cmd/link/internal/ld/config.go
@@ -0,0 +1,307 @@
+// Copyright 2016 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.
+
+package ld
+
+import (
+ "cmd/internal/sys"
+ "fmt"
+ "internal/buildcfg"
+ "internal/platform"
+)
+
+// A BuildMode indicates the sort of object we are building.
+//
+// Possible build modes are the same as those for the -buildmode flag
+// in cmd/go, and are documented in 'go help buildmode'.
+type BuildMode uint8
+
+const (
+ BuildModeUnset BuildMode = iota
+ BuildModeExe
+ BuildModePIE
+ BuildModeCArchive
+ BuildModeCShared
+ BuildModeShared
+ BuildModePlugin
+)
+
+func (mode *BuildMode) Set(s string) error {
+ badmode := func() error {
+ return fmt.Errorf("buildmode %s not supported on %s/%s", s, buildcfg.GOOS, buildcfg.GOARCH)
+ }
+ switch s {
+ default:
+ return fmt.Errorf("invalid buildmode: %q", s)
+ case "exe":
+ switch buildcfg.GOOS + "/" + buildcfg.GOARCH {
+ case "darwin/arm64", "windows/arm", "windows/arm64": // On these platforms, everything is PIE
+ *mode = BuildModePIE
+ default:
+ *mode = BuildModeExe
+ }
+ case "pie":
+ switch buildcfg.GOOS {
+ case "aix", "android", "linux", "windows", "darwin", "ios":
+ case "freebsd":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModePIE
+ case "c-archive":
+ switch buildcfg.GOOS {
+ case "aix", "darwin", "ios", "linux":
+ case "freebsd":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ default:
+ return badmode()
+ }
+ case "windows":
+ switch buildcfg.GOARCH {
+ case "amd64", "386", "arm", "arm64":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModeCArchive
+ case "c-shared":
+ switch buildcfg.GOARCH {
+ case "386", "amd64", "arm", "arm64", "ppc64le", "riscv64", "s390x":
+ default:
+ return badmode()
+ }
+ *mode = BuildModeCShared
+ case "shared":
+ switch buildcfg.GOOS {
+ case "linux":
+ switch buildcfg.GOARCH {
+ case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModeShared
+ case "plugin":
+ switch buildcfg.GOOS {
+ case "linux":
+ switch buildcfg.GOARCH {
+ case "386", "amd64", "arm", "arm64", "s390x", "ppc64le":
+ default:
+ return badmode()
+ }
+ case "darwin":
+ switch buildcfg.GOARCH {
+ case "amd64", "arm64":
+ default:
+ return badmode()
+ }
+ case "freebsd":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ default:
+ return badmode()
+ }
+ default:
+ return badmode()
+ }
+ *mode = BuildModePlugin
+ }
+ return nil
+}
+
+func (mode *BuildMode) String() string {
+ switch *mode {
+ case BuildModeUnset:
+ return "" // avoid showing a default in usage message
+ case BuildModeExe:
+ return "exe"
+ case BuildModePIE:
+ return "pie"
+ case BuildModeCArchive:
+ return "c-archive"
+ case BuildModeCShared:
+ return "c-shared"
+ case BuildModeShared:
+ return "shared"
+ case BuildModePlugin:
+ return "plugin"
+ }
+ return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
+}
+
+// LinkMode indicates whether an external linker is used for the final link.
+type LinkMode uint8
+
+const (
+ LinkAuto LinkMode = iota
+ LinkInternal
+ LinkExternal
+)
+
+func (mode *LinkMode) Set(s string) error {
+ switch s {
+ default:
+ return fmt.Errorf("invalid linkmode: %q", s)
+ case "auto":
+ *mode = LinkAuto
+ case "internal":
+ *mode = LinkInternal
+ case "external":
+ *mode = LinkExternal
+ }
+ return nil
+}
+
+func (mode *LinkMode) String() string {
+ switch *mode {
+ case LinkAuto:
+ return "auto"
+ case LinkInternal:
+ return "internal"
+ case LinkExternal:
+ return "external"
+ }
+ return fmt.Sprintf("LinkMode(%d)", uint8(*mode))
+}
+
+// mustLinkExternal reports whether the program being linked requires
+// the external linker be used to complete the link.
+func mustLinkExternal(ctxt *Link) (res bool, reason string) {
+ if ctxt.Debugvlog > 1 {
+ defer func() {
+ if res {
+ ctxt.Logf("external linking is forced by: %s\n", reason)
+ }
+ }()
+ }
+
+ if platform.MustLinkExternal(buildcfg.GOOS, buildcfg.GOARCH) {
+ return true, fmt.Sprintf("%s/%s requires external linking", buildcfg.GOOS, buildcfg.GOARCH)
+ }
+
+ if *flagMsan {
+ return true, "msan"
+ }
+
+ if *flagAsan {
+ return true, "asan"
+ }
+
+ // Internally linking cgo is incomplete on some architectures.
+ // https://golang.org/issue/14449
+ if iscgo && ctxt.Arch.InFamily(sys.Loong64, sys.MIPS64, sys.MIPS, sys.RISCV64) {
+ return true, buildcfg.GOARCH + " does not support internal cgo"
+ }
+ if iscgo && (buildcfg.GOOS == "android" || buildcfg.GOOS == "dragonfly") {
+ // It seems that on Dragonfly thread local storage is
+ // set up by the dynamic linker, so internal cgo linking
+ // doesn't work. Test case is "go test runtime/cgo".
+ return true, buildcfg.GOOS + " does not support internal cgo"
+ }
+ if iscgo && buildcfg.GOOS == "windows" && buildcfg.GOARCH == "arm64" {
+ // windows/arm64 internal linking is not implemented.
+ return true, buildcfg.GOOS + "/" + buildcfg.GOARCH + " does not support internal cgo"
+ }
+ if iscgo && ctxt.Arch == sys.ArchPPC64 {
+ // Big Endian PPC64 cgo internal linking is not implemented for aix or linux.
+ return true, buildcfg.GOOS + " does not support internal cgo"
+ }
+
+ // Some build modes require work the internal linker cannot do (yet).
+ switch ctxt.BuildMode {
+ case BuildModeCArchive:
+ return true, "buildmode=c-archive"
+ case BuildModeCShared:
+ return true, "buildmode=c-shared"
+ case BuildModePIE:
+ switch buildcfg.GOOS + "/" + buildcfg.GOARCH {
+ case "android/arm64":
+ case "linux/amd64", "linux/arm64", "linux/ppc64le":
+ case "windows/386", "windows/amd64", "windows/arm", "windows/arm64":
+ case "darwin/amd64", "darwin/arm64":
+ default:
+ // Internal linking does not support TLS_IE.
+ return true, "buildmode=pie"
+ }
+ case BuildModePlugin:
+ return true, "buildmode=plugin"
+ case BuildModeShared:
+ return true, "buildmode=shared"
+ }
+ if ctxt.linkShared {
+ return true, "dynamically linking with a shared library"
+ }
+
+ if unknownObjFormat {
+ return true, "some input objects have an unrecognized file format"
+ }
+
+ if len(dynimportfail) > 0 {
+ // This error means that we were unable to generate
+ // the _cgo_import.go file for some packages.
+ // This typically means that there are some dependencies
+ // that the cgo tool could not figure out.
+ // See issue #52863.
+ return true, fmt.Sprintf("some packages could not be built to support internal linking (%v)", dynimportfail)
+ }
+
+ return false, ""
+}
+
+// determineLinkMode sets ctxt.LinkMode.
+//
+// It is called after flags are processed and inputs are processed,
+// so the ctxt.LinkMode variable has an initial value from the -linkmode
+// flag and the iscgo, externalobj, and unknownObjFormat variables are set.
+func determineLinkMode(ctxt *Link) {
+ extNeeded, extReason := mustLinkExternal(ctxt)
+ via := ""
+
+ if ctxt.LinkMode == LinkAuto {
+ // The environment variable GO_EXTLINK_ENABLED controls the
+ // default value of -linkmode. If it is not set when the
+ // linker is called we take the value it was set to when
+ // cmd/link was compiled. (See make.bash.)
+ switch buildcfg.Getgoextlinkenabled() {
+ case "0":
+ ctxt.LinkMode = LinkInternal
+ via = "via GO_EXTLINK_ENABLED "
+ case "1":
+ ctxt.LinkMode = LinkExternal
+ via = "via GO_EXTLINK_ENABLED "
+ default:
+ preferExternal := len(preferlinkext) != 0
+ if preferExternal && ctxt.Debugvlog > 0 {
+ ctxt.Logf("external linking prefer list is %v\n", preferlinkext)
+ }
+ if extNeeded || (iscgo && (externalobj || preferExternal)) {
+ ctxt.LinkMode = LinkExternal
+ } else {
+ ctxt.LinkMode = LinkInternal
+ }
+ }
+ }
+
+ switch ctxt.LinkMode {
+ case LinkInternal:
+ if extNeeded {
+ Exitf("internal linking requested %sbut external linking required: %s", via, extReason)
+ }
+ case LinkExternal:
+ switch {
+ case buildcfg.GOARCH == "ppc64" && buildcfg.GOOS != "aix":
+ Exitf("external linking not supported for %s/ppc64", buildcfg.GOOS)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
new file mode 100644
index 0000000..afba7d3
--- /dev/null
+++ b/src/cmd/link/internal/ld/data.go
@@ -0,0 +1,2956 @@
+// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "bytes"
+ "cmd/internal/gcprog"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/loadpe"
+ "cmd/link/internal/sym"
+ "compress/zlib"
+ "debug/elf"
+ "encoding/binary"
+ "fmt"
+ "log"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+)
+
+// isRuntimeDepPkg reports whether pkg is the runtime package or its dependency.
+func isRuntimeDepPkg(pkg string) bool {
+ switch pkg {
+ case "runtime",
+ "sync/atomic", // runtime may call to sync/atomic, due to go:linkname
+ "internal/abi", // used by reflectcall (and maybe more)
+ "internal/bytealg", // for IndexByte
+ "internal/cpu": // for cpu features
+ return true
+ }
+ return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test")
+}
+
+// Estimate the max size needed to hold any new trampolines created for this function. This
+// is used to determine when the section can be split if it becomes too large, to ensure that
+// the trampolines are in the same section as the function that uses them.
+func maxSizeTrampolines(ctxt *Link, ldr *loader.Loader, s loader.Sym, isTramp bool) uint64 {
+ // If thearch.Trampoline is nil, then trampoline support is not available on this arch.
+ // A trampoline does not need any dependent trampolines.
+ if thearch.Trampoline == nil || isTramp {
+ return 0
+ }
+
+ n := uint64(0)
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Type().IsDirectCallOrJump() {
+ n++
+ }
+ }
+
+ if ctxt.IsARM() {
+ return n * 20 // Trampolines in ARM range from 3 to 5 instructions.
+ }
+ if ctxt.IsPPC64() {
+ return n * 16 // Trampolines in PPC64 are 4 instructions.
+ }
+ if ctxt.IsARM64() {
+ return n * 12 // Trampolines in ARM64 are 3 instructions.
+ }
+ panic("unreachable")
+}
+
+// Detect too-far jumps in function s, and add trampolines if necessary.
+// ARM, PPC64, PPC64LE and RISCV64 support trampoline insertion for internal
+// and external linking. On PPC64 and PPC64LE the text sections might be split
+// but will still insert trampolines where necessary.
+func trampoline(ctxt *Link, s loader.Sym) {
+ if thearch.Trampoline == nil {
+ return // no need or no support of trampolines on this arch
+ }
+
+ ldr := ctxt.loader
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rt := r.Type()
+ if !rt.IsDirectCallOrJump() && !isPLTCall(rt) {
+ continue
+ }
+ rs := r.Sym()
+ if !ldr.AttrReachable(rs) || ldr.SymType(rs) == sym.Sxxx {
+ continue // something is wrong. skip it here and we'll emit a better error later
+ }
+
+ // RISC-V is only able to reach +/-1MiB via a JAL instruction,
+ // which we can readily exceed in the same package. As such, we
+ // need to generate trampolines when the address is unknown.
+ if ldr.SymValue(rs) == 0 && !ctxt.Target.IsRISCV64() && ldr.SymType(rs) != sym.SDYNIMPORT && ldr.SymType(rs) != sym.SUNDEFEXT {
+ if ldr.SymPkg(s) != "" && ldr.SymPkg(rs) == ldr.SymPkg(s) {
+ // Symbols in the same package are laid out together.
+ // Except that if SymPkg(s) == "", it is a host object symbol
+ // which may call an external symbol via PLT.
+ continue
+ }
+ if isRuntimeDepPkg(ldr.SymPkg(s)) && isRuntimeDepPkg(ldr.SymPkg(rs)) {
+ continue // runtime packages are laid out together
+ }
+ }
+ thearch.Trampoline(ctxt, ldr, ri, rs, s)
+ }
+}
+
+// whether rt is a (host object) relocation that will be turned into
+// a call to PLT.
+func isPLTCall(rt objabi.RelocType) bool {
+ const pcrel = 1
+ switch rt {
+ // ARM64
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26),
+ objabi.MachoRelocOffset + MACHO_ARM64_RELOC_BRANCH26*2 + pcrel:
+ return true
+
+ // ARM
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24):
+ return true
+ }
+ // TODO: other architectures.
+ return false
+}
+
+// FoldSubSymbolOffset computes the offset of symbol s to its top-level outer
+// symbol. Returns the top-level symbol and the offset.
+// This is used in generating external relocations.
+func FoldSubSymbolOffset(ldr *loader.Loader, s loader.Sym) (loader.Sym, int64) {
+ outer := ldr.OuterSym(s)
+ off := int64(0)
+ if outer != 0 {
+ off += ldr.SymValue(s) - ldr.SymValue(outer)
+ s = outer
+ }
+ return s, off
+}
+
+// relocsym resolve relocations in "s", updating the symbol's content
+// in "P".
+// The main loop walks through the list of relocations attached to "s"
+// and resolves them where applicable. Relocations are often
+// architecture-specific, requiring calls into the 'archreloc' and/or
+// 'archrelocvariant' functions for the architecture. When external
+// linking is in effect, it may not be possible to completely resolve
+// the address/offset for a symbol, in which case the goal is to lay
+// the groundwork for turning a given relocation into an external reloc
+// (to be applied by the external linker). For more on how relocations
+// work in general, see
+//
+// "Linkers and Loaders", by John R. Levine (Morgan Kaufmann, 1999), ch. 7
+//
+// This is a performance-critical function for the linker; be careful
+// to avoid introducing unnecessary allocations in the main loop.
+func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
+ ldr := st.ldr
+ relocs := ldr.Relocs(s)
+ if relocs.Count() == 0 {
+ return
+ }
+ target := st.target
+ syms := st.syms
+ nExtReloc := 0 // number of external relocations
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ off := r.Off()
+ siz := int32(r.Siz())
+ rs := r.Sym()
+ rt := r.Type()
+ weak := r.Weak()
+ if off < 0 || off+siz > int32(len(P)) {
+ rname := ""
+ if rs != 0 {
+ rname = ldr.SymName(rs)
+ }
+ st.err.Errorf(s, "invalid relocation %s: %d+%d not in [%d,%d)", rname, off, siz, 0, len(P))
+ continue
+ }
+ if siz == 0 { // informational relocation - no work to do
+ continue
+ }
+
+ var rst sym.SymKind
+ if rs != 0 {
+ rst = ldr.SymType(rs)
+ }
+
+ if rs != 0 && (rst == sym.Sxxx || rst == sym.SXREF) {
+ // When putting the runtime but not main into a shared library
+ // these symbols are undefined and that's OK.
+ if target.IsShared() || target.IsPlugin() {
+ if ldr.SymName(rs) == "main.main" || (!target.IsPlugin() && ldr.SymName(rs) == "main..inittask") {
+ sb := ldr.MakeSymbolUpdater(rs)
+ sb.SetType(sym.SDYNIMPORT)
+ } else if strings.HasPrefix(ldr.SymName(rs), "go:info.") {
+ // Skip go.info symbols. They are only needed to communicate
+ // DWARF info between the compiler and linker.
+ continue
+ }
+ } else if target.IsPPC64() && ldr.SymName(rs) == ".TOC." {
+ // TOC symbol doesn't have a type but we do assign a value
+ // (see the address pass) and we can resolve it.
+ // TODO: give it a type.
+ } else {
+ st.err.errorUnresolved(ldr, s, rs)
+ continue
+ }
+ }
+
+ if rt >= objabi.ElfRelocOffset {
+ continue
+ }
+
+ // We need to be able to reference dynimport symbols when linking against
+ // shared libraries, and AIX, Darwin, OpenBSD and Solaris always need it.
+ if !target.IsAIX() && !target.IsDarwin() && !target.IsSolaris() && !target.IsOpenbsd() && rs != 0 && rst == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !ldr.AttrSubSymbol(rs) {
+ if !(target.IsPPC64() && target.IsExternal() && ldr.SymName(rs) == ".TOC.") {
+ st.err.Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", ldr.SymName(rs), rst, rst, rt, sym.RelocName(target.Arch, rt))
+ }
+ }
+ if rs != 0 && rst != sym.STLSBSS && !weak && rt != objabi.R_METHODOFF && !ldr.AttrReachable(rs) {
+ st.err.Errorf(s, "unreachable sym in relocation: %s", ldr.SymName(rs))
+ }
+
+ var rv sym.RelocVariant
+ if target.IsPPC64() || target.IsS390X() {
+ rv = ldr.RelocVariant(s, ri)
+ }
+
+ // TODO(mundaym): remove this special case - see issue 14218.
+ if target.IsS390X() {
+ switch rt {
+ case objabi.R_PCRELDBL:
+ rt = objabi.R_PCREL
+ rv = sym.RV_390_DBL
+ case objabi.R_CALL:
+ rv = sym.RV_390_DBL
+ }
+ }
+
+ var o int64
+ switch rt {
+ default:
+ switch siz {
+ default:
+ st.err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs))
+ case 1:
+ o = int64(P[off])
+ case 2:
+ o = int64(target.Arch.ByteOrder.Uint16(P[off:]))
+ case 4:
+ o = int64(target.Arch.ByteOrder.Uint32(P[off:]))
+ case 8:
+ o = int64(target.Arch.ByteOrder.Uint64(P[off:]))
+ }
+ out, n, ok := thearch.Archreloc(target, ldr, syms, r, s, o)
+ if target.IsExternal() {
+ nExtReloc += n
+ }
+ if ok {
+ o = out
+ } else {
+ st.err.Errorf(s, "unknown reloc to %v: %d (%s)", ldr.SymName(rs), rt, sym.RelocName(target.Arch, rt))
+ }
+ case objabi.R_TLS_LE:
+ if target.IsExternal() && target.IsElf() {
+ nExtReloc++
+ o = 0
+ if !target.IsAMD64() {
+ o = r.Add()
+ }
+ break
+ }
+
+ if target.IsElf() && target.IsARM() {
+ // On ELF ARM, the thread pointer is 8 bytes before
+ // the start of the thread-local data block, so add 8
+ // to the actual TLS offset (r->sym->value).
+ // This 8 seems to be a fundamental constant of
+ // ELF on ARM (or maybe Glibc on ARM); it is not
+ // related to the fact that our own TLS storage happens
+ // to take up 8 bytes.
+ o = 8 + ldr.SymValue(rs)
+ } else if target.IsElf() || target.IsPlan9() || target.IsDarwin() {
+ o = int64(syms.Tlsoffset) + r.Add()
+ } else if target.IsWindows() {
+ o = r.Add()
+ } else {
+ log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType)
+ }
+ case objabi.R_TLS_IE:
+ if target.IsExternal() && target.IsElf() {
+ nExtReloc++
+ o = 0
+ if !target.IsAMD64() {
+ o = r.Add()
+ }
+ if target.Is386() {
+ nExtReloc++ // need two ELF relocations on 386, see ../x86/asm.go:elfreloc1
+ }
+ break
+ }
+ if target.IsPIE() && target.IsElf() {
+ // We are linking the final executable, so we
+ // can optimize any TLS IE relocation to LE.
+ if thearch.TLSIEtoLE == nil {
+ log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family)
+ }
+ thearch.TLSIEtoLE(P, int(off), int(siz))
+ o = int64(syms.Tlsoffset)
+ } else {
+ log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", ldr.SymName(s))
+ }
+ case objabi.R_ADDR:
+ if weak && !ldr.AttrReachable(rs) {
+ // Redirect it to runtime.unreachableMethod, which will throw if called.
+ rs = syms.unreachableMethod
+ }
+ if target.IsExternal() {
+ nExtReloc++
+
+ // set up addend for eventual relocation via outer symbol.
+ rs := rs
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ xadd := r.Add() + off
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil {
+ st.err.Errorf(s, "missing section for relocation target %s", ldr.SymName(rs))
+ }
+
+ o = xadd
+ if target.IsElf() {
+ if target.IsAMD64() {
+ o = 0
+ }
+ } else if target.IsDarwin() {
+ if ldr.SymType(rs) != sym.SHOSTOBJ {
+ o += ldr.SymValue(rs)
+ }
+ } else if target.IsWindows() {
+ // nothing to do
+ } else if target.IsAIX() {
+ o = ldr.SymValue(rs) + xadd
+ } else {
+ st.err.Errorf(s, "unhandled pcrel relocation to %s on %v", ldr.SymName(rs), target.HeadType)
+ }
+
+ break
+ }
+
+ // On AIX, a second relocation must be done by the loader,
+ // as section addresses can change once loaded.
+ // The "default" symbol address is still needed by the loader so
+ // the current relocation can't be skipped.
+ if target.IsAIX() && rst != sym.SDYNIMPORT {
+ // It's not possible to make a loader relocation in a
+ // symbol which is not inside .data section.
+ // FIXME: It should be forbidden to have R_ADDR from a
+ // symbol which isn't in .data. However, as .text has the
+ // same address once loaded, this is possible.
+ if ldr.SymSect(s).Seg == &Segdata {
+ Xcoffadddynrel(target, ldr, syms, s, r, ri)
+ }
+ }
+
+ o = ldr.SymValue(rs) + r.Add()
+
+ // On amd64, 4-byte offsets will be sign-extended, so it is impossible to
+ // access more than 2GB of static data; fail at link time is better than
+ // fail at runtime. See https://golang.org/issue/7980.
+ // Instead of special casing only amd64, we treat this as an error on all
+ // 64-bit architectures so as to be future-proof.
+ if int32(o) < 0 && target.Arch.PtrSize > 4 && siz == 4 {
+ st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", ldr.SymName(rs), uint64(o), ldr.SymValue(rs), r.Add())
+ errorexit()
+ }
+ case objabi.R_DWARFSECREF:
+ if ldr.SymSect(rs) == nil {
+ st.err.Errorf(s, "missing DWARF section for relocation target %s", ldr.SymName(rs))
+ }
+
+ if target.IsExternal() {
+ // On most platforms, the external linker needs to adjust DWARF references
+ // as it combines DWARF sections. However, on Darwin, dsymutil does the
+ // DWARF linking, and it understands how to follow section offsets.
+ // Leaving in the relocation records confuses it (see
+ // https://golang.org/issue/22068) so drop them for Darwin.
+ if !target.IsDarwin() {
+ nExtReloc++
+ }
+
+ xadd := r.Add() + ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr)
+
+ o = xadd
+ if target.IsElf() && target.IsAMD64() {
+ o = 0
+ }
+ break
+ }
+ o = ldr.SymValue(rs) + r.Add() - int64(ldr.SymSect(rs).Vaddr)
+ case objabi.R_METHODOFF:
+ if !ldr.AttrReachable(rs) {
+ // Set it to a sentinel value. The runtime knows this is not pointing to
+ // anything valid.
+ o = -1
+ break
+ }
+ fallthrough
+ case objabi.R_ADDROFF:
+ if weak && !ldr.AttrReachable(rs) {
+ continue
+ }
+ if ldr.SymSect(rs) == nil {
+ st.err.Errorf(s, "unreachable sym in relocation: %s", ldr.SymName(rs))
+ continue
+ }
+
+ // The method offset tables using this relocation expect the offset to be relative
+ // to the start of the first text section, even if there are multiple.
+ if ldr.SymSect(rs).Name == ".text" {
+ o = ldr.SymValue(rs) - int64(Segtext.Sections[0].Vaddr) + r.Add()
+ } else {
+ o = ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + r.Add()
+ }
+
+ case objabi.R_ADDRCUOFF:
+ // debug_range and debug_loc elements use this relocation type to get an
+ // offset from the start of the compile unit.
+ o = ldr.SymValue(rs) + r.Add() - ldr.SymValue(loader.Sym(ldr.SymUnit(rs).Textp[0]))
+
+ // r.Sym() can be 0 when CALL $(constant) is transformed from absolute PC to relative PC call.
+ case objabi.R_GOTPCREL:
+ if target.IsDynlinkingGo() && target.IsDarwin() && rs != 0 {
+ nExtReloc++
+ o = r.Add()
+ break
+ }
+ if target.Is386() && target.IsExternal() && target.IsELF {
+ nExtReloc++ // need two ELF relocations on 386, see ../x86/asm.go:elfreloc1
+ }
+ fallthrough
+ case objabi.R_CALL, objabi.R_PCREL:
+ if target.IsExternal() && rs != 0 && rst == sym.SUNDEFEXT {
+ // pass through to the external linker.
+ nExtReloc++
+ o = 0
+ break
+ }
+ if target.IsExternal() && rs != 0 && (ldr.SymSect(rs) != ldr.SymSect(s) || rt == objabi.R_GOTPCREL) {
+ nExtReloc++
+
+ // set up addend for eventual relocation via outer symbol.
+ rs := rs
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ xadd := r.Add() + off - int64(siz) // relative to address after the relocated chunk
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil {
+ st.err.Errorf(s, "missing section for relocation target %s", ldr.SymName(rs))
+ }
+
+ o = xadd
+ if target.IsElf() {
+ if target.IsAMD64() {
+ o = 0
+ }
+ } else if target.IsDarwin() {
+ if rt == objabi.R_CALL {
+ if target.IsExternal() && rst == sym.SDYNIMPORT {
+ if target.IsAMD64() {
+ // AMD64 dynamic relocations are relative to the end of the relocation.
+ o += int64(siz)
+ }
+ } else {
+ if rst != sym.SHOSTOBJ {
+ o += int64(uint64(ldr.SymValue(rs)) - ldr.SymSect(rs).Vaddr)
+ }
+ o -= int64(off) // relative to section offset, not symbol
+ }
+ } else {
+ o += int64(siz)
+ }
+ } else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL
+ // PE/COFF's PC32 relocation uses the address after the relocated
+ // bytes as the base. Compensate by skewing the addend.
+ o += int64(siz)
+ } else {
+ st.err.Errorf(s, "unhandled pcrel relocation to %s on %v", ldr.SymName(rs), target.HeadType)
+ }
+
+ break
+ }
+
+ o = 0
+ if rs != 0 {
+ o = ldr.SymValue(rs)
+ }
+
+ o += r.Add() - (ldr.SymValue(s) + int64(off) + int64(siz))
+ case objabi.R_SIZE:
+ o = ldr.SymSize(rs) + r.Add()
+
+ case objabi.R_XCOFFREF:
+ if !target.IsAIX() {
+ st.err.Errorf(s, "find XCOFF R_REF on non-XCOFF files")
+ }
+ if !target.IsExternal() {
+ st.err.Errorf(s, "find XCOFF R_REF with internal linking")
+ }
+ nExtReloc++
+ continue
+
+ case objabi.R_DWARFFILEREF:
+ // We don't renumber files in dwarf.go:writelines anymore.
+ continue
+
+ case objabi.R_CONST:
+ o = r.Add()
+
+ case objabi.R_GOTOFF:
+ o = ldr.SymValue(rs) + r.Add() - ldr.SymValue(syms.GOT)
+ }
+
+ if target.IsPPC64() || target.IsS390X() {
+ if rv != sym.RV_NONE {
+ o = thearch.Archrelocvariant(target, ldr, r, rv, s, o, P)
+ }
+ }
+
+ switch siz {
+ default:
+ st.err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs))
+ case 1:
+ P[off] = byte(int8(o))
+ case 2:
+ if o != int64(int16(o)) {
+ st.err.Errorf(s, "relocation address for %s is too big: %#x", ldr.SymName(rs), o)
+ }
+ target.Arch.ByteOrder.PutUint16(P[off:], uint16(o))
+ case 4:
+ if rt == objabi.R_PCREL || rt == objabi.R_CALL {
+ if o != int64(int32(o)) {
+ st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o)
+ }
+ } else {
+ if o != int64(int32(o)) && o != int64(uint32(o)) {
+ st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o))
+ }
+ }
+ target.Arch.ByteOrder.PutUint32(P[off:], uint32(o))
+ case 8:
+ target.Arch.ByteOrder.PutUint64(P[off:], uint64(o))
+ }
+ }
+ if target.IsExternal() {
+ // We'll stream out the external relocations in asmb2 (e.g. elfrelocsect)
+ // and we only need the count here.
+ atomic.AddUint32(&ldr.SymSect(s).Relcount, uint32(nExtReloc))
+ }
+}
+
+// Convert a Go relocation to an external relocation.
+func extreloc(ctxt *Link, ldr *loader.Loader, s loader.Sym, r loader.Reloc) (loader.ExtReloc, bool) {
+ var rr loader.ExtReloc
+ target := &ctxt.Target
+ siz := int32(r.Siz())
+ if siz == 0 { // informational relocation - no work to do
+ return rr, false
+ }
+
+ rt := r.Type()
+ if rt >= objabi.ElfRelocOffset {
+ return rr, false
+ }
+ rr.Type = rt
+ rr.Size = uint8(siz)
+
+ // TODO(mundaym): remove this special case - see issue 14218.
+ if target.IsS390X() {
+ switch rt {
+ case objabi.R_PCRELDBL:
+ rt = objabi.R_PCREL
+ }
+ }
+
+ switch rt {
+ default:
+ return thearch.Extreloc(target, ldr, r, s)
+
+ case objabi.R_TLS_LE, objabi.R_TLS_IE:
+ if target.IsElf() {
+ rs := r.Sym()
+ rr.Xsym = rs
+ if rr.Xsym == 0 {
+ rr.Xsym = ctxt.Tlsg
+ }
+ rr.Xadd = r.Add()
+ break
+ }
+ return rr, false
+
+ case objabi.R_ADDR:
+ // set up addend for eventual relocation via outer symbol.
+ rs := r.Sym()
+ if r.Weak() && !ldr.AttrReachable(rs) {
+ rs = ctxt.ArchSyms.unreachableMethod
+ }
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ rr.Xadd = r.Add() + off
+ rr.Xsym = rs
+
+ case objabi.R_DWARFSECREF:
+ // On most platforms, the external linker needs to adjust DWARF references
+ // as it combines DWARF sections. However, on Darwin, dsymutil does the
+ // DWARF linking, and it understands how to follow section offsets.
+ // Leaving in the relocation records confuses it (see
+ // https://golang.org/issue/22068) so drop them for Darwin.
+ if target.IsDarwin() {
+ return rr, false
+ }
+ rs := r.Sym()
+ rr.Xsym = loader.Sym(ldr.SymSect(rs).Sym)
+ rr.Xadd = r.Add() + ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr)
+
+ // r.Sym() can be 0 when CALL $(constant) is transformed from absolute PC to relative PC call.
+ case objabi.R_GOTPCREL, objabi.R_CALL, objabi.R_PCREL:
+ rs := r.Sym()
+ if rt == objabi.R_GOTPCREL && target.IsDynlinkingGo() && target.IsDarwin() && rs != 0 {
+ rr.Xadd = r.Add()
+ rr.Xadd -= int64(siz) // relative to address after the relocated chunk
+ rr.Xsym = rs
+ break
+ }
+ if rs != 0 && ldr.SymType(rs) == sym.SUNDEFEXT {
+ // pass through to the external linker.
+ rr.Xadd = 0
+ if target.IsElf() {
+ rr.Xadd -= int64(siz)
+ }
+ rr.Xsym = rs
+ break
+ }
+ if rs != 0 && (ldr.SymSect(rs) != ldr.SymSect(s) || rt == objabi.R_GOTPCREL) {
+ // set up addend for eventual relocation via outer symbol.
+ rs := rs
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ rr.Xadd = r.Add() + off
+ rr.Xadd -= int64(siz) // relative to address after the relocated chunk
+ rr.Xsym = rs
+ break
+ }
+ return rr, false
+
+ case objabi.R_XCOFFREF:
+ return ExtrelocSimple(ldr, r), true
+
+ // These reloc types don't need external relocations.
+ case objabi.R_ADDROFF, objabi.R_METHODOFF, objabi.R_ADDRCUOFF,
+ objabi.R_SIZE, objabi.R_CONST, objabi.R_GOTOFF:
+ return rr, false
+ }
+ return rr, true
+}
+
+// ExtrelocSimple creates a simple external relocation from r, with the same
+// symbol and addend.
+func ExtrelocSimple(ldr *loader.Loader, r loader.Reloc) loader.ExtReloc {
+ var rr loader.ExtReloc
+ rs := r.Sym()
+ rr.Xsym = rs
+ rr.Xadd = r.Add()
+ rr.Type = r.Type()
+ rr.Size = r.Siz()
+ return rr
+}
+
+// ExtrelocViaOuterSym creates an external relocation from r targeting the
+// outer symbol and folding the subsymbol's offset into the addend.
+func ExtrelocViaOuterSym(ldr *loader.Loader, r loader.Reloc, s loader.Sym) loader.ExtReloc {
+ // set up addend for eventual relocation via outer symbol.
+ var rr loader.ExtReloc
+ rs := r.Sym()
+ rs, off := FoldSubSymbolOffset(ldr, rs)
+ rr.Xadd = r.Add() + off
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil {
+ ldr.Errorf(s, "missing section for %s", ldr.SymName(rs))
+ }
+ rr.Xsym = rs
+ rr.Type = r.Type()
+ rr.Size = r.Siz()
+ return rr
+}
+
+// relocSymState hold state information needed when making a series of
+// successive calls to relocsym(). The items here are invariant
+// (meaning that they are set up once initially and then don't change
+// during the execution of relocsym), with the exception of a slice
+// used to facilitate batch allocation of external relocations. Calls
+// to relocsym happen in parallel; the assumption is that each
+// parallel thread will have its own state object.
+type relocSymState struct {
+ target *Target
+ ldr *loader.Loader
+ err *ErrorReporter
+ syms *ArchSyms
+}
+
+// makeRelocSymState creates a relocSymState container object to
+// pass to relocsym(). If relocsym() calls happen in parallel,
+// each parallel thread should have its own state object.
+func (ctxt *Link) makeRelocSymState() *relocSymState {
+ return &relocSymState{
+ target: &ctxt.Target,
+ ldr: ctxt.loader,
+ err: &ctxt.ErrorReporter,
+ syms: &ctxt.ArchSyms,
+ }
+}
+
+// windynrelocsym examines a text symbol 's' and looks for relocations
+// from it that correspond to references to symbols defined in DLLs,
+// then fixes up those relocations as needed. A reference to a symbol
+// XYZ from some DLL will fall into one of two categories: an indirect
+// ref via "__imp_XYZ", or a direct ref to "XYZ". Here's an example of
+// an indirect ref (this is an excerpt from objdump -ldr):
+//
+// 1c1: 48 89 c6 movq %rax, %rsi
+// 1c4: ff 15 00 00 00 00 callq *(%rip)
+// 00000000000001c6: IMAGE_REL_AMD64_REL32 __imp__errno
+//
+// In the assembly above, the code loads up the value of __imp_errno
+// and then does an indirect call to that value.
+//
+// Here is what a direct reference might look like:
+//
+// 137: e9 20 06 00 00 jmp 0x75c <pow+0x75c>
+// 13c: e8 00 00 00 00 callq 0x141 <pow+0x141>
+// 000000000000013d: IMAGE_REL_AMD64_REL32 _errno
+//
+// The assembly below dispenses with the import symbol and just makes
+// a direct call to _errno.
+//
+// The code below handles indirect refs by redirecting the target of
+// the relocation from "__imp_XYZ" to "XYZ" (since the latter symbol
+// is what the Windows loader is expected to resolve). For direct refs
+// the call is redirected to a stub, where the stub first loads the
+// symbol and then direct an indirect call to that value.
+//
+// Note that for a given symbol (as above) it is perfectly legal to
+// have both direct and indirect references.
+func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) error {
+ var su *loader.SymbolBuilder
+ relocs := ctxt.loader.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.IsMarker() {
+ continue // skip marker relocations
+ }
+ targ := r.Sym()
+ if targ == 0 {
+ continue
+ }
+ if !ctxt.loader.AttrReachable(targ) {
+ if r.Weak() {
+ continue
+ }
+ return fmt.Errorf("dynamic relocation to unreachable symbol %s",
+ ctxt.loader.SymName(targ))
+ }
+ tgot := ctxt.loader.SymGot(targ)
+ if tgot == loadpe.RedirectToDynImportGotToken {
+
+ // Consistency check: name should be __imp_X
+ sname := ctxt.loader.SymName(targ)
+ if !strings.HasPrefix(sname, "__imp_") {
+ return fmt.Errorf("internal error in windynrelocsym: redirect GOT token applied to non-import symbol %s", sname)
+ }
+
+ // Locate underlying symbol (which originally had type
+ // SDYNIMPORT but has since been retyped to SWINDOWS).
+ ds, err := loadpe.LookupBaseFromImport(targ, ctxt.loader, ctxt.Arch)
+ if err != nil {
+ return err
+ }
+ dstyp := ctxt.loader.SymType(ds)
+ if dstyp != sym.SWINDOWS {
+ return fmt.Errorf("internal error in windynrelocsym: underlying sym for %q has wrong type %s", sname, dstyp.String())
+ }
+
+ // Redirect relocation to the dynimport.
+ r.SetSym(ds)
+ continue
+ }
+
+ tplt := ctxt.loader.SymPlt(targ)
+ if tplt == loadpe.CreateImportStubPltToken {
+
+ // Consistency check: don't want to see both PLT and GOT tokens.
+ if tgot != -1 {
+ return fmt.Errorf("internal error in windynrelocsym: invalid GOT setting %d for reloc to %s", tgot, ctxt.loader.SymName(targ))
+ }
+
+ // make dynimport JMP table for PE object files.
+ tplt := int32(rel.Size())
+ ctxt.loader.SetPlt(targ, tplt)
+
+ if su == nil {
+ su = ctxt.loader.MakeSymbolUpdater(s)
+ }
+ r.SetSym(rel.Sym())
+ r.SetAdd(int64(tplt))
+
+ // jmp *addr
+ switch ctxt.Arch.Family {
+ default:
+ return fmt.Errorf("internal error in windynrelocsym: unsupported arch %v", ctxt.Arch.Family)
+ case sys.I386:
+ rel.AddUint8(0xff)
+ rel.AddUint8(0x25)
+ rel.AddAddrPlus(ctxt.Arch, targ, 0)
+ rel.AddUint8(0x90)
+ rel.AddUint8(0x90)
+ case sys.AMD64:
+ rel.AddUint8(0xff)
+ rel.AddUint8(0x24)
+ rel.AddUint8(0x25)
+ rel.AddAddrPlus4(ctxt.Arch, targ, 0)
+ rel.AddUint8(0x90)
+ }
+ } else if tplt >= 0 {
+ if su == nil {
+ su = ctxt.loader.MakeSymbolUpdater(s)
+ }
+ r.SetSym(rel.Sym())
+ r.SetAdd(int64(tplt))
+ }
+ }
+ return nil
+}
+
+// windynrelocsyms generates jump table to C library functions that will be
+// added later. windynrelocsyms writes the table into .rel symbol.
+func (ctxt *Link) windynrelocsyms() {
+ if !(ctxt.IsWindows() && iscgo && ctxt.IsInternal()) {
+ return
+ }
+
+ rel := ctxt.loader.CreateSymForUpdate(".rel", 0)
+ rel.SetType(sym.STEXT)
+
+ for _, s := range ctxt.Textp {
+ if err := windynrelocsym(ctxt, rel, s); err != nil {
+ ctxt.Errorf(s, "%v", err)
+ }
+ }
+
+ ctxt.Textp = append(ctxt.Textp, rel.Sym())
+}
+
+func dynrelocsym(ctxt *Link, s loader.Sym) {
+ target := &ctxt.Target
+ ldr := ctxt.loader
+ syms := &ctxt.ArchSyms
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.IsMarker() {
+ continue // skip marker relocations
+ }
+ rSym := r.Sym()
+ if r.Weak() && !ldr.AttrReachable(rSym) {
+ continue
+ }
+ if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal {
+ // It's expected that some relocations will be done
+ // later by relocsym (R_TLS_LE, R_ADDROFF), so
+ // don't worry if Adddynrel returns false.
+ thearch.Adddynrel(target, ldr, syms, s, r, ri)
+ continue
+ }
+
+ if rSym != 0 && ldr.SymType(rSym) == sym.SDYNIMPORT || r.Type() >= objabi.ElfRelocOffset {
+ if rSym != 0 && !ldr.AttrReachable(rSym) {
+ ctxt.Errorf(s, "dynamic relocation to unreachable symbol %s", ldr.SymName(rSym))
+ }
+ if !thearch.Adddynrel(target, ldr, syms, s, r, ri) {
+ ctxt.Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d (%s) stype=%d (%s))", ldr.SymName(rSym), r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymType(rSym), ldr.SymType(rSym))
+ }
+ }
+ }
+}
+
+func (state *dodataState) dynreloc(ctxt *Link) {
+ if ctxt.HeadType == objabi.Hwindows {
+ return
+ }
+ // -d suppresses dynamic loader format, so we may as well not
+ // compute these sections or mark their symbols as reachable.
+ if *FlagD {
+ return
+ }
+
+ for _, s := range ctxt.Textp {
+ dynrelocsym(ctxt, s)
+ }
+ for _, syms := range state.data {
+ for _, s := range syms {
+ dynrelocsym(ctxt, s)
+ }
+ }
+ if ctxt.IsELF {
+ elfdynhash(ctxt)
+ }
+}
+
+func CodeblkPad(ctxt *Link, out *OutBuf, addr int64, size int64, pad []byte) {
+ writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, ctxt.Textp, addr, size, pad)
+}
+
+const blockSize = 1 << 20 // 1MB chunks written at a time.
+
+// writeBlocks writes a specified chunk of symbols to the output buffer. It
+// breaks the write up into ≥blockSize chunks to write them out, and schedules
+// as many goroutines as necessary to accomplish this task. This call then
+// blocks, waiting on the writes to complete. Note that we use the sem parameter
+// to limit the number of concurrent writes taking place.
+func writeBlocks(ctxt *Link, out *OutBuf, sem chan int, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) {
+ for i, s := range syms {
+ if ldr.SymValue(s) >= addr && !ldr.AttrSubSymbol(s) {
+ syms = syms[i:]
+ break
+ }
+ }
+
+ var wg sync.WaitGroup
+ max, lastAddr, written := int64(blockSize), addr+size, int64(0)
+ for addr < lastAddr {
+ // Find the last symbol we'd write.
+ idx := -1
+ for i, s := range syms {
+ if ldr.AttrSubSymbol(s) {
+ continue
+ }
+
+ // If the next symbol's size would put us out of bounds on the total length,
+ // stop looking.
+ end := ldr.SymValue(s) + ldr.SymSize(s)
+ if end > lastAddr {
+ break
+ }
+
+ // We're gonna write this symbol.
+ idx = i
+
+ // If we cross over the max size, we've got enough symbols.
+ if end > addr+max {
+ break
+ }
+ }
+
+ // If we didn't find any symbols to write, we're done here.
+ if idx < 0 {
+ break
+ }
+
+ // Compute the length to write, including padding.
+ // We need to write to the end address (lastAddr), or the next symbol's
+ // start address, whichever comes first. If there is no more symbols,
+ // just write to lastAddr. This ensures we don't leave holes between the
+ // blocks or at the end.
+ length := int64(0)
+ if idx+1 < len(syms) {
+ // Find the next top-level symbol.
+ // Skip over sub symbols so we won't split a container symbol
+ // into two blocks.
+ next := syms[idx+1]
+ for ldr.AttrSubSymbol(next) {
+ idx++
+ next = syms[idx+1]
+ }
+ length = ldr.SymValue(next) - addr
+ }
+ if length == 0 || length > lastAddr-addr {
+ length = lastAddr - addr
+ }
+
+ // Start the block output operator.
+ if o, err := out.View(uint64(out.Offset() + written)); err == nil {
+ sem <- 1
+ wg.Add(1)
+ go func(o *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) {
+ writeBlock(ctxt, o, ldr, syms, addr, size, pad)
+ wg.Done()
+ <-sem
+ }(o, ldr, syms, addr, length, pad)
+ } else { // output not mmaped, don't parallelize.
+ writeBlock(ctxt, out, ldr, syms, addr, length, pad)
+ }
+
+ // Prepare for the next loop.
+ if idx != -1 {
+ syms = syms[idx+1:]
+ }
+ written += length
+ addr += length
+ }
+ wg.Wait()
+}
+
+func writeBlock(ctxt *Link, out *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) {
+
+ st := ctxt.makeRelocSymState()
+
+ // This doesn't distinguish the memory size from the file
+ // size, and it lays out the file based on Symbol.Value, which
+ // is the virtual address. DWARF compression changes file sizes,
+ // so dwarfcompress will fix this up later if necessary.
+ eaddr := addr + size
+ for _, s := range syms {
+ if ldr.AttrSubSymbol(s) {
+ continue
+ }
+ val := ldr.SymValue(s)
+ if val >= eaddr {
+ break
+ }
+ if val < addr {
+ ldr.Errorf(s, "phase error: addr=%#x but sym=%#x type=%v sect=%v", addr, val, ldr.SymType(s), ldr.SymSect(s).Name)
+ errorexit()
+ }
+ if addr < val {
+ out.WriteStringPad("", int(val-addr), pad)
+ addr = val
+ }
+ P := out.WriteSym(ldr, s)
+ st.relocsym(s, P)
+ if f, ok := ctxt.generatorSyms[s]; ok {
+ f(ctxt, s)
+ }
+ addr += int64(len(P))
+ siz := ldr.SymSize(s)
+ if addr < val+siz {
+ out.WriteStringPad("", int(val+siz-addr), pad)
+ addr = val + siz
+ }
+ if addr != val+siz {
+ ldr.Errorf(s, "phase error: addr=%#x value+size=%#x", addr, val+siz)
+ errorexit()
+ }
+ if val+siz >= eaddr {
+ break
+ }
+ }
+
+ if addr < eaddr {
+ out.WriteStringPad("", int(eaddr-addr), pad)
+ }
+}
+
+type writeFn func(*Link, *OutBuf, int64, int64)
+
+// writeParallel handles scheduling parallel execution of data write functions.
+func writeParallel(wg *sync.WaitGroup, fn writeFn, ctxt *Link, seek, vaddr, length uint64) {
+ if out, err := ctxt.Out.View(seek); err != nil {
+ ctxt.Out.SeekSet(int64(seek))
+ fn(ctxt, ctxt.Out, int64(vaddr), int64(length))
+ } else {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ fn(ctxt, out, int64(vaddr), int64(length))
+ }()
+ }
+}
+
+func datblk(ctxt *Link, out *OutBuf, addr, size int64) {
+ writeDatblkToOutBuf(ctxt, out, addr, size)
+}
+
+// Used only on Wasm for now.
+func DatblkBytes(ctxt *Link, addr int64, size int64) []byte {
+ buf := make([]byte, size)
+ out := &OutBuf{heap: buf}
+ writeDatblkToOutBuf(ctxt, out, addr, size)
+ return buf
+}
+
+func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) {
+ writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, ctxt.datap, addr, size, zeros[:])
+}
+
+func dwarfblk(ctxt *Link, out *OutBuf, addr int64, size int64) {
+ // Concatenate the section symbol lists into a single list to pass
+ // to writeBlocks.
+ //
+ // NB: ideally we would do a separate writeBlocks call for each
+ // section, but this would run the risk of undoing any file offset
+ // adjustments made during layout.
+ n := 0
+ for i := range dwarfp {
+ n += len(dwarfp[i].syms)
+ }
+ syms := make([]loader.Sym, 0, n)
+ for i := range dwarfp {
+ syms = append(syms, dwarfp[i].syms...)
+ }
+ writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, syms, addr, size, zeros[:])
+}
+
+var covCounterDataStartOff, covCounterDataLen uint64
+
+var zeros [512]byte
+
+var (
+ strdata = make(map[string]string)
+ strnames []string
+)
+
+func addstrdata1(ctxt *Link, arg string) {
+ eq := strings.Index(arg, "=")
+ dot := strings.LastIndex(arg[:eq+1], ".")
+ if eq < 0 || dot < 0 {
+ Exitf("-X flag requires argument of the form importpath.name=value")
+ }
+ pkg := arg[:dot]
+ if ctxt.BuildMode == BuildModePlugin && pkg == "main" {
+ pkg = *flagPluginPath
+ }
+ pkg = objabi.PathToPrefix(pkg)
+ name := pkg + arg[dot:eq]
+ value := arg[eq+1:]
+ if _, ok := strdata[name]; !ok {
+ strnames = append(strnames, name)
+ }
+ strdata[name] = value
+}
+
+// addstrdata sets the initial value of the string variable name to value.
+func addstrdata(arch *sys.Arch, l *loader.Loader, name, value string) {
+ s := l.Lookup(name, 0)
+ if s == 0 {
+ return
+ }
+ if goType := l.SymGoType(s); goType == 0 {
+ return
+ } else if typeName := l.SymName(goType); typeName != "type:string" {
+ Errorf(nil, "%s: cannot set with -X: not a var of type string (%s)", name, typeName)
+ return
+ }
+ if !l.AttrReachable(s) {
+ return // don't bother setting unreachable variable
+ }
+ bld := l.MakeSymbolUpdater(s)
+ if bld.Type() == sym.SBSS {
+ bld.SetType(sym.SDATA)
+ }
+
+ p := fmt.Sprintf("%s.str", name)
+ sbld := l.CreateSymForUpdate(p, 0)
+ sbld.Addstring(value)
+ sbld.SetType(sym.SRODATA)
+
+ // Don't reset the variable's size. String variable usually has size of
+ // 2*PtrSize, but in ASAN build it can be larger due to red zone.
+ // (See issue 56175.)
+ bld.SetData(make([]byte, arch.PtrSize*2))
+ bld.SetReadOnly(false)
+ bld.ResetRelocs()
+ bld.SetAddrPlus(arch, 0, sbld.Sym(), 0)
+ bld.SetUint(arch, int64(arch.PtrSize), uint64(len(value)))
+}
+
+func (ctxt *Link) dostrdata() {
+ for _, name := range strnames {
+ addstrdata(ctxt.Arch, ctxt.loader, name, strdata[name])
+ }
+}
+
+// addgostring adds str, as a Go string value, to s. symname is the name of the
+// symbol used to define the string data and must be unique per linked object.
+func addgostring(ctxt *Link, ldr *loader.Loader, s *loader.SymbolBuilder, symname, str string) {
+ sdata := ldr.CreateSymForUpdate(symname, 0)
+ if sdata.Type() != sym.Sxxx {
+ ctxt.Errorf(s.Sym(), "duplicate symname in addgostring: %s", symname)
+ }
+ sdata.SetLocal(true)
+ sdata.SetType(sym.SRODATA)
+ sdata.SetSize(int64(len(str)))
+ sdata.SetData([]byte(str))
+ s.AddAddr(ctxt.Arch, sdata.Sym())
+ s.AddUint(ctxt.Arch, uint64(len(str)))
+}
+
+func addinitarrdata(ctxt *Link, ldr *loader.Loader, s loader.Sym) {
+ p := ldr.SymName(s) + ".ptr"
+ sp := ldr.CreateSymForUpdate(p, 0)
+ sp.SetType(sym.SINITARR)
+ sp.SetSize(0)
+ sp.SetDuplicateOK(true)
+ sp.AddAddr(ctxt.Arch, s)
+}
+
+// symalign returns the required alignment for the given symbol s.
+func symalign(ldr *loader.Loader, s loader.Sym) int32 {
+ min := int32(thearch.Minalign)
+ align := ldr.SymAlign(s)
+ if align >= min {
+ return align
+ } else if align != 0 {
+ return min
+ }
+ align = int32(thearch.Maxalign)
+ ssz := ldr.SymSize(s)
+ for int64(align) > ssz && align > min {
+ align >>= 1
+ }
+ ldr.SetSymAlign(s, align)
+ return align
+}
+
+func aligndatsize(state *dodataState, datsize int64, s loader.Sym) int64 {
+ return Rnd(datsize, int64(symalign(state.ctxt.loader, s)))
+}
+
+const debugGCProg = false
+
+type GCProg struct {
+ ctxt *Link
+ sym *loader.SymbolBuilder
+ w gcprog.Writer
+}
+
+func (p *GCProg) Init(ctxt *Link, name string) {
+ p.ctxt = ctxt
+ p.sym = ctxt.loader.CreateSymForUpdate(name, 0)
+ p.w.Init(p.writeByte())
+ if debugGCProg {
+ fmt.Fprintf(os.Stderr, "ld: start GCProg %s\n", name)
+ p.w.Debug(os.Stderr)
+ }
+}
+
+func (p *GCProg) writeByte() func(x byte) {
+ return func(x byte) {
+ p.sym.AddUint8(x)
+ }
+}
+
+func (p *GCProg) End(size int64) {
+ p.w.ZeroUntil(size / int64(p.ctxt.Arch.PtrSize))
+ p.w.End()
+ if debugGCProg {
+ fmt.Fprintf(os.Stderr, "ld: end GCProg\n")
+ }
+}
+
+func (p *GCProg) AddSym(s loader.Sym) {
+ ldr := p.ctxt.loader
+ typ := ldr.SymGoType(s)
+
+ // Things without pointers should be in sym.SNOPTRDATA or sym.SNOPTRBSS;
+ // everything we see should have pointers and should therefore have a type.
+ if typ == 0 {
+ switch ldr.SymName(s) {
+ case "runtime.data", "runtime.edata", "runtime.bss", "runtime.ebss":
+ // Ignore special symbols that are sometimes laid out
+ // as real symbols. See comment about dyld on darwin in
+ // the address function.
+ return
+ }
+ p.ctxt.Errorf(p.sym.Sym(), "missing Go type information for global symbol %s: size %d", ldr.SymName(s), ldr.SymSize(s))
+ return
+ }
+
+ ptrsize := int64(p.ctxt.Arch.PtrSize)
+ typData := ldr.Data(typ)
+ nptr := decodetypePtrdata(p.ctxt.Arch, typData) / ptrsize
+
+ if debugGCProg {
+ fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", ldr.SymName(s), ldr.SymValue(s), ldr.SymValue(s)/ptrsize, nptr)
+ }
+
+ sval := ldr.SymValue(s)
+ if decodetypeUsegcprog(p.ctxt.Arch, typData) == 0 {
+ // Copy pointers from mask into program.
+ mask := decodetypeGcmask(p.ctxt, typ)
+ for i := int64(0); i < nptr; i++ {
+ if (mask[i/8]>>uint(i%8))&1 != 0 {
+ p.w.Ptr(sval/ptrsize + i)
+ }
+ }
+ return
+ }
+
+ // Copy program.
+ prog := decodetypeGcprog(p.ctxt, typ)
+ p.w.ZeroUntil(sval / ptrsize)
+ p.w.Append(prog[4:], nptr)
+}
+
+// cutoff is the maximum data section size permitted by the linker
+// (see issue #9862).
+const cutoff = 2e9 // 2 GB (or so; looks better in errors than 2^31)
+
+func (state *dodataState) checkdatsize(symn sym.SymKind) {
+ if state.datsize > cutoff {
+ Errorf(nil, "too much data in section %v (over %v bytes)", symn, cutoff)
+ }
+}
+
+// fixZeroSizedSymbols gives a few special symbols with zero size some space.
+func fixZeroSizedSymbols(ctxt *Link) {
+ // The values in moduledata are filled out by relocations
+ // pointing to the addresses of these special symbols.
+ // Typically these symbols have no size and are not laid
+ // out with their matching section.
+ //
+ // However on darwin, dyld will find the special symbol
+ // in the first loaded module, even though it is local.
+ //
+ // (An hypothesis, formed without looking in the dyld sources:
+ // these special symbols have no size, so their address
+ // matches a real symbol. The dynamic linker assumes we
+ // want the normal symbol with the same address and finds
+ // it in the other module.)
+ //
+ // To work around this we lay out the symbls whose
+ // addresses are vital for multi-module programs to work
+ // as normal symbols, and give them a little size.
+ //
+ // On AIX, as all DATA sections are merged together, ld might not put
+ // these symbols at the beginning of their respective section if there
+ // aren't real symbols, their alignment might not match the
+ // first symbol alignment. Therefore, there are explicitly put at the
+ // beginning of their section with the same alignment.
+ if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+ return
+ }
+
+ ldr := ctxt.loader
+ bss := ldr.CreateSymForUpdate("runtime.bss", 0)
+ bss.SetSize(8)
+ ldr.SetAttrSpecial(bss.Sym(), false)
+
+ ebss := ldr.CreateSymForUpdate("runtime.ebss", 0)
+ ldr.SetAttrSpecial(ebss.Sym(), false)
+
+ data := ldr.CreateSymForUpdate("runtime.data", 0)
+ data.SetSize(8)
+ ldr.SetAttrSpecial(data.Sym(), false)
+
+ edata := ldr.CreateSymForUpdate("runtime.edata", 0)
+ ldr.SetAttrSpecial(edata.Sym(), false)
+
+ if ctxt.HeadType == objabi.Haix {
+ // XCOFFTOC symbols are part of .data section.
+ edata.SetType(sym.SXCOFFTOC)
+ }
+
+ types := ldr.CreateSymForUpdate("runtime.types", 0)
+ types.SetType(sym.STYPE)
+ types.SetSize(8)
+ ldr.SetAttrSpecial(types.Sym(), false)
+
+ etypes := ldr.CreateSymForUpdate("runtime.etypes", 0)
+ etypes.SetType(sym.SFUNCTAB)
+ ldr.SetAttrSpecial(etypes.Sym(), false)
+
+ if ctxt.HeadType == objabi.Haix {
+ rodata := ldr.CreateSymForUpdate("runtime.rodata", 0)
+ rodata.SetType(sym.SSTRING)
+ rodata.SetSize(8)
+ ldr.SetAttrSpecial(rodata.Sym(), false)
+
+ erodata := ldr.CreateSymForUpdate("runtime.erodata", 0)
+ ldr.SetAttrSpecial(erodata.Sym(), false)
+ }
+}
+
+// makeRelroForSharedLib creates a section of readonly data if necessary.
+func (state *dodataState) makeRelroForSharedLib(target *Link) {
+ if !target.UseRelro() {
+ return
+ }
+
+ // "read only" data with relocations needs to go in its own section
+ // when building a shared library. We do this by boosting objects of
+ // type SXXX with relocations to type SXXXRELRO.
+ ldr := target.loader
+ for _, symnro := range sym.ReadOnly {
+ symnrelro := sym.RelROMap[symnro]
+
+ ro := []loader.Sym{}
+ relro := state.data[symnrelro]
+
+ for _, s := range state.data[symnro] {
+ relocs := ldr.Relocs(s)
+ isRelro := relocs.Count() > 0
+ switch state.symType(s) {
+ case sym.STYPE, sym.STYPERELRO, sym.SGOFUNCRELRO:
+ // Symbols are not sorted yet, so it is possible
+ // that an Outer symbol has been changed to a
+ // relro Type before it reaches here.
+ isRelro = true
+ case sym.SFUNCTAB:
+ if ldr.SymName(s) == "runtime.etypes" {
+ // runtime.etypes must be at the end of
+ // the relro data.
+ isRelro = true
+ }
+ case sym.SGOFUNC:
+ // The only SGOFUNC symbols that contain relocations are .stkobj,
+ // and their relocations are of type objabi.R_ADDROFF,
+ // which always get resolved during linking.
+ isRelro = false
+ }
+ if isRelro {
+ state.setSymType(s, symnrelro)
+ if outer := ldr.OuterSym(s); outer != 0 {
+ state.setSymType(outer, symnrelro)
+ }
+ relro = append(relro, s)
+ } else {
+ ro = append(ro, s)
+ }
+ }
+
+ // Check that we haven't made two symbols with the same .Outer into
+ // different types (because references two symbols with non-nil Outer
+ // become references to the outer symbol + offset it's vital that the
+ // symbol and the outer end up in the same section).
+ for _, s := range relro {
+ if outer := ldr.OuterSym(s); outer != 0 {
+ st := state.symType(s)
+ ost := state.symType(outer)
+ if st != ost {
+ state.ctxt.Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)",
+ ldr.SymName(outer), st, ost)
+ }
+ }
+ }
+
+ state.data[symnro] = ro
+ state.data[symnrelro] = relro
+ }
+}
+
+// dodataState holds bits of state information needed by dodata() and the
+// various helpers it calls. The lifetime of these items should not extend
+// past the end of dodata().
+type dodataState struct {
+ // Link context
+ ctxt *Link
+ // Data symbols bucketed by type.
+ data [sym.SXREF][]loader.Sym
+ // Max alignment for each flavor of data symbol.
+ dataMaxAlign [sym.SXREF]int32
+ // Overridden sym type
+ symGroupType []sym.SymKind
+ // Current data size so far.
+ datsize int64
+}
+
+// A note on symType/setSymType below:
+//
+// In the legacy linker, the types of symbols (notably data symbols) are
+// changed during the symtab() phase so as to insure that similar symbols
+// are bucketed together, then their types are changed back again during
+// dodata. Symbol to section assignment also plays tricks along these lines
+// in the case where a relro segment is needed.
+//
+// The value returned from setType() below reflects the effects of
+// any overrides made by symtab and/or dodata.
+
+// symType returns the (possibly overridden) type of 's'.
+func (state *dodataState) symType(s loader.Sym) sym.SymKind {
+ if int(s) < len(state.symGroupType) {
+ if override := state.symGroupType[s]; override != 0 {
+ return override
+ }
+ }
+ return state.ctxt.loader.SymType(s)
+}
+
+// setSymType sets a new override type for 's'.
+func (state *dodataState) setSymType(s loader.Sym, kind sym.SymKind) {
+ if s == 0 {
+ panic("bad")
+ }
+ if int(s) < len(state.symGroupType) {
+ state.symGroupType[s] = kind
+ } else {
+ su := state.ctxt.loader.MakeSymbolUpdater(s)
+ su.SetType(kind)
+ }
+}
+
+func (ctxt *Link) dodata(symGroupType []sym.SymKind) {
+
+ // Give zeros sized symbols space if necessary.
+ fixZeroSizedSymbols(ctxt)
+
+ // Collect data symbols by type into data.
+ state := dodataState{ctxt: ctxt, symGroupType: symGroupType}
+ ldr := ctxt.loader
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || ldr.AttrSubSymbol(s) ||
+ !ldr.TopLevelSym(s) {
+ continue
+ }
+
+ st := state.symType(s)
+
+ if st <= sym.STEXT || st >= sym.SXREF {
+ continue
+ }
+ state.data[st] = append(state.data[st], s)
+
+ // Similarly with checking the onlist attr.
+ if ldr.AttrOnList(s) {
+ log.Fatalf("symbol %s listed multiple times", ldr.SymName(s))
+ }
+ ldr.SetAttrOnList(s, true)
+ }
+
+ // Now that we have the data symbols, but before we start
+ // to assign addresses, record all the necessary
+ // dynamic relocations. These will grow the relocation
+ // symbol, which is itself data.
+ //
+ // On darwin, we need the symbol table numbers for dynreloc.
+ if ctxt.HeadType == objabi.Hdarwin {
+ machosymorder(ctxt)
+ }
+ state.dynreloc(ctxt)
+
+ // Move any RO data with relocations to a separate section.
+ state.makeRelroForSharedLib(ctxt)
+
+ // Set alignment for the symbol with the largest known index,
+ // so as to trigger allocation of the loader's internal
+ // alignment array. This will avoid data races in the parallel
+ // section below.
+ lastSym := loader.Sym(ldr.NSym() - 1)
+ ldr.SetSymAlign(lastSym, ldr.SymAlign(lastSym))
+
+ // Sort symbols.
+ var wg sync.WaitGroup
+ for symn := range state.data {
+ symn := sym.SymKind(symn)
+ wg.Add(1)
+ go func() {
+ state.data[symn], state.dataMaxAlign[symn] = state.dodataSect(ctxt, symn, state.data[symn])
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+
+ if ctxt.IsELF {
+ // Make .rela and .rela.plt contiguous, the ELF ABI requires this
+ // and Solaris actually cares.
+ syms := state.data[sym.SELFROSECT]
+ reli, plti := -1, -1
+ for i, s := range syms {
+ switch ldr.SymName(s) {
+ case ".rel.plt", ".rela.plt":
+ plti = i
+ case ".rel", ".rela":
+ reli = i
+ }
+ }
+ if reli >= 0 && plti >= 0 && plti != reli+1 {
+ var first, second int
+ if plti > reli {
+ first, second = reli, plti
+ } else {
+ first, second = plti, reli
+ }
+ rel, plt := syms[reli], syms[plti]
+ copy(syms[first+2:], syms[first+1:second])
+ syms[first+0] = rel
+ syms[first+1] = plt
+
+ // Make sure alignment doesn't introduce a gap.
+ // Setting the alignment explicitly prevents
+ // symalign from basing it on the size and
+ // getting it wrong.
+ ldr.SetSymAlign(rel, int32(ctxt.Arch.RegSize))
+ ldr.SetSymAlign(plt, int32(ctxt.Arch.RegSize))
+ }
+ state.data[sym.SELFROSECT] = syms
+ }
+
+ if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal {
+ // These symbols must have the same alignment as their section.
+ // Otherwise, ld might change the layout of Go sections.
+ ldr.SetSymAlign(ldr.Lookup("runtime.data", 0), state.dataMaxAlign[sym.SDATA])
+ ldr.SetSymAlign(ldr.Lookup("runtime.bss", 0), state.dataMaxAlign[sym.SBSS])
+ }
+
+ // Create *sym.Section objects and assign symbols to sections for
+ // data/rodata (and related) symbols.
+ state.allocateDataSections(ctxt)
+
+ // Create *sym.Section objects and assign symbols to sections for
+ // DWARF symbols.
+ state.allocateDwarfSections(ctxt)
+
+ /* number the sections */
+ n := int16(1)
+
+ for _, sect := range Segtext.Sections {
+ sect.Extnum = n
+ n++
+ }
+ for _, sect := range Segrodata.Sections {
+ sect.Extnum = n
+ n++
+ }
+ for _, sect := range Segrelrodata.Sections {
+ sect.Extnum = n
+ n++
+ }
+ for _, sect := range Segdata.Sections {
+ sect.Extnum = n
+ n++
+ }
+ for _, sect := range Segdwarf.Sections {
+ sect.Extnum = n
+ n++
+ }
+}
+
+// allocateDataSectionForSym creates a new sym.Section into which a a
+// single symbol will be placed. Here "seg" is the segment into which
+// the section will go, "s" is the symbol to be placed into the new
+// section, and "rwx" contains permissions for the section.
+func (state *dodataState) allocateDataSectionForSym(seg *sym.Segment, s loader.Sym, rwx int) *sym.Section {
+ ldr := state.ctxt.loader
+ sname := ldr.SymName(s)
+ if strings.HasPrefix(sname, "go:") {
+ sname = ".go." + sname[len("go:"):]
+ }
+ sect := addsection(ldr, state.ctxt.Arch, seg, sname, rwx)
+ sect.Align = symalign(ldr, s)
+ state.datsize = Rnd(state.datsize, int64(sect.Align))
+ sect.Vaddr = uint64(state.datsize)
+ return sect
+}
+
+// allocateNamedDataSection creates a new sym.Section for a category
+// of data symbols. Here "seg" is the segment into which the section
+// will go, "sName" is the name to give to the section, "types" is a
+// range of symbol types to be put into the section, and "rwx"
+// contains permissions for the section.
+func (state *dodataState) allocateNamedDataSection(seg *sym.Segment, sName string, types []sym.SymKind, rwx int) *sym.Section {
+ sect := addsection(state.ctxt.loader, state.ctxt.Arch, seg, sName, rwx)
+ if len(types) == 0 {
+ sect.Align = 1
+ } else if len(types) == 1 {
+ sect.Align = state.dataMaxAlign[types[0]]
+ } else {
+ for _, symn := range types {
+ align := state.dataMaxAlign[symn]
+ if sect.Align < align {
+ sect.Align = align
+ }
+ }
+ }
+ state.datsize = Rnd(state.datsize, int64(sect.Align))
+ sect.Vaddr = uint64(state.datsize)
+ return sect
+}
+
+// assignDsymsToSection assigns a collection of data symbols to a
+// newly created section. "sect" is the section into which to place
+// the symbols, "syms" holds the list of symbols to assign,
+// "forceType" (if non-zero) contains a new sym type to apply to each
+// sym during the assignment, and "aligner" is a hook to call to
+// handle alignment during the assignment process.
+func (state *dodataState) assignDsymsToSection(sect *sym.Section, syms []loader.Sym, forceType sym.SymKind, aligner func(state *dodataState, datsize int64, s loader.Sym) int64) {
+ ldr := state.ctxt.loader
+ for _, s := range syms {
+ state.datsize = aligner(state, state.datsize, s)
+ ldr.SetSymSect(s, sect)
+ if forceType != sym.Sxxx {
+ state.setSymType(s, forceType)
+ }
+ ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr))
+ state.datsize += ldr.SymSize(s)
+ }
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+}
+
+func (state *dodataState) assignToSection(sect *sym.Section, symn sym.SymKind, forceType sym.SymKind) {
+ state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize)
+ state.checkdatsize(symn)
+}
+
+// allocateSingleSymSections walks through the bucketed data symbols
+// with type 'symn', creates a new section for each sym, and assigns
+// the sym to a newly created section. Section name is set from the
+// symbol name. "Seg" is the segment into which to place the new
+// section, "forceType" is the new sym.SymKind to assign to the symbol
+// within the section, and "rwx" holds section permissions.
+func (state *dodataState) allocateSingleSymSections(seg *sym.Segment, symn sym.SymKind, forceType sym.SymKind, rwx int) {
+ ldr := state.ctxt.loader
+ for _, s := range state.data[symn] {
+ sect := state.allocateDataSectionForSym(seg, s, rwx)
+ ldr.SetSymSect(s, sect)
+ state.setSymType(s, forceType)
+ ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr))
+ state.datsize += ldr.SymSize(s)
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+ }
+ state.checkdatsize(symn)
+}
+
+// allocateNamedSectionAndAssignSyms creates a new section with the
+// specified name, then walks through the bucketed data symbols with
+// type 'symn' and assigns each of them to this new section. "Seg" is
+// the segment into which to place the new section, "secName" is the
+// name to give to the new section, "forceType" (if non-zero) contains
+// a new sym type to apply to each sym during the assignment, and
+// "rwx" holds section permissions.
+func (state *dodataState) allocateNamedSectionAndAssignSyms(seg *sym.Segment, secName string, symn sym.SymKind, forceType sym.SymKind, rwx int) *sym.Section {
+
+ sect := state.allocateNamedDataSection(seg, secName, []sym.SymKind{symn}, rwx)
+ state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize)
+ return sect
+}
+
+// allocateDataSections allocates sym.Section objects for data/rodata
+// (and related) symbols, and then assigns symbols to those sections.
+func (state *dodataState) allocateDataSections(ctxt *Link) {
+ // Allocate sections.
+ // Data is processed before segtext, because we need
+ // to see all symbols in the .data and .bss sections in order
+ // to generate garbage collection information.
+
+ // Writable data sections that do not need any specialized handling.
+ writable := []sym.SymKind{
+ sym.SBUILDINFO,
+ sym.SELFSECT,
+ sym.SMACHO,
+ sym.SMACHOGOT,
+ sym.SWINDOWS,
+ }
+ for _, symn := range writable {
+ state.allocateSingleSymSections(&Segdata, symn, sym.SDATA, 06)
+ }
+ ldr := ctxt.loader
+
+ // .got
+ if len(state.data[sym.SELFGOT]) > 0 {
+ state.allocateNamedSectionAndAssignSyms(&Segdata, ".got", sym.SELFGOT, sym.SDATA, 06)
+ }
+
+ /* pointer-free data */
+ sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrdata", sym.SNOPTRDATA, sym.SDATA, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrdata", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrdata", 0), sect)
+
+ hasinitarr := ctxt.linkShared
+
+ /* shared library initializer */
+ switch ctxt.BuildMode {
+ case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin:
+ hasinitarr = true
+ }
+
+ if ctxt.HeadType == objabi.Haix {
+ if len(state.data[sym.SINITARR]) > 0 {
+ Errorf(nil, "XCOFF format doesn't allow .init_array section")
+ }
+ }
+
+ if hasinitarr && len(state.data[sym.SINITARR]) > 0 {
+ state.allocateNamedSectionAndAssignSyms(&Segdata, ".init_array", sym.SINITARR, sym.Sxxx, 06)
+ }
+
+ /* data */
+ sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".data", sym.SDATA, sym.SDATA, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.data", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.edata", 0), sect)
+ dataGcEnd := state.datsize - int64(sect.Vaddr)
+
+ // On AIX, TOC entries must be the last of .data
+ // These aren't part of gc as they won't change during the runtime.
+ state.assignToSection(sect, sym.SXCOFFTOC, sym.SDATA)
+ state.checkdatsize(sym.SDATA)
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+
+ /* bss */
+ sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".bss", sym.SBSS, sym.Sxxx, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.bss", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ebss", 0), sect)
+ bssGcEnd := state.datsize - int64(sect.Vaddr)
+
+ // Emit gcdata for bss symbols now that symbol values have been assigned.
+ gcsToEmit := []struct {
+ symName string
+ symKind sym.SymKind
+ gcEnd int64
+ }{
+ {"runtime.gcdata", sym.SDATA, dataGcEnd},
+ {"runtime.gcbss", sym.SBSS, bssGcEnd},
+ }
+ for _, g := range gcsToEmit {
+ var gc GCProg
+ gc.Init(ctxt, g.symName)
+ for _, s := range state.data[g.symKind] {
+ gc.AddSym(s)
+ }
+ gc.End(g.gcEnd)
+ }
+
+ /* pointer-free bss */
+ sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrbss", sym.SNOPTRBSS, sym.Sxxx, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrbss", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrbss", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.end", 0), sect)
+
+ // Code coverage counters are assigned to the .noptrbss section.
+ // We assign them in a separate pass so that they stay aggregated
+ // together in a single blob (coverage runtime depends on this).
+ covCounterDataStartOff = sect.Length
+ state.assignToSection(sect, sym.SCOVERAGE_COUNTER, sym.SNOPTRBSS)
+ covCounterDataLen = sect.Length - covCounterDataStartOff
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.covctrs", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ecovctrs", 0), sect)
+
+ // Coverage instrumentation counters for libfuzzer.
+ if len(state.data[sym.SLIBFUZZER_8BIT_COUNTER]) > 0 {
+ sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".go.fuzzcntrs", sym.SLIBFUZZER_8BIT_COUNTER, sym.Sxxx, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.__start___sancov_cntrs", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.__stop___sancov_cntrs", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._counters", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._ecounters", 0), sect)
+ }
+
+ if len(state.data[sym.STLSBSS]) > 0 {
+ var sect *sym.Section
+ // FIXME: not clear why it is sometimes necessary to suppress .tbss section creation.
+ if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) {
+ sect = addsection(ldr, ctxt.Arch, &Segdata, ".tbss", 06)
+ sect.Align = int32(ctxt.Arch.PtrSize)
+ // FIXME: why does this need to be set to zero?
+ sect.Vaddr = 0
+ }
+ state.datsize = 0
+
+ for _, s := range state.data[sym.STLSBSS] {
+ state.datsize = aligndatsize(state, state.datsize, s)
+ if sect != nil {
+ ldr.SetSymSect(s, sect)
+ }
+ ldr.SetSymValue(s, state.datsize)
+ state.datsize += ldr.SymSize(s)
+ }
+ state.checkdatsize(sym.STLSBSS)
+
+ if sect != nil {
+ sect.Length = uint64(state.datsize)
+ }
+ }
+
+ /*
+ * We finished data, begin read-only data.
+ * Not all systems support a separate read-only non-executable data section.
+ * ELF and Windows PE systems do.
+ * OS X and Plan 9 do not.
+ * And if we're using external linking mode, the point is moot,
+ * since it's not our decision; that code expects the sections in
+ * segtext.
+ */
+ var segro *sym.Segment
+ if ctxt.IsELF && ctxt.LinkMode == LinkInternal {
+ segro = &Segrodata
+ } else if ctxt.HeadType == objabi.Hwindows {
+ segro = &Segrodata
+ } else {
+ segro = &Segtext
+ }
+
+ state.datsize = 0
+
+ /* read-only executable ELF, Mach-O sections */
+ if len(state.data[sym.STEXT]) != 0 {
+ culprit := ldr.SymName(state.data[sym.STEXT][0])
+ Errorf(nil, "dodata found an sym.STEXT symbol: %s", culprit)
+ }
+ state.allocateSingleSymSections(&Segtext, sym.SELFRXSECT, sym.SRODATA, 05)
+ state.allocateSingleSymSections(&Segtext, sym.SMACHOPLT, sym.SRODATA, 05)
+
+ /* read-only data */
+ sect = state.allocateNamedDataSection(segro, ".rodata", sym.ReadOnly, 04)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.rodata", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.erodata", 0), sect)
+ if !ctxt.UseRelro() {
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect)
+ }
+ for _, symn := range sym.ReadOnly {
+ symnStartValue := state.datsize
+ if len(state.data[symn]) != 0 {
+ symnStartValue = aligndatsize(state, symnStartValue, state.data[symn][0])
+ }
+ state.assignToSection(sect, symn, sym.SRODATA)
+ setCarrierSize(symn, state.datsize-symnStartValue)
+ if ctxt.HeadType == objabi.Haix {
+ // Read-only symbols might be wrapped inside their outer
+ // symbol.
+ // XCOFF symbol table needs to know the size of
+ // these outer symbols.
+ xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn)
+ }
+ }
+
+ /* read-only ELF, Mach-O sections */
+ state.allocateSingleSymSections(segro, sym.SELFROSECT, sym.SRODATA, 04)
+
+ // There is some data that are conceptually read-only but are written to by
+ // relocations. On GNU systems, we can arrange for the dynamic linker to
+ // mprotect sections after relocations are applied by giving them write
+ // permissions in the object file and calling them ".data.rel.ro.FOO". We
+ // divide the .rodata section between actual .rodata and .data.rel.ro.rodata,
+ // but for the other sections that this applies to, we just write a read-only
+ // .FOO section or a read-write .data.rel.ro.FOO section depending on the
+ // situation.
+ // TODO(mwhudson): It would make sense to do this more widely, but it makes
+ // the system linker segfault on darwin.
+ const relroPerm = 06
+ const fallbackPerm = 04
+ relroSecPerm := fallbackPerm
+ genrelrosecname := func(suffix string) string {
+ if suffix == "" {
+ return ".rodata"
+ }
+ return suffix
+ }
+ seg := segro
+
+ if ctxt.UseRelro() {
+ segrelro := &Segrelrodata
+ if ctxt.LinkMode == LinkExternal && !ctxt.IsAIX() && !ctxt.IsDarwin() {
+ // Using a separate segment with an external
+ // linker results in some programs moving
+ // their data sections unexpectedly, which
+ // corrupts the moduledata. So we use the
+ // rodata segment and let the external linker
+ // sort out a rel.ro segment.
+ segrelro = segro
+ } else {
+ // Reset datsize for new segment.
+ state.datsize = 0
+ }
+
+ if !ctxt.IsDarwin() { // We don't need the special names on darwin.
+ genrelrosecname = func(suffix string) string {
+ return ".data.rel.ro" + suffix
+ }
+ }
+
+ relroReadOnly := []sym.SymKind{}
+ for _, symnro := range sym.ReadOnly {
+ symn := sym.RelROMap[symnro]
+ relroReadOnly = append(relroReadOnly, symn)
+ }
+ seg = segrelro
+ relroSecPerm = relroPerm
+
+ /* data only written by relocations */
+ sect = state.allocateNamedDataSection(segrelro, genrelrosecname(""), relroReadOnly, relroSecPerm)
+
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect)
+
+ for i, symnro := range sym.ReadOnly {
+ if i == 0 && symnro == sym.STYPE && ctxt.HeadType != objabi.Haix {
+ // Skip forward so that no type
+ // reference uses a zero offset.
+ // This is unlikely but possible in small
+ // programs with no other read-only data.
+ state.datsize++
+ }
+
+ symn := sym.RelROMap[symnro]
+ symnStartValue := state.datsize
+ if len(state.data[symn]) != 0 {
+ symnStartValue = aligndatsize(state, symnStartValue, state.data[symn][0])
+ }
+
+ for _, s := range state.data[symn] {
+ outer := ldr.OuterSym(s)
+ if s != 0 && ldr.SymSect(outer) != nil && ldr.SymSect(outer) != sect {
+ ctxt.Errorf(s, "s.Outer (%s) in different section from s, %s != %s", ldr.SymName(outer), ldr.SymSect(outer).Name, sect.Name)
+ }
+ }
+ state.assignToSection(sect, symn, sym.SRODATA)
+ setCarrierSize(symn, state.datsize-symnStartValue)
+ if ctxt.HeadType == objabi.Haix {
+ // Read-only symbols might be wrapped inside their outer
+ // symbol.
+ // XCOFF symbol table needs to know the size of
+ // these outer symbols.
+ xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn)
+ }
+ }
+
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+ }
+
+ /* typelink */
+ sect = state.allocateNamedDataSection(seg, genrelrosecname(".typelink"), []sym.SymKind{sym.STYPELINK}, relroSecPerm)
+
+ typelink := ldr.CreateSymForUpdate("runtime.typelink", 0)
+ ldr.SetSymSect(typelink.Sym(), sect)
+ typelink.SetType(sym.SRODATA)
+ state.datsize += typelink.Size()
+ state.checkdatsize(sym.STYPELINK)
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+
+ /* itablink */
+ sect = state.allocateNamedDataSection(seg, genrelrosecname(".itablink"), []sym.SymKind{sym.SITABLINK}, relroSecPerm)
+
+ itablink := ldr.CreateSymForUpdate("runtime.itablink", 0)
+ ldr.SetSymSect(itablink.Sym(), sect)
+ itablink.SetType(sym.SRODATA)
+ state.datsize += itablink.Size()
+ state.checkdatsize(sym.SITABLINK)
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+
+ /* gosymtab */
+ sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gosymtab"), sym.SSYMTAB, sym.SRODATA, relroSecPerm)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.symtab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.esymtab", 0), sect)
+
+ /* gopclntab */
+ sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gopclntab"), sym.SPCLNTAB, sym.SRODATA, relroSecPerm)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pcheader", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.funcnametab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.cutab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
+ setCarrierSize(sym.SPCLNTAB, int64(sect.Length))
+ if ctxt.HeadType == objabi.Haix {
+ xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB)
+ }
+
+ // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits.
+ if state.datsize != int64(uint32(state.datsize)) {
+ Errorf(nil, "read-only data segment too large: %d", state.datsize)
+ }
+
+ siz := 0
+ for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ {
+ siz += len(state.data[symn])
+ }
+ ctxt.datap = make([]loader.Sym, 0, siz)
+ for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ {
+ ctxt.datap = append(ctxt.datap, state.data[symn]...)
+ }
+}
+
+// allocateDwarfSections allocates sym.Section objects for DWARF
+// symbols, and assigns symbols to sections.
+func (state *dodataState) allocateDwarfSections(ctxt *Link) {
+
+ alignOne := func(state *dodataState, datsize int64, s loader.Sym) int64 { return datsize }
+
+ ldr := ctxt.loader
+ for i := 0; i < len(dwarfp); i++ {
+ // First the section symbol.
+ s := dwarfp[i].secSym()
+ sect := state.allocateNamedDataSection(&Segdwarf, ldr.SymName(s), []sym.SymKind{}, 04)
+ ldr.SetSymSect(s, sect)
+ sect.Sym = sym.LoaderSym(s)
+ curType := ldr.SymType(s)
+ state.setSymType(s, sym.SRODATA)
+ ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr))
+ state.datsize += ldr.SymSize(s)
+
+ // Then any sub-symbols for the section symbol.
+ subSyms := dwarfp[i].subSyms()
+ state.assignDsymsToSection(sect, subSyms, sym.SRODATA, alignOne)
+
+ for j := 0; j < len(subSyms); j++ {
+ s := subSyms[j]
+ if ctxt.HeadType == objabi.Haix && curType == sym.SDWARFLOC {
+ // Update the size of .debug_loc for this symbol's
+ // package.
+ addDwsectCUSize(".debug_loc", ldr.SymPkg(s), uint64(ldr.SymSize(s)))
+ }
+ }
+ sect.Length = uint64(state.datsize) - sect.Vaddr
+ state.checkdatsize(curType)
+ }
+}
+
+type symNameSize struct {
+ name string
+ sz int64
+ val int64
+ sym loader.Sym
+}
+
+func (state *dodataState) dodataSect(ctxt *Link, symn sym.SymKind, syms []loader.Sym) (result []loader.Sym, maxAlign int32) {
+ var head, tail loader.Sym
+ ldr := ctxt.loader
+ sl := make([]symNameSize, len(syms))
+ for k, s := range syms {
+ ss := ldr.SymSize(s)
+ sl[k] = symNameSize{name: ldr.SymName(s), sz: ss, sym: s}
+ ds := int64(len(ldr.Data(s)))
+ switch {
+ case ss < ds:
+ ctxt.Errorf(s, "initialize bounds (%d < %d)", ss, ds)
+ case ss < 0:
+ ctxt.Errorf(s, "negative size (%d bytes)", ss)
+ case ss > cutoff:
+ ctxt.Errorf(s, "symbol too large (%d bytes)", ss)
+ }
+
+ // If the usually-special section-marker symbols are being laid
+ // out as regular symbols, put them either at the beginning or
+ // end of their section.
+ if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) {
+ switch ldr.SymName(s) {
+ case "runtime.text", "runtime.bss", "runtime.data", "runtime.types", "runtime.rodata":
+ head = s
+ continue
+ case "runtime.etext", "runtime.ebss", "runtime.edata", "runtime.etypes", "runtime.erodata":
+ tail = s
+ continue
+ }
+ }
+ }
+
+ // For ppc64, we want to interleave the .got and .toc sections
+ // from input files. Both are type sym.SELFGOT, so in that case
+ // we skip size comparison and fall through to the name
+ // comparison (conveniently, .got sorts before .toc).
+ checkSize := symn != sym.SELFGOT
+
+ // Perform the sort.
+ if symn != sym.SPCLNTAB {
+ sort.Slice(sl, func(i, j int) bool {
+ si, sj := sl[i].sym, sl[j].sym
+ switch {
+ case si == head, sj == tail:
+ return true
+ case sj == head, si == tail:
+ return false
+ }
+ if checkSize {
+ isz := sl[i].sz
+ jsz := sl[j].sz
+ if isz != jsz {
+ return isz < jsz
+ }
+ }
+ iname := sl[i].name
+ jname := sl[j].name
+ if iname != jname {
+ return iname < jname
+ }
+ return si < sj
+ })
+ } else {
+ // PCLNTAB was built internally, and already has the proper order.
+ }
+
+ // Set alignment, construct result
+ syms = syms[:0]
+ for k := range sl {
+ s := sl[k].sym
+ if s != head && s != tail {
+ align := symalign(ldr, s)
+ if maxAlign < align {
+ maxAlign = align
+ }
+ }
+ syms = append(syms, s)
+ }
+
+ return syms, maxAlign
+}
+
+// Add buildid to beginning of text segment, on non-ELF systems.
+// Non-ELF binary formats are not always flexible enough to
+// give us a place to put the Go build ID. On those systems, we put it
+// at the very beginning of the text segment.
+// This “header” is read by cmd/go.
+func (ctxt *Link) textbuildid() {
+ if ctxt.IsELF || ctxt.BuildMode == BuildModePlugin || *flagBuildid == "" {
+ return
+ }
+
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate("go:buildid", 0)
+ // The \xff is invalid UTF-8, meant to make it less likely
+ // to find one of these accidentally.
+ data := "\xff Go build ID: " + strconv.Quote(*flagBuildid) + "\n \xff"
+ s.SetType(sym.STEXT)
+ s.SetData([]byte(data))
+ s.SetSize(int64(len(data)))
+
+ ctxt.Textp = append(ctxt.Textp, 0)
+ copy(ctxt.Textp[1:], ctxt.Textp)
+ ctxt.Textp[0] = s.Sym()
+}
+
+func (ctxt *Link) buildinfo() {
+ if ctxt.linkShared || ctxt.BuildMode == BuildModePlugin {
+ // -linkshared and -buildmode=plugin get confused
+ // about the relocations in go.buildinfo
+ // pointing at the other data sections.
+ // The version information is only available in executables.
+ return
+ }
+
+ // Write the buildinfo symbol, which go version looks for.
+ // The code reading this data is in package debug/buildinfo.
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate("go:buildinfo", 0)
+ s.SetType(sym.SBUILDINFO)
+ s.SetAlign(16)
+ // The \xff is invalid UTF-8, meant to make it less likely
+ // to find one of these accidentally.
+ const prefix = "\xff Go buildinf:" // 14 bytes, plus 2 data bytes filled in below
+ data := make([]byte, 32)
+ copy(data, prefix)
+ data[len(prefix)] = byte(ctxt.Arch.PtrSize)
+ data[len(prefix)+1] = 0
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ data[len(prefix)+1] = 1
+ }
+ data[len(prefix)+1] |= 2 // signals new pointer-free format
+ data = appendString(data, strdata["runtime.buildVersion"])
+ data = appendString(data, strdata["runtime.modinfo"])
+ // MacOS linker gets very upset if the size os not a multiple of alignment.
+ for len(data)%16 != 0 {
+ data = append(data, 0)
+ }
+ s.SetData(data)
+ s.SetSize(int64(len(data)))
+
+ // Add reference to go:buildinfo from the rodata section,
+ // so that external linking with -Wl,--gc-sections does not
+ // delete the build info.
+ sr := ldr.CreateSymForUpdate("go:buildinfo.ref", 0)
+ sr.SetType(sym.SRODATA)
+ sr.SetAlign(int32(ctxt.Arch.PtrSize))
+ sr.AddAddr(ctxt.Arch, s.Sym())
+}
+
+// appendString appends s to data, prefixed by its varint-encoded length.
+func appendString(data []byte, s string) []byte {
+ var v [binary.MaxVarintLen64]byte
+ n := binary.PutUvarint(v[:], uint64(len(s)))
+ data = append(data, v[:n]...)
+ data = append(data, s...)
+ return data
+}
+
+// assign addresses to text
+func (ctxt *Link) textaddress() {
+ addsection(ctxt.loader, ctxt.Arch, &Segtext, ".text", 05)
+
+ // Assign PCs in text segment.
+ // Could parallelize, by assigning to text
+ // and then letting threads copy down, but probably not worth it.
+ sect := Segtext.Sections[0]
+
+ sect.Align = int32(Funcalign)
+
+ ldr := ctxt.loader
+
+ text := ctxt.xdefine("runtime.text", sym.STEXT, 0)
+ etext := ctxt.xdefine("runtime.etext", sym.STEXT, 0)
+ ldr.SetSymSect(text, sect)
+ if ctxt.IsAIX() && ctxt.IsExternal() {
+ // Setting runtime.text has a real symbol prevents ld to
+ // change its base address resulting in wrong offsets for
+ // reflect methods.
+ u := ldr.MakeSymbolUpdater(text)
+ u.SetAlign(sect.Align)
+ u.SetSize(8)
+ }
+
+ if (ctxt.DynlinkingGo() && ctxt.IsDarwin()) || (ctxt.IsAIX() && ctxt.IsExternal()) {
+ ldr.SetSymSect(etext, sect)
+ ctxt.Textp = append(ctxt.Textp, etext, 0)
+ copy(ctxt.Textp[1:], ctxt.Textp)
+ ctxt.Textp[0] = text
+ }
+
+ start := uint64(Rnd(*FlagTextAddr, int64(Funcalign)))
+ va := start
+ n := 1
+ sect.Vaddr = va
+
+ limit := thearch.TrampLimit
+ if limit == 0 {
+ limit = 1 << 63 // unlimited
+ }
+ if *FlagDebugTextSize != 0 {
+ limit = uint64(*FlagDebugTextSize)
+ }
+ if *FlagDebugTramp > 1 {
+ limit = 1 // debug mode, force generating trampolines for everything
+ }
+
+ if ctxt.IsAIX() && ctxt.IsExternal() {
+ // On AIX, normally we won't generate direct calls to external symbols,
+ // except in one test, cmd/go/testdata/script/link_syso_issue33139.txt.
+ // That test doesn't make much sense, and I'm not sure it ever works.
+ // Just generate trampoline for now (which will turn a direct call to
+ // an indirect call, which at least builds).
+ limit = 1
+ }
+
+ // First pass: assign addresses assuming the program is small and
+ // don't generate trampolines.
+ big := false
+ for _, s := range ctxt.Textp {
+ sect, n, va = assignAddress(ctxt, sect, n, s, va, false, big)
+ if va-start >= limit {
+ big = true
+ break
+ }
+ }
+
+ // Second pass: only if it is too big, insert trampolines for too-far
+ // jumps and targets with unknown addresses.
+ if big {
+ // reset addresses
+ for _, s := range ctxt.Textp {
+ if ldr.OuterSym(s) != 0 || s == text {
+ continue
+ }
+ oldv := ldr.SymValue(s)
+ for sub := s; sub != 0; sub = ldr.SubSym(sub) {
+ ldr.SetSymValue(sub, ldr.SymValue(sub)-oldv)
+ }
+ }
+ va = start
+
+ ntramps := 0
+ for _, s := range ctxt.Textp {
+ sect, n, va = assignAddress(ctxt, sect, n, s, va, false, big)
+
+ trampoline(ctxt, s) // resolve jumps, may add trampolines if jump too far
+
+ // lay down trampolines after each function
+ for ; ntramps < len(ctxt.tramps); ntramps++ {
+ tramp := ctxt.tramps[ntramps]
+ if ctxt.IsAIX() && strings.HasPrefix(ldr.SymName(tramp), "runtime.text.") {
+ // Already set in assignAddress
+ continue
+ }
+ sect, n, va = assignAddress(ctxt, sect, n, tramp, va, true, big)
+ }
+ }
+
+ // merge tramps into Textp, keeping Textp in address order
+ if ntramps != 0 {
+ newtextp := make([]loader.Sym, 0, len(ctxt.Textp)+ntramps)
+ i := 0
+ for _, s := range ctxt.Textp {
+ for ; i < ntramps && ldr.SymValue(ctxt.tramps[i]) < ldr.SymValue(s); i++ {
+ newtextp = append(newtextp, ctxt.tramps[i])
+ }
+ newtextp = append(newtextp, s)
+ }
+ newtextp = append(newtextp, ctxt.tramps[i:ntramps]...)
+
+ ctxt.Textp = newtextp
+ }
+ }
+
+ sect.Length = va - sect.Vaddr
+ ldr.SetSymSect(etext, sect)
+ if ldr.SymValue(etext) == 0 {
+ // Set the address of the start/end symbols, if not already
+ // (i.e. not darwin+dynlink or AIX+external, see above).
+ ldr.SetSymValue(etext, int64(va))
+ ldr.SetSymValue(text, int64(Segtext.Sections[0].Vaddr))
+ }
+}
+
+// assigns address for a text symbol, returns (possibly new) section, its number, and the address.
+func assignAddress(ctxt *Link, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp, big bool) (*sym.Section, int, uint64) {
+ ldr := ctxt.loader
+ if thearch.AssignAddress != nil {
+ return thearch.AssignAddress(ldr, sect, n, s, va, isTramp)
+ }
+
+ ldr.SetSymSect(s, sect)
+ if ldr.AttrSubSymbol(s) {
+ return sect, n, va
+ }
+
+ align := ldr.SymAlign(s)
+ if align == 0 {
+ align = int32(Funcalign)
+ }
+ va = uint64(Rnd(int64(va), int64(align)))
+ if sect.Align < align {
+ sect.Align = align
+ }
+
+ funcsize := uint64(MINFUNC) // spacing required for findfunctab
+ if ldr.SymSize(s) > MINFUNC {
+ funcsize = uint64(ldr.SymSize(s))
+ }
+
+ // If we need to split text sections, and this function doesn't fit in the current
+ // section, then create a new one.
+ //
+ // Only break at outermost syms.
+ if big && splitTextSections(ctxt) && ldr.OuterSym(s) == 0 {
+ // For debugging purposes, allow text size limit to be cranked down,
+ // so as to stress test the code that handles multiple text sections.
+ var textSizelimit uint64 = thearch.TrampLimit
+ if *FlagDebugTextSize != 0 {
+ textSizelimit = uint64(*FlagDebugTextSize)
+ }
+
+ // Sanity check: make sure the limit is larger than any
+ // individual text symbol.
+ if funcsize > textSizelimit {
+ panic(fmt.Sprintf("error: text size limit %d less than text symbol %s size of %d", textSizelimit, ldr.SymName(s), funcsize))
+ }
+
+ if va-sect.Vaddr+funcsize+maxSizeTrampolines(ctxt, ldr, s, isTramp) > textSizelimit {
+ sectAlign := int32(thearch.Funcalign)
+ if ctxt.IsPPC64() {
+ // Align the next text section to the worst case function alignment likely
+ // to be encountered when processing function symbols. The start address
+ // is rounded against the final alignment of the text section later on in
+ // (*Link).address. This may happen due to usage of PCALIGN directives
+ // larger than Funcalign, or usage of ISA 3.1 prefixed instructions
+ // (see ISA 3.1 Book I 1.9).
+ const ppc64maxFuncalign = 64
+ sectAlign = ppc64maxFuncalign
+ va = uint64(Rnd(int64(va), ppc64maxFuncalign))
+ }
+
+ // Set the length for the previous text section
+ sect.Length = va - sect.Vaddr
+
+ // Create new section, set the starting Vaddr
+ sect = addsection(ctxt.loader, ctxt.Arch, &Segtext, ".text", 05)
+
+ sect.Vaddr = va
+ sect.Align = sectAlign
+ ldr.SetSymSect(s, sect)
+
+ // Create a symbol for the start of the secondary text sections
+ ntext := ldr.CreateSymForUpdate(fmt.Sprintf("runtime.text.%d", n), 0)
+ ntext.SetSect(sect)
+ if ctxt.IsAIX() {
+ // runtime.text.X must be a real symbol on AIX.
+ // Assign its address directly in order to be the
+ // first symbol of this new section.
+ ntext.SetType(sym.STEXT)
+ ntext.SetSize(int64(MINFUNC))
+ ntext.SetOnList(true)
+ ntext.SetAlign(sectAlign)
+ ctxt.tramps = append(ctxt.tramps, ntext.Sym())
+
+ ntext.SetValue(int64(va))
+ va += uint64(ntext.Size())
+
+ if align := ldr.SymAlign(s); align != 0 {
+ va = uint64(Rnd(int64(va), int64(align)))
+ } else {
+ va = uint64(Rnd(int64(va), int64(Funcalign)))
+ }
+ }
+ n++
+ }
+ }
+
+ ldr.SetSymValue(s, 0)
+ for sub := s; sub != 0; sub = ldr.SubSym(sub) {
+ ldr.SetSymValue(sub, ldr.SymValue(sub)+int64(va))
+ if ctxt.Debugvlog > 2 {
+ fmt.Println("assign text address:", ldr.SymName(sub), ldr.SymValue(sub))
+ }
+ }
+
+ va += funcsize
+
+ return sect, n, va
+}
+
+// Return whether we may need to split text sections.
+//
+// On PPC64x, when external linking, a text section should not be
+// larger than 2^25 bytes due to the size of call target offset field
+// in the 'bl' instruction. Splitting into smaller text sections
+// smaller than this limit allows the system linker to modify the long
+// calls appropriately. The limit allows for the space needed for
+// tables inserted by the linker.
+//
+// The same applies to Darwin/ARM64, with 2^27 byte threshold.
+//
+// Similarly for ARM, we split sections (at 2^25 bytes) to avoid
+// inconsistencies between the Go linker's reachability calculations
+// (e.g. will direct call from X to Y need a trampoline) and similar
+// machinery in the external linker; see #58425 for more on the
+// history here.
+func splitTextSections(ctxt *Link) bool {
+ return (ctxt.IsARM() || ctxt.IsPPC64() || (ctxt.IsARM64() && ctxt.IsDarwin())) && ctxt.IsExternal()
+}
+
+// On Wasm, we reserve 4096 bytes for zero page, then 8192 bytes for wasm_exec.js
+// to store command line args and environment variables.
+// Data sections starts from at least address 12288.
+// Keep in sync with wasm_exec.js.
+const wasmMinDataAddr = 4096 + 8192
+
+// address assigns virtual addresses to all segments and sections and
+// returns all segments in file order.
+func (ctxt *Link) address() []*sym.Segment {
+ var order []*sym.Segment // Layout order
+
+ va := uint64(*FlagTextAddr)
+ order = append(order, &Segtext)
+ Segtext.Rwx = 05
+ Segtext.Vaddr = va
+ for i, s := range Segtext.Sections {
+ va = uint64(Rnd(int64(va), int64(s.Align)))
+ s.Vaddr = va
+ va += s.Length
+
+ if ctxt.IsWasm() && i == 0 && va < wasmMinDataAddr {
+ va = wasmMinDataAddr
+ }
+ }
+
+ Segtext.Length = va - uint64(*FlagTextAddr)
+
+ if len(Segrodata.Sections) > 0 {
+ // align to page boundary so as not to mix
+ // rodata and executable text.
+ //
+ // Note: gold or GNU ld will reduce the size of the executable
+ // file by arranging for the relro segment to end at a page
+ // boundary, and overlap the end of the text segment with the
+ // start of the relro segment in the file. The PT_LOAD segments
+ // will be such that the last page of the text segment will be
+ // mapped twice, once r-x and once starting out rw- and, after
+ // relocation processing, changed to r--.
+ //
+ // Ideally the last page of the text segment would not be
+ // writable even for this short period.
+ va = uint64(Rnd(int64(va), int64(*FlagRound)))
+
+ order = append(order, &Segrodata)
+ Segrodata.Rwx = 04
+ Segrodata.Vaddr = va
+ for _, s := range Segrodata.Sections {
+ va = uint64(Rnd(int64(va), int64(s.Align)))
+ s.Vaddr = va
+ va += s.Length
+ }
+
+ Segrodata.Length = va - Segrodata.Vaddr
+ }
+ if len(Segrelrodata.Sections) > 0 {
+ // align to page boundary so as not to mix
+ // rodata, rel-ro data, and executable text.
+ va = uint64(Rnd(int64(va), int64(*FlagRound)))
+ if ctxt.HeadType == objabi.Haix {
+ // Relro data are inside data segment on AIX.
+ va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE)
+ }
+
+ order = append(order, &Segrelrodata)
+ Segrelrodata.Rwx = 06
+ Segrelrodata.Vaddr = va
+ for _, s := range Segrelrodata.Sections {
+ va = uint64(Rnd(int64(va), int64(s.Align)))
+ s.Vaddr = va
+ va += s.Length
+ }
+
+ Segrelrodata.Length = va - Segrelrodata.Vaddr
+ }
+
+ va = uint64(Rnd(int64(va), int64(*FlagRound)))
+ if ctxt.HeadType == objabi.Haix && len(Segrelrodata.Sections) == 0 {
+ // Data sections are moved to an unreachable segment
+ // to ensure that they are position-independent.
+ // Already done if relro sections exist.
+ va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE)
+ }
+ order = append(order, &Segdata)
+ Segdata.Rwx = 06
+ Segdata.Vaddr = va
+ var data *sym.Section
+ var noptr *sym.Section
+ var bss *sym.Section
+ var noptrbss *sym.Section
+ var fuzzCounters *sym.Section
+ for i, s := range Segdata.Sections {
+ if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && s.Name == ".tbss" {
+ continue
+ }
+ vlen := int64(s.Length)
+ if i+1 < len(Segdata.Sections) && !((ctxt.IsELF || ctxt.HeadType == objabi.Haix) && Segdata.Sections[i+1].Name == ".tbss") {
+ vlen = int64(Segdata.Sections[i+1].Vaddr - s.Vaddr)
+ }
+ s.Vaddr = va
+ va += uint64(vlen)
+ Segdata.Length = va - Segdata.Vaddr
+ switch s.Name {
+ case ".data":
+ data = s
+ case ".noptrdata":
+ noptr = s
+ case ".bss":
+ bss = s
+ case ".noptrbss":
+ noptrbss = s
+ case ".go.fuzzcntrs":
+ fuzzCounters = s
+ }
+ }
+
+ // Assign Segdata's Filelen omitting the BSS. We do this here
+ // simply because right now we know where the BSS starts.
+ Segdata.Filelen = bss.Vaddr - Segdata.Vaddr
+
+ va = uint64(Rnd(int64(va), int64(*FlagRound)))
+ order = append(order, &Segdwarf)
+ Segdwarf.Rwx = 06
+ Segdwarf.Vaddr = va
+ for i, s := range Segdwarf.Sections {
+ vlen := int64(s.Length)
+ if i+1 < len(Segdwarf.Sections) {
+ vlen = int64(Segdwarf.Sections[i+1].Vaddr - s.Vaddr)
+ }
+ s.Vaddr = va
+ va += uint64(vlen)
+ if ctxt.HeadType == objabi.Hwindows {
+ va = uint64(Rnd(int64(va), PEFILEALIGN))
+ }
+ Segdwarf.Length = va - Segdwarf.Vaddr
+ }
+
+ ldr := ctxt.loader
+ var (
+ rodata = ldr.SymSect(ldr.LookupOrCreateSym("runtime.rodata", 0))
+ symtab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.symtab", 0))
+ pclntab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0))
+ types = ldr.SymSect(ldr.LookupOrCreateSym("runtime.types", 0))
+ )
+
+ for _, s := range ctxt.datap {
+ if sect := ldr.SymSect(s); sect != nil {
+ ldr.AddToSymValue(s, int64(sect.Vaddr))
+ }
+ v := ldr.SymValue(s)
+ for sub := ldr.SubSym(s); sub != 0; sub = ldr.SubSym(sub) {
+ ldr.AddToSymValue(sub, v)
+ }
+ }
+
+ for _, si := range dwarfp {
+ for _, s := range si.syms {
+ if sect := ldr.SymSect(s); sect != nil {
+ ldr.AddToSymValue(s, int64(sect.Vaddr))
+ }
+ sub := ldr.SubSym(s)
+ if sub != 0 {
+ panic(fmt.Sprintf("unexpected sub-sym for %s %s", ldr.SymName(s), ldr.SymType(s).String()))
+ }
+ v := ldr.SymValue(s)
+ for ; sub != 0; sub = ldr.SubSym(sub) {
+ ldr.AddToSymValue(s, v)
+ }
+ }
+ }
+
+ if ctxt.BuildMode == BuildModeShared {
+ s := ldr.LookupOrCreateSym("go:link.abihashbytes", 0)
+ sect := ldr.SymSect(ldr.LookupOrCreateSym(".note.go.abihash", 0))
+ ldr.SetSymSect(s, sect)
+ ldr.SetSymValue(s, int64(sect.Vaddr+16))
+ }
+
+ // If there are multiple text sections, create runtime.text.n for
+ // their section Vaddr, using n for index
+ n := 1
+ for _, sect := range Segtext.Sections[1:] {
+ if sect.Name != ".text" {
+ break
+ }
+ symname := fmt.Sprintf("runtime.text.%d", n)
+ if ctxt.HeadType != objabi.Haix || ctxt.LinkMode != LinkExternal {
+ // Addresses are already set on AIX with external linker
+ // because these symbols are part of their sections.
+ ctxt.xdefine(symname, sym.STEXT, int64(sect.Vaddr))
+ }
+ n++
+ }
+
+ ctxt.xdefine("runtime.rodata", sym.SRODATA, int64(rodata.Vaddr))
+ ctxt.xdefine("runtime.erodata", sym.SRODATA, int64(rodata.Vaddr+rodata.Length))
+ ctxt.xdefine("runtime.types", sym.SRODATA, int64(types.Vaddr))
+ ctxt.xdefine("runtime.etypes", sym.SRODATA, int64(types.Vaddr+types.Length))
+
+ s := ldr.Lookup("runtime.gcdata", 0)
+ ldr.SetAttrLocal(s, true)
+ ctxt.xdefine("runtime.egcdata", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s))
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcdata", 0), ldr.SymSect(s))
+
+ s = ldr.LookupOrCreateSym("runtime.gcbss", 0)
+ ldr.SetAttrLocal(s, true)
+ ctxt.xdefine("runtime.egcbss", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s))
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcbss", 0), ldr.SymSect(s))
+
+ ctxt.xdefine("runtime.symtab", sym.SRODATA, int64(symtab.Vaddr))
+ ctxt.xdefine("runtime.esymtab", sym.SRODATA, int64(symtab.Vaddr+symtab.Length))
+ ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr))
+ ctxt.defineInternal("runtime.pcheader", sym.SRODATA)
+ ctxt.defineInternal("runtime.funcnametab", sym.SRODATA)
+ ctxt.defineInternal("runtime.cutab", sym.SRODATA)
+ ctxt.defineInternal("runtime.filetab", sym.SRODATA)
+ ctxt.defineInternal("runtime.pctab", sym.SRODATA)
+ ctxt.defineInternal("runtime.functab", sym.SRODATA)
+ ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
+ ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr))
+ ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length))
+ ctxt.xdefine("runtime.bss", sym.SBSS, int64(bss.Vaddr))
+ ctxt.xdefine("runtime.ebss", sym.SBSS, int64(bss.Vaddr+bss.Length))
+ ctxt.xdefine("runtime.data", sym.SDATA, int64(data.Vaddr))
+ ctxt.xdefine("runtime.edata", sym.SDATA, int64(data.Vaddr+data.Length))
+ ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr))
+ ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length))
+ ctxt.xdefine("runtime.covctrs", sym.SCOVERAGE_COUNTER, int64(noptrbss.Vaddr+covCounterDataStartOff))
+ ctxt.xdefine("runtime.ecovctrs", sym.SCOVERAGE_COUNTER, int64(noptrbss.Vaddr+covCounterDataStartOff+covCounterDataLen))
+ ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length))
+
+ if fuzzCounters != nil {
+ ctxt.xdefine("runtime.__start___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr))
+ ctxt.xdefine("runtime.__stop___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length))
+ ctxt.xdefine("internal/fuzz._counters", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr))
+ ctxt.xdefine("internal/fuzz._ecounters", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length))
+ }
+
+ if ctxt.IsSolaris() {
+ // On Solaris, in the runtime it sets the external names of the
+ // end symbols. Unset them and define separate symbols, so we
+ // keep both.
+ etext := ldr.Lookup("runtime.etext", 0)
+ edata := ldr.Lookup("runtime.edata", 0)
+ end := ldr.Lookup("runtime.end", 0)
+ ldr.SetSymExtname(etext, "runtime.etext")
+ ldr.SetSymExtname(edata, "runtime.edata")
+ ldr.SetSymExtname(end, "runtime.end")
+ ctxt.xdefine("_etext", ldr.SymType(etext), ldr.SymValue(etext))
+ ctxt.xdefine("_edata", ldr.SymType(edata), ldr.SymValue(edata))
+ ctxt.xdefine("_end", ldr.SymType(end), ldr.SymValue(end))
+ ldr.SetSymSect(ldr.Lookup("_etext", 0), ldr.SymSect(etext))
+ ldr.SetSymSect(ldr.Lookup("_edata", 0), ldr.SymSect(edata))
+ ldr.SetSymSect(ldr.Lookup("_end", 0), ldr.SymSect(end))
+ }
+
+ if ctxt.IsPPC64() && ctxt.IsElf() {
+ // Resolve .TOC. symbols for all objects. Only one TOC region is supported. If a
+ // GOT section is present, compute it as suggested by the ELFv2 ABI. Otherwise,
+ // choose a similar offset from the start of the data segment.
+ tocAddr := int64(Segdata.Vaddr) + 0x8000
+ if gotAddr := ldr.SymValue(ctxt.GOT); gotAddr != 0 {
+ tocAddr = gotAddr + 0x8000
+ }
+ for i := range ctxt.DotTOC {
+ if i >= sym.SymVerABICount && i < sym.SymVerStatic { // these versions are not used currently
+ continue
+ }
+ if toc := ldr.Lookup(".TOC.", i); toc != 0 {
+ ldr.SetSymValue(toc, tocAddr)
+ }
+ }
+ }
+
+ return order
+}
+
+// layout assigns file offsets and lengths to the segments in order.
+// Returns the file size containing all the segments.
+func (ctxt *Link) layout(order []*sym.Segment) uint64 {
+ var prev *sym.Segment
+ for _, seg := range order {
+ if prev == nil {
+ seg.Fileoff = uint64(HEADR)
+ } else {
+ switch ctxt.HeadType {
+ default:
+ // Assuming the previous segment was
+ // aligned, the following rounding
+ // should ensure that this segment's
+ // VA ≡ Fileoff mod FlagRound.
+ seg.Fileoff = uint64(Rnd(int64(prev.Fileoff+prev.Filelen), int64(*FlagRound)))
+ if seg.Vaddr%uint64(*FlagRound) != seg.Fileoff%uint64(*FlagRound) {
+ Exitf("bad segment rounding (Vaddr=%#x Fileoff=%#x FlagRound=%#x)", seg.Vaddr, seg.Fileoff, *FlagRound)
+ }
+ case objabi.Hwindows:
+ seg.Fileoff = prev.Fileoff + uint64(Rnd(int64(prev.Filelen), PEFILEALIGN))
+ case objabi.Hplan9:
+ seg.Fileoff = prev.Fileoff + prev.Filelen
+ }
+ }
+ if seg != &Segdata {
+ // Link.address already set Segdata.Filelen to
+ // account for BSS.
+ seg.Filelen = seg.Length
+ }
+ prev = seg
+ }
+ return prev.Fileoff + prev.Filelen
+}
+
+// add a trampoline with symbol s (to be laid down after the current function)
+func (ctxt *Link) AddTramp(s *loader.SymbolBuilder) {
+ s.SetType(sym.STEXT)
+ s.SetReachable(true)
+ s.SetOnList(true)
+ ctxt.tramps = append(ctxt.tramps, s.Sym())
+ if *FlagDebugTramp > 0 && ctxt.Debugvlog > 0 {
+ ctxt.Logf("trampoline %s inserted\n", s.Name())
+ }
+}
+
+// compressSyms compresses syms and returns the contents of the
+// compressed section. If the section would get larger, it returns nil.
+func compressSyms(ctxt *Link, syms []loader.Sym) []byte {
+ ldr := ctxt.loader
+ var total int64
+ for _, sym := range syms {
+ total += ldr.SymSize(sym)
+ }
+
+ var buf bytes.Buffer
+ if ctxt.IsELF {
+ switch ctxt.Arch.PtrSize {
+ case 8:
+ binary.Write(&buf, ctxt.Arch.ByteOrder, elf.Chdr64{
+ Type: uint32(elf.COMPRESS_ZLIB),
+ Size: uint64(total),
+ Addralign: uint64(ctxt.Arch.Alignment),
+ })
+ case 4:
+ binary.Write(&buf, ctxt.Arch.ByteOrder, elf.Chdr32{
+ Type: uint32(elf.COMPRESS_ZLIB),
+ Size: uint32(total),
+ Addralign: uint32(ctxt.Arch.Alignment),
+ })
+ default:
+ log.Fatalf("can't compress header size:%d", ctxt.Arch.PtrSize)
+ }
+ } else {
+ buf.Write([]byte("ZLIB"))
+ var sizeBytes [8]byte
+ binary.BigEndian.PutUint64(sizeBytes[:], uint64(total))
+ buf.Write(sizeBytes[:])
+ }
+
+ var relocbuf []byte // temporary buffer for applying relocations
+
+ // Using zlib.BestSpeed achieves very nearly the same
+ // compression levels of zlib.DefaultCompression, but takes
+ // substantially less time. This is important because DWARF
+ // compression can be a significant fraction of link time.
+ z, err := zlib.NewWriterLevel(&buf, zlib.BestSpeed)
+ if err != nil {
+ log.Fatalf("NewWriterLevel failed: %s", err)
+ }
+ st := ctxt.makeRelocSymState()
+ for _, s := range syms {
+ // Symbol data may be read-only. Apply relocations in a
+ // temporary buffer, and immediately write it out.
+ P := ldr.Data(s)
+ relocs := ldr.Relocs(s)
+ if relocs.Count() != 0 {
+ relocbuf = append(relocbuf[:0], P...)
+ P = relocbuf
+ st.relocsym(s, P)
+ }
+ if _, err := z.Write(P); err != nil {
+ log.Fatalf("compression failed: %s", err)
+ }
+ for i := ldr.SymSize(s) - int64(len(P)); i > 0; {
+ b := zeros[:]
+ if i < int64(len(b)) {
+ b = b[:i]
+ }
+ n, err := z.Write(b)
+ if err != nil {
+ log.Fatalf("compression failed: %s", err)
+ }
+ i -= int64(n)
+ }
+ }
+ if err := z.Close(); err != nil {
+ log.Fatalf("compression failed: %s", err)
+ }
+ if int64(buf.Len()) >= total {
+ // Compression didn't save any space.
+ return nil
+ }
+ return buf.Bytes()
+}
diff --git a/src/cmd/link/internal/ld/data_test.go b/src/cmd/link/internal/ld/data_test.go
new file mode 100644
index 0000000..f91493b
--- /dev/null
+++ b/src/cmd/link/internal/ld/data_test.go
@@ -0,0 +1,93 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "internal/buildcfg"
+ "testing"
+)
+
+func setUpContext(arch *sys.Arch, iself bool, ht objabi.HeadType, bm, lm string) *Link {
+ ctxt := linknew(arch)
+ edummy := func(str string, off int) {}
+ ctxt.HeadType = ht
+ er := loader.ErrorReporter{}
+ ctxt.loader = loader.NewLoader(0, edummy, &er)
+ ctxt.BuildMode.Set(bm)
+ ctxt.LinkMode.Set(lm)
+ ctxt.IsELF = iself
+ ctxt.mustSetHeadType()
+ ctxt.setArchSyms()
+ return ctxt
+}
+
+// Make sure the addgotsym properly increases the symbols.
+func TestAddGotSym(t *testing.T) {
+ tests := []struct {
+ arch *sys.Arch
+ ht objabi.HeadType
+ bm, lm string
+ rel string
+ relsize int
+ gotsize int
+ }{
+ {
+ arch: sys.Arch386,
+ ht: objabi.Hlinux,
+ bm: "pie",
+ lm: "internal",
+ rel: ".rel",
+ relsize: 2 * sys.Arch386.PtrSize,
+ gotsize: sys.Arch386.PtrSize,
+ },
+ {
+ arch: sys.ArchAMD64,
+ ht: objabi.Hlinux,
+ bm: "pie",
+ lm: "internal",
+ rel: ".rela",
+ relsize: 3 * sys.ArchAMD64.PtrSize,
+ gotsize: sys.ArchAMD64.PtrSize,
+ },
+ {
+ arch: sys.ArchAMD64,
+ ht: objabi.Hdarwin,
+ bm: "pie",
+ lm: "external",
+ gotsize: sys.ArchAMD64.PtrSize,
+ },
+ }
+
+ // Save the architecture as we're going to set it on each test run.
+ origArch := buildcfg.GOARCH
+ defer func() {
+ buildcfg.GOARCH = origArch
+ }()
+
+ for i, test := range tests {
+ iself := len(test.rel) != 0
+ buildcfg.GOARCH = test.arch.Name
+ ctxt := setUpContext(test.arch, iself, test.ht, test.bm, test.lm)
+ foo := ctxt.loader.CreateSymForUpdate("foo", 0)
+ ctxt.loader.CreateExtSym("bar", 0)
+ AddGotSym(&ctxt.Target, ctxt.loader, &ctxt.ArchSyms, foo.Sym(), 0)
+
+ if iself {
+ rel := ctxt.loader.Lookup(test.rel, 0)
+ if rel == 0 {
+ t.Fatalf("[%d] could not find symbol: %q", i, test.rel)
+ }
+ if s := ctxt.loader.SymSize(rel); s != int64(test.relsize) {
+ t.Fatalf("[%d] expected ldr.Size(%q) == %v, got %v", i, test.rel, test.relsize, s)
+ }
+ }
+ if s := ctxt.loader.SymSize(ctxt.loader.Lookup(".got", 0)); s != int64(test.gotsize) {
+ t.Fatalf(`[%d] expected ldr.Size(".got") == %v, got %v`, i, test.gotsize, s)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
new file mode 100644
index 0000000..0738a51
--- /dev/null
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -0,0 +1,469 @@
+// Copyright 2019 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.
+
+package ld
+
+import (
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "fmt"
+ "internal/buildcfg"
+ "unicode"
+)
+
+var _ = fmt.Print
+
+type deadcodePass struct {
+ ctxt *Link
+ ldr *loader.Loader
+ wq heap // work queue, using min-heap for better locality
+
+ ifaceMethod map[methodsig]bool // methods called from reached interface call sites
+ genericIfaceMethod map[string]bool // names of methods called from reached generic interface call sites
+ markableMethods []methodref // methods of reached types
+ reflectSeen bool // whether we have seen a reflect method call
+ dynlink bool
+
+ methodsigstmp []methodsig // scratch buffer for decoding method signatures
+}
+
+func (d *deadcodePass) init() {
+ d.ldr.InitReachable()
+ d.ifaceMethod = make(map[methodsig]bool)
+ d.genericIfaceMethod = make(map[string]bool)
+ if buildcfg.Experiment.FieldTrack {
+ d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
+ }
+ d.dynlink = d.ctxt.DynlinkingGo()
+
+ if d.ctxt.BuildMode == BuildModeShared {
+ // Mark all symbols defined in this library as reachable when
+ // building a shared library.
+ n := d.ldr.NDef()
+ for i := 1; i < n; i++ {
+ s := loader.Sym(i)
+ d.mark(s, 0)
+ }
+ return
+ }
+
+ var names []string
+
+ // In a normal binary, start at main.main and the init
+ // functions and mark what is reachable from there.
+ if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+ names = append(names, "main.main", "main..inittask")
+ } else {
+ // The external linker refers main symbol directly.
+ if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+ if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 {
+ *flagEntrySymbol = "_main"
+ } else {
+ *flagEntrySymbol = "main"
+ }
+ }
+ names = append(names, *flagEntrySymbol)
+ }
+ // runtime.unreachableMethod is a function that will throw if called.
+ // We redirect unreachable methods to it.
+ names = append(names, "runtime.unreachableMethod")
+ if d.ctxt.BuildMode == BuildModePlugin {
+ names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go:plugin.tabs")
+
+ // We don't keep the go.plugin.exports symbol,
+ // but we do keep the symbols it refers to.
+ exportsIdx := d.ldr.Lookup("go:plugin.exports", 0)
+ if exportsIdx != 0 {
+ relocs := d.ldr.Relocs(exportsIdx)
+ for i := 0; i < relocs.Count(); i++ {
+ d.mark(relocs.At(i).Sym(), 0)
+ }
+ }
+ }
+
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("deadcode start names: %v\n", names)
+ }
+
+ for _, name := range names {
+ // Mark symbol as a data/ABI0 symbol.
+ d.mark(d.ldr.Lookup(name, 0), 0)
+ if abiInternalVer != 0 {
+ // Also mark any Go functions (internal ABI).
+ d.mark(d.ldr.Lookup(name, abiInternalVer), 0)
+ }
+ }
+
+ // All dynamic exports are roots.
+ for _, s := range d.ctxt.dynexp {
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("deadcode start dynexp: %s<%d>\n", d.ldr.SymName(s), d.ldr.SymVersion(s))
+ }
+ d.mark(s, 0)
+ }
+}
+
+func (d *deadcodePass) flood() {
+ var methods []methodref
+ for !d.wq.empty() {
+ symIdx := d.wq.pop()
+
+ d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx)
+
+ isgotype := d.ldr.IsGoType(symIdx)
+ relocs := d.ldr.Relocs(symIdx)
+ var usedInIface bool
+
+ if isgotype {
+ if d.dynlink {
+ // When dynamic linking, a type may be passed across DSO
+ // boundary and get converted to interface at the other side.
+ d.ldr.SetAttrUsedInIface(symIdx, true)
+ }
+ usedInIface = d.ldr.AttrUsedInIface(symIdx)
+ }
+
+ methods = methods[:0]
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At(i)
+ // When build with "-linkshared", we can't tell if the interface
+ // method in itab will be used or not. Ignore the weak attribute.
+ if r.Weak() && !(d.ctxt.linkShared && d.ldr.IsItab(symIdx)) {
+ continue
+ }
+ t := r.Type()
+ switch t {
+ case objabi.R_METHODOFF:
+ if i+2 >= relocs.Count() {
+ panic("expect three consecutive R_METHODOFF relocs")
+ }
+ if usedInIface {
+ methods = append(methods, methodref{src: symIdx, r: i})
+ // The method descriptor is itself a type descriptor, and
+ // it can be used to reach other types, e.g. by using
+ // reflect.Type.Method(i).Type.In(j). We need to traverse
+ // its child types with UsedInIface set. (See also the
+ // comment below.)
+ rs := r.Sym()
+ if !d.ldr.AttrUsedInIface(rs) {
+ d.ldr.SetAttrUsedInIface(rs, true)
+ if d.ldr.AttrReachable(rs) {
+ d.ldr.SetAttrReachable(rs, false)
+ d.mark(rs, symIdx)
+ }
+ }
+ }
+ i += 2
+ continue
+ case objabi.R_USETYPE:
+ // type symbol used for DWARF. we need to load the symbol but it may not
+ // be otherwise reachable in the program.
+ // do nothing for now as we still load all type symbols.
+ continue
+ case objabi.R_USEIFACE:
+ // R_USEIFACE is a marker relocation that tells the linker the type is
+ // converted to an interface, i.e. should have UsedInIface set. See the
+ // comment below for why we need to unset the Reachable bit and re-mark it.
+ rs := r.Sym()
+ if !d.ldr.AttrUsedInIface(rs) {
+ d.ldr.SetAttrUsedInIface(rs, true)
+ if d.ldr.AttrReachable(rs) {
+ d.ldr.SetAttrReachable(rs, false)
+ d.mark(rs, symIdx)
+ }
+ }
+ continue
+ case objabi.R_USEIFACEMETHOD:
+ // R_USEIFACEMETHOD is a marker relocation that marks an interface
+ // method as used.
+ rs := r.Sym()
+ if d.ctxt.linkShared && (d.ldr.SymType(rs) == sym.SDYNIMPORT || d.ldr.SymType(rs) == sym.Sxxx) {
+ // Don't decode symbol from shared library (we'll mark all exported methods anyway).
+ // We check for both SDYNIMPORT and Sxxx because name-mangled symbols haven't
+ // been resolved at this point.
+ continue
+ }
+ m := d.decodeIfaceMethod(d.ldr, d.ctxt.Arch, rs, r.Add())
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("reached iface method: %v\n", m)
+ }
+ d.ifaceMethod[m] = true
+ continue
+ case objabi.R_USEGENERICIFACEMETHOD:
+ name := d.decodeGenericIfaceMethod(d.ldr, r.Sym())
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("reached generic iface method: %s\n", name)
+ }
+ d.genericIfaceMethod[name] = true
+ continue // don't mark referenced symbol - it is not needed in the final binary.
+ }
+ rs := r.Sym()
+ if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) {
+ // If a type is converted to an interface, it is possible to obtain an
+ // interface with a "child" type of it using reflection (e.g. obtain an
+ // interface of T from []chan T). We need to traverse its "child" types
+ // with UsedInIface attribute set.
+ // When visiting the child type (chan T in the example above), it will
+ // have UsedInIface set, so it in turn will mark and (re)visit its children
+ // (e.g. T above).
+ // We unset the reachable bit here, so if the child type is already visited,
+ // it will be visited again.
+ // Note that a type symbol can be visited at most twice, one without
+ // UsedInIface and one with. So termination is still guaranteed.
+ d.ldr.SetAttrUsedInIface(rs, true)
+ d.ldr.SetAttrReachable(rs, false)
+ }
+ d.mark(rs, symIdx)
+ }
+ naux := d.ldr.NAux(symIdx)
+ for i := 0; i < naux; i++ {
+ a := d.ldr.Aux(symIdx, i)
+ if a.Type() == goobj.AuxGotype {
+ // A symbol being reachable doesn't imply we need its
+ // type descriptor. Don't mark it.
+ continue
+ }
+ d.mark(a.Sym(), symIdx)
+ }
+ // Some host object symbols have an outer object, which acts like a
+ // "carrier" symbol, or it holds all the symbols for a particular
+ // section. We need to mark all "referenced" symbols from that carrier,
+ // so we make sure we're pulling in all outer symbols, and their sub
+ // symbols. This is not ideal, and these carrier/section symbols could
+ // be removed.
+ if d.ldr.IsExternal(symIdx) {
+ d.mark(d.ldr.OuterSym(symIdx), symIdx)
+ d.mark(d.ldr.SubSym(symIdx), symIdx)
+ }
+
+ if len(methods) != 0 {
+ if !isgotype {
+ panic("method found on non-type symbol")
+ }
+ // Decode runtime type information for type methods
+ // to help work out which methods can be called
+ // dynamically via interfaces.
+ methodsigs := d.decodetypeMethods(d.ldr, d.ctxt.Arch, symIdx, &relocs)
+ if len(methods) != len(methodsigs) {
+ panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs)))
+ }
+ for i, m := range methodsigs {
+ methods[i].m = m
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("markable method: %v of sym %v %s\n", m, symIdx, d.ldr.SymName(symIdx))
+ }
+ }
+ d.markableMethods = append(d.markableMethods, methods...)
+ }
+ }
+}
+
+func (d *deadcodePass) mark(symIdx, parent loader.Sym) {
+ if symIdx != 0 && !d.ldr.AttrReachable(symIdx) {
+ d.wq.push(symIdx)
+ d.ldr.SetAttrReachable(symIdx, true)
+ if buildcfg.Experiment.FieldTrack && d.ldr.Reachparent[symIdx] == 0 {
+ d.ldr.Reachparent[symIdx] = parent
+ }
+ if *flagDumpDep {
+ to := d.ldr.SymName(symIdx)
+ if to != "" {
+ if d.ldr.AttrUsedInIface(symIdx) {
+ to += " <UsedInIface>"
+ }
+ from := "_"
+ if parent != 0 {
+ from = d.ldr.SymName(parent)
+ if d.ldr.AttrUsedInIface(parent) {
+ from += " <UsedInIface>"
+ }
+ }
+ fmt.Printf("%s -> %s\n", from, to)
+ }
+ }
+ }
+}
+
+func (d *deadcodePass) markMethod(m methodref) {
+ relocs := d.ldr.Relocs(m.src)
+ d.mark(relocs.At(m.r).Sym(), m.src)
+ d.mark(relocs.At(m.r+1).Sym(), m.src)
+ d.mark(relocs.At(m.r+2).Sym(), m.src)
+}
+
+// deadcode marks all reachable symbols.
+//
+// The basis of the dead code elimination is a flood fill of symbols,
+// following their relocations, beginning at *flagEntrySymbol.
+//
+// This flood fill is wrapped in logic for pruning unused methods.
+// All methods are mentioned by relocations on their receiver's *rtype.
+// These relocations are specially defined as R_METHODOFF by the compiler
+// so we can detect and manipulated them here.
+//
+// There are three ways a method of a reachable type can be invoked:
+//
+// 1. direct call
+// 2. through a reachable interface type
+// 3. reflect.Value.Method (or MethodByName), or reflect.Type.Method
+// (or MethodByName)
+//
+// The first case is handled by the flood fill, a directly called method
+// is marked as reachable.
+//
+// The second case is handled by decomposing all reachable interface
+// types into method signatures. Each encountered method is compared
+// against the interface method signatures, if it matches it is marked
+// as reachable. This is extremely conservative, but easy and correct.
+//
+// The third case is handled by looking to see if any of:
+// - reflect.Value.Method or MethodByName is reachable
+// - reflect.Type.Method or MethodByName is called (through the
+// REFLECTMETHOD attribute marked by the compiler).
+//
+// If any of these happen, all bets are off and all exported methods
+// of reachable types are marked reachable.
+//
+// Any unreached text symbols are removed from ctxt.Textp.
+func deadcode(ctxt *Link) {
+ ldr := ctxt.loader
+ d := deadcodePass{ctxt: ctxt, ldr: ldr}
+ d.init()
+ d.flood()
+
+ methSym := ldr.Lookup("reflect.Value.Method", abiInternalVer)
+ methByNameSym := ldr.Lookup("reflect.Value.MethodByName", abiInternalVer)
+
+ if ctxt.DynlinkingGo() {
+ // Exported methods may satisfy interfaces we don't know
+ // about yet when dynamically linking.
+ d.reflectSeen = true
+ }
+
+ for {
+ // Methods might be called via reflection. Give up on
+ // static analysis, mark all exported methods of
+ // all reachable types as reachable.
+ d.reflectSeen = d.reflectSeen || (methSym != 0 && ldr.AttrReachable(methSym)) || (methByNameSym != 0 && ldr.AttrReachable(methByNameSym))
+
+ // Mark all methods that could satisfy a discovered
+ // interface as reachable. We recheck old marked interfaces
+ // as new types (with new methods) may have been discovered
+ // in the last pass.
+ rem := d.markableMethods[:0]
+ for _, m := range d.markableMethods {
+ if (d.reflectSeen && (m.isExported() || d.dynlink)) || d.ifaceMethod[m.m] || d.genericIfaceMethod[m.m.name] {
+ d.markMethod(m)
+ } else {
+ rem = append(rem, m)
+ }
+ }
+ d.markableMethods = rem
+
+ if d.wq.empty() {
+ // No new work was discovered. Done.
+ break
+ }
+ d.flood()
+ }
+}
+
+// methodsig is a typed method signature (name + type).
+type methodsig struct {
+ name string
+ typ loader.Sym // type descriptor symbol of the function
+}
+
+// methodref holds the relocations from a receiver type symbol to its
+// method. There are three relocations, one for each of the fields in
+// the reflect.method struct: mtyp, ifn, and tfn.
+type methodref struct {
+ m methodsig
+ src loader.Sym // receiver type symbol
+ r int // the index of R_METHODOFF relocations
+}
+
+func (m methodref) isExported() bool {
+ for _, r := range m.m.name {
+ return unicode.IsUpper(r)
+ }
+ panic("methodref has no signature")
+}
+
+// decodeMethodSig decodes an array of method signature information.
+// Each element of the array is size bytes. The first 4 bytes is a
+// nameOff for the method name, and the next 4 bytes is a typeOff for
+// the function type.
+//
+// Conveniently this is the layout of both runtime.method and runtime.imethod.
+func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig {
+ if cap(d.methodsigstmp) < count {
+ d.methodsigstmp = append(d.methodsigstmp[:0], make([]methodsig, count)...)
+ }
+ var methods = d.methodsigstmp[:count]
+ for i := 0; i < count; i++ {
+ methods[i].name = decodetypeName(ldr, symIdx, relocs, off)
+ methods[i].typ = decodeRelocSym(ldr, symIdx, relocs, int32(off+4))
+ off += size
+ }
+ return methods
+}
+
+// Decode the method of interface type symbol symIdx at offset off.
+func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off int64) methodsig {
+ p := ldr.Data(symIdx)
+ if p == nil {
+ panic(fmt.Sprintf("missing symbol %q", ldr.SymName(symIdx)))
+ }
+ if decodetypeKind(arch, p)&kindMask != kindInterface {
+ panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
+ }
+ relocs := ldr.Relocs(symIdx)
+ var m methodsig
+ m.name = decodetypeName(ldr, symIdx, &relocs, int(off))
+ m.typ = decodeRelocSym(ldr, symIdx, &relocs, int32(off+4))
+ return m
+}
+
+// Decode the method name stored in symbol symIdx. The symbol should contain just the bytes of a method name.
+func (d *deadcodePass) decodeGenericIfaceMethod(ldr *loader.Loader, symIdx loader.Sym) string {
+ return string(ldr.Data(symIdx))
+}
+
+func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
+ p := ldr.Data(symIdx)
+ if !decodetypeHasUncommon(arch, p) {
+ panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx)))
+ }
+ off := commonsize(arch) // reflect.rtype
+ switch decodetypeKind(arch, p) & kindMask {
+ case kindStruct: // reflect.structType
+ off += 4 * arch.PtrSize
+ case kindPtr: // reflect.ptrType
+ off += arch.PtrSize
+ case kindFunc: // reflect.funcType
+ off += arch.PtrSize // 4 bytes, pointer aligned
+ case kindSlice: // reflect.sliceType
+ off += arch.PtrSize
+ case kindArray: // reflect.arrayType
+ off += 3 * arch.PtrSize
+ case kindChan: // reflect.chanType
+ off += 2 * arch.PtrSize
+ case kindMap: // reflect.mapType
+ off += 4*arch.PtrSize + 8
+ case kindInterface: // reflect.interfaceType
+ off += 3 * arch.PtrSize
+ default:
+ // just Sizeof(rtype)
+ }
+
+ mcount := int(decodeInuxi(arch, p[off+4:], 2))
+ moff := int(decodeInuxi(arch, p[off+4+2+2:], 4))
+ off += moff // offset to array of reflect.method values
+ const sizeofMethod = 4 * 4 // sizeof reflect.method in program
+ return d.decodeMethodSig(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount)
+}
diff --git a/src/cmd/link/internal/ld/deadcode_test.go b/src/cmd/link/internal/ld/deadcode_test.go
new file mode 100644
index 0000000..573bff3
--- /dev/null
+++ b/src/cmd/link/internal/ld/deadcode_test.go
@@ -0,0 +1,50 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+ "bytes"
+ "internal/testenv"
+ "path/filepath"
+ "testing"
+)
+
+func TestDeadcode(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ tmpdir := t.TempDir()
+
+ tests := []struct {
+ src string
+ pos, neg string // positive and negative patterns
+ }{
+ {"reflectcall", "", "main.T.M"},
+ {"typedesc", "", "type:main.T"},
+ {"ifacemethod", "", "main.T.M"},
+ {"ifacemethod2", "main.T.M", ""},
+ {"ifacemethod3", "main.S.M", ""},
+ {"ifacemethod4", "", "main.T.M"},
+ }
+ for _, test := range tests {
+ test := test
+ t.Run(test.src, func(t *testing.T) {
+ t.Parallel()
+ src := filepath.Join("testdata", "deadcode", test.src+".go")
+ exe := filepath.Join(tmpdir, test.src+".exe")
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-dumpdep", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+ }
+ if test.pos != "" && !bytes.Contains(out, []byte(test.pos+"\n")) {
+ t.Errorf("%s should be reachable. Output:\n%s", test.pos, out)
+ }
+ if test.neg != "" && bytes.Contains(out, []byte(test.neg+"\n")) {
+ t.Errorf("%s should not be reachable. Output:\n%s", test.neg, out)
+ }
+ })
+ }
+}
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
new file mode 100644
index 0000000..b0f4b87
--- /dev/null
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -0,0 +1,310 @@
+// Copyright 2012 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.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/binary"
+ "log"
+)
+
+// Decoding the type.* symbols. This has to be in sync with
+// ../../runtime/type.go, or more specifically, with what
+// cmd/compile/internal/reflectdata/reflect.go stuffs in these.
+
+// tflag is documented in reflect/type.go.
+//
+// tflag values must be kept in sync with copies in:
+//
+// cmd/compile/internal/reflectdata/reflect.go
+// cmd/link/internal/ld/decodesym.go
+// reflect/type.go
+// runtime/type.go
+const (
+ tflagUncommon = 1 << 0
+ tflagExtraStar = 1 << 1
+)
+
+func decodeInuxi(arch *sys.Arch, p []byte, sz int) uint64 {
+ switch sz {
+ case 2:
+ return uint64(arch.ByteOrder.Uint16(p))
+ case 4:
+ return uint64(arch.ByteOrder.Uint32(p))
+ case 8:
+ return arch.ByteOrder.Uint64(p)
+ default:
+ Exitf("dwarf: decode inuxi %d", sz)
+ panic("unreachable")
+ }
+}
+
+func commonsize(arch *sys.Arch) int { return 4*arch.PtrSize + 8 + 8 } // runtime._type
+func structfieldSize(arch *sys.Arch) int { return 3 * arch.PtrSize } // runtime.structfield
+func uncommonSize() int { return 4 + 2 + 2 + 4 + 4 } // runtime.uncommontype
+
+// Type.commonType.kind
+func decodetypeKind(arch *sys.Arch, p []byte) uint8 {
+ return p[2*arch.PtrSize+7] & objabi.KindMask // 0x13 / 0x1f
+}
+
+// Type.commonType.kind
+func decodetypeUsegcprog(arch *sys.Arch, p []byte) uint8 {
+ return p[2*arch.PtrSize+7] & objabi.KindGCProg // 0x13 / 0x1f
+}
+
+// Type.commonType.size
+func decodetypeSize(arch *sys.Arch, p []byte) int64 {
+ return int64(decodeInuxi(arch, p, arch.PtrSize)) // 0x8 / 0x10
+}
+
+// Type.commonType.ptrdata
+func decodetypePtrdata(arch *sys.Arch, p []byte) int64 {
+ return int64(decodeInuxi(arch, p[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10
+}
+
+// Type.commonType.tflag
+func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool {
+ return p[2*arch.PtrSize+4]&tflagUncommon != 0
+}
+
+// Type.FuncType.dotdotdot
+func decodetypeFuncDotdotdot(arch *sys.Arch, p []byte) bool {
+ return uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2))&(1<<15) != 0
+}
+
+// Type.FuncType.inCount
+func decodetypeFuncInCount(arch *sys.Arch, p []byte) int {
+ return int(decodeInuxi(arch, p[commonsize(arch):], 2))
+}
+
+func decodetypeFuncOutCount(arch *sys.Arch, p []byte) int {
+ return int(uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2)) & (1<<15 - 1))
+}
+
+// InterfaceType.methods.length
+func decodetypeIfaceMethodCount(arch *sys.Arch, p []byte) int64 {
+ return int64(decodeInuxi(arch, p[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+// Matches runtime/typekind.go and reflect.Kind.
+const (
+ kindArray = 17
+ kindChan = 18
+ kindFunc = 19
+ kindInterface = 20
+ kindMap = 21
+ kindPtr = 22
+ kindSlice = 23
+ kindStruct = 25
+ kindMask = (1 << 5) - 1
+)
+
+func decodeReloc(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Reloc {
+ for j := 0; j < relocs.Count(); j++ {
+ rel := relocs.At(j)
+ if rel.Off() == off {
+ return rel
+ }
+ }
+ return loader.Reloc{}
+}
+
+func decodeRelocSym(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Sym {
+ return decodeReloc(ldr, symIdx, relocs, off).Sym()
+}
+
+// decodetypeName decodes the name from a reflect.name.
+func decodetypeName(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) string {
+ r := decodeRelocSym(ldr, symIdx, relocs, int32(off))
+ if r == 0 {
+ return ""
+ }
+
+ data := ldr.Data(r)
+ nameLen, nameLenLen := binary.Uvarint(data[1:])
+ return string(data[1+nameLenLen : 1+nameLenLen+int(nameLen)])
+}
+
+func decodetypeNameEmbedded(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) bool {
+ r := decodeRelocSym(ldr, symIdx, relocs, int32(off))
+ if r == 0 {
+ return false
+ }
+ data := ldr.Data(r)
+ return data[0]&(1<<3) != 0
+}
+
+func decodetypeFuncInType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
+ uadd := commonsize(arch) + 4
+ if arch.PtrSize == 8 {
+ uadd += 4
+ }
+ if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
+ uadd += uncommonSize()
+ }
+ return decodeRelocSym(ldr, symIdx, relocs, int32(uadd+i*arch.PtrSize))
+}
+
+func decodetypeFuncOutType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
+ return decodetypeFuncInType(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
+}
+
+func decodetypeArrayElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeArrayLen(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int64 {
+ data := ldr.Data(symIdx)
+ return int64(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeChanElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeMapKey(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeMapValue(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38
+}
+
+func decodetypePtrElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+}
+
+func decodetypeStructFieldCount(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int {
+ data := ldr.Data(symIdx)
+ return int(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeStructFieldArrayOff(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int {
+ data := ldr.Data(symIdx)
+ off := commonsize(arch) + 4*arch.PtrSize
+ if decodetypeHasUncommon(arch, data) {
+ off += uncommonSize()
+ }
+ off += i * structfieldSize(arch)
+ return off
+}
+
+func decodetypeStructFieldName(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) string {
+ off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
+ relocs := ldr.Relocs(symIdx)
+ return decodetypeName(ldr, symIdx, &relocs, off)
+}
+
+func decodetypeStructFieldType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym {
+ off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
+ relocs := ldr.Relocs(symIdx)
+ return decodeRelocSym(ldr, symIdx, &relocs, int32(off+arch.PtrSize))
+}
+
+func decodetypeStructFieldOffset(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 {
+ off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
+ data := ldr.Data(symIdx)
+ return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize))
+}
+
+func decodetypeStructFieldEmbedded(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) bool {
+ off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
+ relocs := ldr.Relocs(symIdx)
+ return decodetypeNameEmbedded(ldr, symIdx, &relocs, off)
+}
+
+// decodetypeStr returns the contents of an rtype's str field (a nameOff).
+func decodetypeStr(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) string {
+ relocs := ldr.Relocs(symIdx)
+ str := decodetypeName(ldr, symIdx, &relocs, 4*arch.PtrSize+8)
+ data := ldr.Data(symIdx)
+ if data[2*arch.PtrSize+4]&tflagExtraStar != 0 {
+ return str[1:]
+ }
+ return str
+}
+
+func decodetypeGcmask(ctxt *Link, s loader.Sym) []byte {
+ if ctxt.loader.SymType(s) == sym.SDYNIMPORT {
+ symData := ctxt.loader.Data(s)
+ addr := decodetypeGcprogShlib(ctxt, symData)
+ ptrdata := decodetypePtrdata(ctxt.Arch, symData)
+ sect := findShlibSection(ctxt, ctxt.loader.SymPkg(s), addr)
+ if sect != nil {
+ bits := ptrdata / int64(ctxt.Arch.PtrSize)
+ r := make([]byte, (bits+7)/8)
+ // ldshlibsyms avoids closing the ELF file so sect.ReadAt works.
+ // If we remove this read (and the ones in decodetypeGcprog), we
+ // can close the file.
+ _, err := sect.ReadAt(r, int64(addr-sect.Addr))
+ if err != nil {
+ log.Fatal(err)
+ }
+ return r
+ }
+ Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s))
+ return nil
+ }
+ relocs := ctxt.loader.Relocs(s)
+ mask := decodeRelocSym(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize))
+ return ctxt.loader.Data(mask)
+}
+
+// Type.commonType.gc
+func decodetypeGcprog(ctxt *Link, s loader.Sym) []byte {
+ if ctxt.loader.SymType(s) == sym.SDYNIMPORT {
+ symData := ctxt.loader.Data(s)
+ addr := decodetypeGcprogShlib(ctxt, symData)
+ sect := findShlibSection(ctxt, ctxt.loader.SymPkg(s), addr)
+ if sect != nil {
+ // A gcprog is a 4-byte uint32 indicating length, followed by
+ // the actual program.
+ progsize := make([]byte, 4)
+ _, err := sect.ReadAt(progsize, int64(addr-sect.Addr))
+ if err != nil {
+ log.Fatal(err)
+ }
+ progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize))
+ _, err = sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
+ if err != nil {
+ log.Fatal(err)
+ }
+ return append(progsize, progbytes...)
+ }
+ Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s))
+ return nil
+ }
+ relocs := ctxt.loader.Relocs(s)
+ rs := decodeRelocSym(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize))
+ return ctxt.loader.Data(rs)
+}
+
+// Find the elf.Section of a given shared library that contains a given address.
+func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section {
+ for _, shlib := range ctxt.Shlibs {
+ if shlib.Path == path {
+ for _, sect := range shlib.File.Sections[1:] { // skip the NULL section
+ if sect.Addr <= addr && addr < sect.Addr+sect.Size {
+ return sect
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func decodetypeGcprogShlib(ctxt *Link, data []byte) uint64 {
+ return decodeInuxi(ctxt.Arch, data[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize)
+}
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
new file mode 100644
index 0000000..feea864
--- /dev/null
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -0,0 +1,2328 @@
+// Copyright 2019 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.
+
+// TODO/NICETOHAVE:
+// - eliminate DW_CLS_ if not used
+// - package info in compilation units
+// - assign types to their packages
+// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg
+// ptype struct '[]uint8' and qualifiers need to be quoted away
+// - file:line info for variables
+// - make strings a typedef so prettyprinters can see the underlying string type
+
+package ld
+
+import (
+ "cmd/internal/dwarf"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "fmt"
+ "internal/buildcfg"
+ "log"
+ "path"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+)
+
+// dwctxt is a wrapper intended to satisfy the method set of
+// dwarf.Context, so that functions like dwarf.PutAttrs will work with
+// DIEs that use loader.Sym as opposed to *sym.Symbol. It is also
+// being used as a place to store tables/maps that are useful as part
+// of type conversion (this is just a convenience; it would be easy to
+// split these things out into another type if need be).
+type dwctxt struct {
+ linkctxt *Link
+ ldr *loader.Loader
+ arch *sys.Arch
+
+ // This maps type name string (e.g. "uintptr") to loader symbol for
+ // the DWARF DIE for that type (e.g. "go:info.type.uintptr")
+ tmap map[string]loader.Sym
+
+ // This maps loader symbol for the DWARF DIE symbol generated for
+ // a type (e.g. "go:info.uintptr") to the type symbol itself
+ // ("type:uintptr").
+ // FIXME: try converting this map (and the next one) to a single
+ // array indexed by loader.Sym -- this may perform better.
+ rtmap map[loader.Sym]loader.Sym
+
+ // This maps Go type symbol (e.g. "type:XXX") to loader symbol for
+ // the typedef DIE for that type (e.g. "go:info.XXX..def")
+ tdmap map[loader.Sym]loader.Sym
+
+ // Cache these type symbols, so as to avoid repeatedly looking them up
+ typeRuntimeEface loader.Sym
+ typeRuntimeIface loader.Sym
+ uintptrInfoSym loader.Sym
+
+ // Used at various points in that parallel portion of DWARF gen to
+ // protect against conflicting updates to globals (such as "gdbscript")
+ dwmu *sync.Mutex
+}
+
+// dwSym wraps a loader.Sym; this type is meant to obey the interface
+// rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and
+// DwAttr objects contain references to symbols via this type.
+type dwSym loader.Sym
+
+func (s dwSym) Length(dwarfContext interface{}) int64 {
+ l := dwarfContext.(dwctxt).ldr
+ return int64(len(l.Data(loader.Sym(s))))
+}
+
+func (c dwctxt) PtrSize() int {
+ return c.arch.PtrSize
+}
+
+func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ dsu.AddUintXX(c.arch, uint64(i), size)
+}
+
+func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ dsu.AddBytes(b)
+}
+
+func (c dwctxt) AddString(s dwarf.Sym, v string) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ dsu.Addstring(v)
+}
+
+func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ if value != 0 {
+ value -= dsu.Value()
+ }
+ tgtds := loader.Sym(data.(dwSym))
+ dsu.AddAddrPlus(c.arch, tgtds, value)
+}
+
+func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ if value != 0 {
+ value -= dsu.Value()
+ }
+ tgtds := loader.Sym(data.(dwSym))
+ dsu.AddCURelativeAddrPlus(c.arch, tgtds, value)
+}
+
+func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ tds := loader.Sym(t.(dwSym))
+ switch size {
+ default:
+ c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size)
+ case c.arch.PtrSize, 4:
+ }
+ dsu.AddSymRef(c.arch, tds, ofs, objabi.R_ADDROFF, size)
+}
+
+func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
+ size := 4
+ if isDwarf64(c.linkctxt) {
+ size = 8
+ }
+ ds := loader.Sym(s.(dwSym))
+ dsu := c.ldr.MakeSymbolUpdater(ds)
+ tds := loader.Sym(t.(dwSym))
+ switch size {
+ default:
+ c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size)
+ case c.arch.PtrSize, 4:
+ }
+ dsu.AddSymRef(c.arch, tds, ofs, objabi.R_DWARFSECREF, size)
+}
+
+func (c dwctxt) Logf(format string, args ...interface{}) {
+ c.linkctxt.Logf(format, args...)
+}
+
+// At the moment these interfaces are only used in the compiler.
+
+func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) {
+ panic("should be used only in the compiler")
+}
+
+func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 {
+ panic("should be used only in the compiler")
+}
+
+func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) {
+ panic("should be used only in the compiler")
+}
+
+func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
+ panic("should be used only in the compiler")
+}
+
+func isDwarf64(ctxt *Link) bool {
+ return ctxt.HeadType == objabi.Haix
+}
+
+// https://sourceware.org/gdb/onlinedocs/gdb/dotdebug_005fgdb_005fscripts-section.html
+// Each entry inside .debug_gdb_scripts section begins with a non-null prefix
+// byte that specifies the kind of entry. The following entries are supported:
+const (
+ GdbScriptPythonFileId = 1
+ GdbScriptSchemeFileId = 3
+ GdbScriptPythonTextId = 4
+ GdbScriptSchemeTextId = 6
+)
+
+var gdbscript string
+
+// dwarfSecInfo holds information about a DWARF output section,
+// specifically a section symbol and a list of symbols contained in
+// that section. On the syms list, the first symbol will always be the
+// section symbol, then any remaining symbols (if any) will be
+// sub-symbols in that section. Note that for some sections (eg:
+// .debug_abbrev), the section symbol is all there is (all content is
+// contained in it). For other sections (eg: .debug_info), the section
+// symbol is empty and all the content is in the sub-symbols. Finally
+// there are some sections (eg: .debug_ranges) where it is a mix (both
+// the section symbol and the sub-symbols have content)
+type dwarfSecInfo struct {
+ syms []loader.Sym
+}
+
+// secSym returns the section symbol for the section.
+func (dsi *dwarfSecInfo) secSym() loader.Sym {
+ if len(dsi.syms) == 0 {
+ return 0
+ }
+ return dsi.syms[0]
+}
+
+// subSyms returns a list of sub-symbols for the section.
+func (dsi *dwarfSecInfo) subSyms() []loader.Sym {
+ if len(dsi.syms) == 0 {
+ return []loader.Sym{}
+ }
+ return dsi.syms[1:]
+}
+
+// dwarfp stores the collected DWARF symbols created during
+// dwarf generation.
+var dwarfp []dwarfSecInfo
+
+func (d *dwctxt) writeabbrev() dwarfSecInfo {
+ abrvs := d.ldr.CreateSymForUpdate(".debug_abbrev", 0)
+ abrvs.SetType(sym.SDWARFSECT)
+ abrvs.AddBytes(dwarf.GetAbbrev())
+ return dwarfSecInfo{syms: []loader.Sym{abrvs.Sym()}}
+}
+
+var dwtypes dwarf.DWDie
+
+// newattr attaches a new attribute to the specified DIE.
+//
+// FIXME: at the moment attributes are stored in a linked list in a
+// fairly space-inefficient way -- it might be better to instead look
+// up all attrs in a single large table, then store indices into the
+// table in the DIE. This would allow us to common up storage for
+// attributes that are shared by many DIEs (ex: byte size of N).
+func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) {
+ a := new(dwarf.DWAttr)
+ a.Link = die.Attr
+ die.Attr = a
+ a.Atr = attr
+ a.Cls = uint8(cls)
+ a.Value = value
+ a.Data = data
+}
+
+// Each DIE (except the root ones) has at least 1 attribute: its
+// name. getattr moves the desired one to the front so
+// frequently searched ones are found faster.
+func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr {
+ if die.Attr.Atr == attr {
+ return die.Attr
+ }
+
+ a := die.Attr
+ b := a.Link
+ for b != nil {
+ if b.Atr == attr {
+ a.Link = b.Link
+ b.Link = die.Attr
+ die.Attr = b
+ return b
+ }
+
+ a = b
+ b = b.Link
+ }
+
+ return nil
+}
+
+// Every DIE manufactured by the linker has at least an AT_name
+// attribute (but it will only be written out if it is listed in the abbrev).
+// The compiler does create nameless DWARF DIEs (ex: concrete subprogram
+// instance).
+// FIXME: it would be more efficient to bulk-allocate DIEs.
+func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string) *dwarf.DWDie {
+ die := new(dwarf.DWDie)
+ die.Abbrev = abbrev
+ die.Link = parent.Child
+ parent.Child = die
+
+ newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name)
+
+ // Sanity check: all DIEs created in the linker should be named.
+ if name == "" {
+ panic("nameless DWARF DIE")
+ }
+
+ var st sym.SymKind
+ switch abbrev {
+ case dwarf.DW_ABRV_FUNCTYPEPARAM, dwarf.DW_ABRV_DOTDOTDOT, dwarf.DW_ABRV_STRUCTFIELD, dwarf.DW_ABRV_ARRAYRANGE:
+ // There are no relocations against these dies, and their names
+ // are not unique, so don't create a symbol.
+ return die
+ case dwarf.DW_ABRV_COMPUNIT, dwarf.DW_ABRV_COMPUNIT_TEXTLESS:
+ // Avoid collisions with "real" symbol names.
+ name = fmt.Sprintf(".pkg.%s.%d", name, len(d.linkctxt.compUnits))
+ st = sym.SDWARFCUINFO
+ case dwarf.DW_ABRV_VARIABLE:
+ st = sym.SDWARFVAR
+ default:
+ // Everything else is assigned a type of SDWARFTYPE. that
+ // this also includes loose ends such as STRUCT_FIELD.
+ st = sym.SDWARFTYPE
+ }
+ ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, 0)
+ dsu := d.ldr.MakeSymbolUpdater(ds)
+ dsu.SetType(st)
+ d.ldr.SetAttrNotInSymbolTable(ds, true)
+ d.ldr.SetAttrReachable(ds, true)
+ die.Sym = dwSym(ds)
+ if abbrev >= dwarf.DW_ABRV_NULLTYPE && abbrev <= dwarf.DW_ABRV_TYPEDECL {
+ d.tmap[name] = ds
+ }
+
+ return die
+}
+
+func walktypedef(die *dwarf.DWDie) *dwarf.DWDie {
+ if die == nil {
+ return nil
+ }
+ // Resolve typedef if present.
+ if die.Abbrev == dwarf.DW_ABRV_TYPEDECL {
+ for attr := die.Attr; attr != nil; attr = attr.Link {
+ if attr.Atr == dwarf.DW_AT_type && attr.Cls == dwarf.DW_CLS_REFERENCE && attr.Data != nil {
+ return attr.Data.(*dwarf.DWDie)
+ }
+ }
+ }
+
+ return die
+}
+
+func (d *dwctxt) walksymtypedef(symIdx loader.Sym) loader.Sym {
+
+ // We're being given the loader symbol for the type DIE, e.g.
+ // "go:info.type.uintptr". Map that first to the type symbol (e.g.
+ // "type:uintptr") and then to the typedef DIE for the type.
+ // FIXME: this seems clunky, maybe there is a better way to do this.
+
+ if ts, ok := d.rtmap[symIdx]; ok {
+ if def, ok := d.tdmap[ts]; ok {
+ return def
+ }
+ d.linkctxt.Errorf(ts, "internal error: no entry for sym %d in tdmap\n", ts)
+ return 0
+ }
+ d.linkctxt.Errorf(symIdx, "internal error: no entry for sym %d in rtmap\n", symIdx)
+ return 0
+}
+
+// Find child by AT_name using hashtable if available or linear scan
+// if not.
+func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie {
+ var prev *dwarf.DWDie
+ for ; die != prev; prev, die = die, walktypedef(die) {
+ for a := die.Child; a != nil; a = a.Link {
+ if name == getattr(a, dwarf.DW_AT_name).Data {
+ return a
+ }
+ }
+ continue
+ }
+ return nil
+}
+
+// find looks up the loader symbol for the DWARF DIE generated for the
+// type with the specified name.
+func (d *dwctxt) find(name string) loader.Sym {
+ return d.tmap[name]
+}
+
+func (d *dwctxt) mustFind(name string) loader.Sym {
+ r := d.find(name)
+ if r == 0 {
+ Exitf("dwarf find: cannot find %s", name)
+ }
+ return r
+}
+
+func (d *dwctxt) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) {
+ switch size {
+ default:
+ d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size)
+ case d.arch.PtrSize, 4:
+ }
+ sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size)
+}
+
+func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) {
+ if ref == 0 {
+ return
+ }
+ newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref))
+}
+
+func (d *dwctxt) dtolsym(s dwarf.Sym) loader.Sym {
+ if s == nil {
+ return 0
+ }
+ dws := loader.Sym(s.(dwSym))
+ return dws
+}
+
+func (d *dwctxt) putdie(syms []loader.Sym, die *dwarf.DWDie) []loader.Sym {
+ s := d.dtolsym(die.Sym)
+ if s == 0 {
+ s = syms[len(syms)-1]
+ } else {
+ syms = append(syms, s)
+ }
+ sDwsym := dwSym(s)
+ dwarf.Uleb128put(d, sDwsym, int64(die.Abbrev))
+ dwarf.PutAttrs(d, sDwsym, die.Abbrev, die.Attr)
+ if dwarf.HasChildren(die) {
+ for die := die.Child; die != nil; die = die.Link {
+ syms = d.putdie(syms, die)
+ }
+ dsu := d.ldr.MakeSymbolUpdater(syms[len(syms)-1])
+ dsu.AddUint8(0)
+ }
+ return syms
+}
+
+func reverselist(list **dwarf.DWDie) {
+ curr := *list
+ var prev *dwarf.DWDie
+ for curr != nil {
+ next := curr.Link
+ curr.Link = prev
+ prev = curr
+ curr = next
+ }
+
+ *list = prev
+}
+
+func reversetree(list **dwarf.DWDie) {
+ reverselist(list)
+ for die := *list; die != nil; die = die.Link {
+ if dwarf.HasChildren(die) {
+ reversetree(&die.Child)
+ }
+ }
+}
+
+func newmemberoffsetattr(die *dwarf.DWDie, offs int32) {
+ newattr(die, dwarf.DW_AT_data_member_location, dwarf.DW_CLS_CONSTANT, int64(offs), nil)
+}
+
+func (d *dwctxt) lookupOrDiag(n string) loader.Sym {
+ symIdx := d.ldr.Lookup(n, 0)
+ if symIdx == 0 {
+ Exitf("dwarf: missing type: %s", n)
+ }
+ if len(d.ldr.Data(symIdx)) == 0 {
+ Exitf("dwarf: missing type (no data): %s", n)
+ }
+
+ return symIdx
+}
+
+func (d *dwctxt) dotypedef(parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie {
+ // Only emit typedefs for real names.
+ if strings.HasPrefix(name, "map[") {
+ return nil
+ }
+ if strings.HasPrefix(name, "struct {") {
+ return nil
+ }
+ // cmd/compile uses "noalg.struct {...}" as type name when hash and eq algorithm generation of
+ // this struct type is suppressed.
+ if strings.HasPrefix(name, "noalg.struct {") {
+ return nil
+ }
+ if strings.HasPrefix(name, "chan ") {
+ return nil
+ }
+ if name[0] == '[' || name[0] == '*' {
+ return nil
+ }
+ if def == nil {
+ Errorf(nil, "dwarf: bad def in dotypedef")
+ }
+
+ // Create a new loader symbol for the typedef. We no longer
+ // do lookups of typedef symbols by name, so this is going
+ // to be an anonymous symbol (we want this for perf reasons).
+ tds := d.ldr.CreateExtSym("", 0)
+ tdsu := d.ldr.MakeSymbolUpdater(tds)
+ tdsu.SetType(sym.SDWARFTYPE)
+ def.Sym = dwSym(tds)
+ d.ldr.SetAttrNotInSymbolTable(tds, true)
+ d.ldr.SetAttrReachable(tds, true)
+
+ // The typedef entry must be created after the def,
+ // so that future lookups will find the typedef instead
+ // of the real definition. This hooks the typedef into any
+ // circular definition loops, so that gdb can understand them.
+ die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name)
+
+ d.newrefattr(die, dwarf.DW_AT_type, tds)
+
+ return die
+}
+
+// Define gotype, for composite ones recurse into constituents.
+func (d *dwctxt) defgotype(gotype loader.Sym) loader.Sym {
+ if gotype == 0 {
+ return d.mustFind("<unspecified>")
+ }
+
+ // If we already have a tdmap entry for the gotype, return it.
+ if ds, ok := d.tdmap[gotype]; ok {
+ return ds
+ }
+
+ sn := d.ldr.SymName(gotype)
+ if !strings.HasPrefix(sn, "type:") {
+ d.linkctxt.Errorf(gotype, "dwarf: type name doesn't start with \"type:\"")
+ return d.mustFind("<unspecified>")
+ }
+ name := sn[5:] // could also decode from Type.string
+
+ sdie := d.find(name)
+ if sdie != 0 {
+ return sdie
+ }
+
+ gtdwSym := d.newtype(gotype)
+ d.tdmap[gotype] = loader.Sym(gtdwSym.Sym.(dwSym))
+ return loader.Sym(gtdwSym.Sym.(dwSym))
+}
+
+func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie {
+ sn := d.ldr.SymName(gotype)
+ name := sn[5:] // could also decode from Type.string
+ tdata := d.ldr.Data(gotype)
+ if len(tdata) == 0 {
+ d.linkctxt.Errorf(gotype, "missing type")
+ }
+ kind := decodetypeKind(d.arch, tdata)
+ bytesize := decodetypeSize(d.arch, tdata)
+
+ var die, typedefdie *dwarf.DWDie
+ switch kind {
+ case objabi.KindBool:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindInt,
+ objabi.KindInt8,
+ objabi.KindInt16,
+ objabi.KindInt32,
+ objabi.KindInt64:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindUint,
+ objabi.KindUint8,
+ objabi.KindUint16,
+ objabi.KindUint32,
+ objabi.KindUint64,
+ objabi.KindUintptr:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindFloat32,
+ objabi.KindFloat64:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindComplex64,
+ objabi.KindComplex128:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindArray:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+ s := decodetypeArrayElem(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
+ fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range")
+
+ // use actual length not upper bound; correct for 0-length arrays.
+ newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(d.ldr, d.arch, gotype), 0)
+
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+
+ case objabi.KindChan:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name)
+ s := decodetypeChanElem(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s))
+ // Save elem type for synthesizechantypes. We could synthesize here
+ // but that would change the order of DIEs we output.
+ d.newrefattr(die, dwarf.DW_AT_type, s)
+
+ case objabi.KindFunc:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ data := d.ldr.Data(gotype)
+ // FIXME: add caching or reuse reloc slice.
+ relocs := d.ldr.Relocs(gotype)
+ nfields := decodetypeFuncInCount(d.arch, data)
+ for i := 0; i < nfields; i++ {
+ s := decodetypeFuncInType(d.ldr, d.arch, gotype, &relocs, i)
+ sn := d.ldr.SymName(s)
+ fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:])
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
+ }
+
+ if decodetypeFuncDotdotdot(d.arch, data) {
+ d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...")
+ }
+ nfields = decodetypeFuncOutCount(d.arch, data)
+ for i := 0; i < nfields; i++ {
+ s := decodetypeFuncOutType(d.ldr, d.arch, gotype, &relocs, i)
+ sn := d.ldr.SymName(s)
+ fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:])
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s)))
+ }
+
+ case objabi.KindInterface:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ data := d.ldr.Data(gotype)
+ nfields := int(decodetypeIfaceMethodCount(d.arch, data))
+ var s loader.Sym
+ if nfields == 0 {
+ s = d.typeRuntimeEface
+ } else {
+ s = d.typeRuntimeIface
+ }
+ d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
+
+ case objabi.KindMap:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name)
+ s := decodetypeMapKey(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s))
+ s = decodetypeMapValue(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s))
+ // Save gotype for use in synthesizemaptypes. We could synthesize here,
+ // but that would change the order of the DIEs.
+ d.newrefattr(die, dwarf.DW_AT_type, gotype)
+
+ case objabi.KindPtr:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ s := decodetypePtrElem(d.ldr, d.arch, gotype)
+ d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
+
+ case objabi.KindSlice:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+ s := decodetypeArrayElem(d.ldr, d.arch, gotype)
+ elem := d.defgotype(s)
+ d.newrefattr(die, dwarf.DW_AT_go_elem, elem)
+
+ case objabi.KindString:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+
+ case objabi.KindStruct:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name)
+ typedefdie = d.dotypedef(&dwtypes, name, die)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+ nfields := decodetypeStructFieldCount(d.ldr, d.arch, gotype)
+ for i := 0; i < nfields; i++ {
+ f := decodetypeStructFieldName(d.ldr, d.arch, gotype, i)
+ s := decodetypeStructFieldType(d.ldr, d.arch, gotype, i)
+ if f == "" {
+ sn := d.ldr.SymName(s)
+ f = sn[5:] // skip "type:"
+ }
+ fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
+ offset := decodetypeStructFieldOffset(d.ldr, d.arch, gotype, i)
+ newmemberoffsetattr(fld, int32(offset))
+ if decodetypeStructFieldEmbedded(d.ldr, d.arch, gotype, i) {
+ newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0)
+ }
+ }
+
+ case objabi.KindUnsafePointer:
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name)
+
+ default:
+ d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind)
+ die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name)
+ d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("<unspecified>"))
+ }
+
+ newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0)
+
+ if d.ldr.AttrReachable(gotype) {
+ newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gotype))
+ }
+
+ // Sanity check.
+ if _, ok := d.rtmap[gotype]; ok {
+ log.Fatalf("internal error: rtmap entry already installed\n")
+ }
+
+ ds := loader.Sym(die.Sym.(dwSym))
+ if typedefdie != nil {
+ ds = loader.Sym(typedefdie.Sym.(dwSym))
+ }
+ d.rtmap[ds] = gotype
+
+ if _, ok := prototypedies[sn]; ok {
+ prototypedies[sn] = die
+ }
+
+ if typedefdie != nil {
+ return typedefdie
+ }
+ return die
+}
+
+func (d *dwctxt) nameFromDIESym(dwtypeDIESym loader.Sym) string {
+ sn := d.ldr.SymName(dwtypeDIESym)
+ return sn[len(dwarf.InfoPrefix):]
+}
+
+func (d *dwctxt) defptrto(dwtype loader.Sym) loader.Sym {
+
+ // FIXME: it would be nice if the compiler attached an aux symbol
+ // ref from the element type to the pointer type -- it would be
+ // more efficient to do it this way as opposed to via name lookups.
+
+ ptrname := "*" + d.nameFromDIESym(dwtype)
+ if die := d.find(ptrname); die != 0 {
+ return die
+ }
+
+ pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname)
+ d.newrefattr(pdie, dwarf.DW_AT_type, dwtype)
+
+ // The DWARF info synthesizes pointer types that don't exist at the
+ // language level, like *hash<...> and *bucket<...>, and the data
+ // pointers of slices. Link to the ones we can find.
+ gts := d.ldr.Lookup("type:"+ptrname, 0)
+ if gts != 0 && d.ldr.AttrReachable(gts) {
+ newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gts))
+ }
+
+ if gts != 0 {
+ ds := loader.Sym(pdie.Sym.(dwSym))
+ d.rtmap[ds] = gts
+ d.tdmap[gts] = ds
+ }
+
+ return d.dtolsym(pdie.Sym)
+}
+
+// Copies src's children into dst. Copies attributes by value.
+// DWAttr.data is copied as pointer only. If except is one of
+// the top-level children, it will not be copied.
+func (d *dwctxt) copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) {
+ for src = src.Child; src != nil; src = src.Link {
+ if src == except {
+ continue
+ }
+ c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string))
+ for a := src.Attr; a != nil; a = a.Link {
+ newattr(c, a.Atr, int(a.Cls), a.Value, a.Data)
+ }
+ d.copychildrenexcept(ctxt, c, src, nil)
+ }
+
+ reverselist(&dst.Child)
+}
+
+func (d *dwctxt) copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) {
+ d.copychildrenexcept(ctxt, dst, src, nil)
+}
+
+// Search children (assumed to have TAG_member) for the one named
+// field and set its AT_type to dwtype
+func (d *dwctxt) substitutetype(structdie *dwarf.DWDie, field string, dwtype loader.Sym) {
+ child := findchild(structdie, field)
+ if child == nil {
+ Exitf("dwarf substitutetype: %s does not have member %s",
+ getattr(structdie, dwarf.DW_AT_name).Data, field)
+ return
+ }
+
+ a := getattr(child, dwarf.DW_AT_type)
+ if a != nil {
+ a.Data = dwSym(dwtype)
+ } else {
+ d.newrefattr(child, dwarf.DW_AT_type, dwtype)
+ }
+}
+
+func (d *dwctxt) findprotodie(ctxt *Link, name string) *dwarf.DWDie {
+ die, ok := prototypedies[name]
+ if ok && die == nil {
+ d.defgotype(d.lookupOrDiag(name))
+ die = prototypedies[name]
+ }
+ if die == nil {
+ log.Fatalf("internal error: DIE generation failed for %s\n", name)
+ }
+ return die
+}
+
+func (d *dwctxt) synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) {
+ prototype := walktypedef(d.findprotodie(ctxt, "type:runtime.stringStructDWARF"))
+ if prototype == nil {
+ return
+ }
+
+ for ; die != nil; die = die.Link {
+ if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE {
+ continue
+ }
+ d.copychildren(ctxt, die, prototype)
+ }
+}
+
+func (d *dwctxt) synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) {
+ prototype := walktypedef(d.findprotodie(ctxt, "type:runtime.slice"))
+ if prototype == nil {
+ return
+ }
+
+ for ; die != nil; die = die.Link {
+ if die.Abbrev != dwarf.DW_ABRV_SLICETYPE {
+ continue
+ }
+ d.copychildren(ctxt, die, prototype)
+ elem := loader.Sym(getattr(die, dwarf.DW_AT_go_elem).Data.(dwSym))
+ d.substitutetype(die, "array", d.defptrto(elem))
+ }
+}
+
+func mkinternaltypename(base string, arg1 string, arg2 string) string {
+ if arg2 == "" {
+ return fmt.Sprintf("%s<%s>", base, arg1)
+ }
+ return fmt.Sprintf("%s<%s,%s>", base, arg1, arg2)
+}
+
+// synthesizemaptypes is way too closely married to runtime/hashmap.c
+const (
+ MaxKeySize = 128
+ MaxValSize = 128
+ BucketSize = 8
+)
+
+func (d *dwctxt) mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) loader.Sym {
+ name := mkinternaltypename(typename, keyname, valname)
+ symname := dwarf.InfoPrefix + name
+ s := d.ldr.Lookup(symname, 0)
+ if s != 0 && d.ldr.SymType(s) == sym.SDWARFTYPE {
+ return s
+ }
+ die := d.newdie(&dwtypes, abbrev, name)
+ f(die)
+ return d.dtolsym(die.Sym)
+}
+
+func (d *dwctxt) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
+ hash := walktypedef(d.findprotodie(ctxt, "type:runtime.hmap"))
+ bucket := walktypedef(d.findprotodie(ctxt, "type:runtime.bmap"))
+
+ if hash == nil {
+ return
+ }
+
+ for ; die != nil; die = die.Link {
+ if die.Abbrev != dwarf.DW_ABRV_MAPTYPE {
+ continue
+ }
+ gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
+ keytype := decodetypeMapKey(d.ldr, d.arch, gotype)
+ valtype := decodetypeMapValue(d.ldr, d.arch, gotype)
+ keydata := d.ldr.Data(keytype)
+ valdata := d.ldr.Data(valtype)
+ keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata)
+ keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype))
+
+ // compute size info like hashmap.c does.
+ indirectKey, indirectVal := false, false
+ if keysize > MaxKeySize {
+ keysize = int64(d.arch.PtrSize)
+ indirectKey = true
+ }
+ if valsize > MaxValSize {
+ valsize = int64(d.arch.PtrSize)
+ indirectVal = true
+ }
+
+ // Construct type to represent an array of BucketSize keys
+ keyname := d.nameFromDIESym(keytype)
+ dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) {
+ newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0)
+ t := keytype
+ if indirectKey {
+ t = d.defptrto(keytype)
+ }
+ d.newrefattr(dwhk, dwarf.DW_AT_type, t)
+ fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size")
+ newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+ })
+
+ // Construct type to represent an array of BucketSize values
+ valname := d.nameFromDIESym(valtype)
+ dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) {
+ newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0)
+ t := valtype
+ if indirectVal {
+ t = d.defptrto(valtype)
+ }
+ d.newrefattr(dwhv, dwarf.DW_AT_type, t)
+ fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size")
+ newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+ })
+
+ // Construct bucket<K,V>
+ dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) {
+ // Copy over all fields except the field "data" from the generic
+ // bucket. "data" will be replaced with keys/values below.
+ d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
+
+ fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys")
+ d.newrefattr(fld, dwarf.DW_AT_type, dwhks)
+ newmemberoffsetattr(fld, BucketSize)
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values")
+ d.newrefattr(fld, dwarf.DW_AT_type, dwhvs)
+ newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize))
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow")
+ d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym)))
+ newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize)))
+ if d.arch.RegSize > d.arch.PtrSize {
+ fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad")
+ d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
+ newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize))
+ }
+
+ newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(d.arch.RegSize), 0)
+ })
+
+ // Construct hash<K,V>
+ dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) {
+ d.copychildren(ctxt, dwh, hash)
+ d.substitutetype(dwh, "buckets", d.defptrto(dwhbs))
+ d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs))
+ newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil)
+ })
+
+ // make map type a pointer to hash<K,V>
+ d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs))
+ }
+}
+
+func (d *dwctxt) synthesizechantypes(ctxt *Link, die *dwarf.DWDie) {
+ sudog := walktypedef(d.findprotodie(ctxt, "type:runtime.sudog"))
+ waitq := walktypedef(d.findprotodie(ctxt, "type:runtime.waitq"))
+ hchan := walktypedef(d.findprotodie(ctxt, "type:runtime.hchan"))
+ if sudog == nil || waitq == nil || hchan == nil {
+ return
+ }
+
+ sudogsize := int(getattr(sudog, dwarf.DW_AT_byte_size).Value)
+
+ for ; die != nil; die = die.Link {
+ if die.Abbrev != dwarf.DW_ABRV_CHANTYPE {
+ continue
+ }
+ elemgotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
+ tname := d.ldr.SymName(elemgotype)
+ elemname := tname[5:]
+ elemtype := d.walksymtypedef(d.defgotype(d.lookupOrDiag(tname)))
+
+ // sudog<T>
+ dwss := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) {
+ d.copychildren(ctxt, dws, sudog)
+ d.substitutetype(dws, "elem", d.defptrto(elemtype))
+ newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil)
+ })
+
+ // waitq<T>
+ dwws := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) {
+
+ d.copychildren(ctxt, dww, waitq)
+ d.substitutetype(dww, "first", d.defptrto(dwss))
+ d.substitutetype(dww, "last", d.defptrto(dwss))
+ newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil)
+ })
+
+ // hchan<T>
+ dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) {
+ d.copychildren(ctxt, dwh, hchan)
+ d.substitutetype(dwh, "recvq", dwws)
+ d.substitutetype(dwh, "sendq", dwws)
+ newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil)
+ })
+
+ d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs))
+ }
+}
+
+// createUnitLength creates the initial length field with value v and update
+// offset of unit_length if needed.
+func (d *dwctxt) createUnitLength(su *loader.SymbolBuilder, v uint64) {
+ if isDwarf64(d.linkctxt) {
+ su.AddUint32(d.arch, 0xFFFFFFFF)
+ }
+ d.addDwarfAddrField(su, v)
+}
+
+// addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits.
+func (d *dwctxt) addDwarfAddrField(sb *loader.SymbolBuilder, v uint64) {
+ if isDwarf64(d.linkctxt) {
+ sb.AddUint(d.arch, v)
+ } else {
+ sb.AddUint32(d.arch, uint32(v))
+ }
+}
+
+// addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits.
+func (d *dwctxt) addDwarfAddrRef(sb *loader.SymbolBuilder, t loader.Sym) {
+ if isDwarf64(d.linkctxt) {
+ d.adddwarfref(sb, t, 8)
+ } else {
+ d.adddwarfref(sb, t, 4)
+ }
+}
+
+// calcCompUnitRanges calculates the PC ranges of the compilation units.
+func (d *dwctxt) calcCompUnitRanges() {
+ var prevUnit *sym.CompilationUnit
+ for _, s := range d.linkctxt.Textp {
+ sym := loader.Sym(s)
+
+ fi := d.ldr.FuncInfo(sym)
+ if !fi.Valid() {
+ continue
+ }
+
+ // Skip linker-created functions (ex: runtime.addmoduledata), since they
+ // don't have DWARF to begin with.
+ unit := d.ldr.SymUnit(sym)
+ if unit == nil {
+ continue
+ }
+
+ // Update PC ranges.
+ //
+ // We don't simply compare the end of the previous
+ // symbol with the start of the next because there's
+ // often a little padding between them. Instead, we
+ // only create boundaries between symbols from
+ // different units.
+ sval := d.ldr.SymValue(sym)
+ u0val := d.ldr.SymValue(loader.Sym(unit.Textp[0]))
+ if prevUnit != unit {
+ unit.PCs = append(unit.PCs, dwarf.Range{Start: sval - u0val})
+ prevUnit = unit
+ }
+ unit.PCs[len(unit.PCs)-1].End = sval - u0val + int64(len(d.ldr.Data(sym)))
+ }
+}
+
+func movetomodule(ctxt *Link, parent *dwarf.DWDie) {
+ die := ctxt.runtimeCU.DWInfo.Child
+ if die == nil {
+ ctxt.runtimeCU.DWInfo.Child = parent.Child
+ return
+ }
+ for die.Link != nil {
+ die = die.Link
+ }
+ die.Link = parent.Child
+}
+
+/*
+ * Generate a sequence of opcodes that is as short as possible.
+ * See section 6.2.5
+ */
+const (
+ LINE_BASE = -4
+ LINE_RANGE = 10
+ PC_RANGE = (255 - OPCODE_BASE) / LINE_RANGE
+ OPCODE_BASE = 11
+)
+
+/*
+ * Walk prog table, emit line program and build DIE tree.
+ */
+
+func getCompilationDir() string {
+ // OSX requires this be set to something, but it's not easy to choose
+ // a value. Linking takes place in a temporary directory, so there's
+ // no point including it here. Paths in the file table are usually
+ // absolute, in which case debuggers will ignore this value. -trimpath
+ // produces relative paths, but we don't know where they start, so
+ // all we can do here is try not to make things worse.
+ return "."
+}
+
+func (d *dwctxt) importInfoSymbol(dsym loader.Sym) {
+ d.ldr.SetAttrReachable(dsym, true)
+ d.ldr.SetAttrNotInSymbolTable(dsym, true)
+ dst := d.ldr.SymType(dsym)
+ if dst != sym.SDWARFCONST && dst != sym.SDWARFABSFCN {
+ log.Fatalf("error: DWARF info sym %d/%s with incorrect type %s", dsym, d.ldr.SymName(dsym), d.ldr.SymType(dsym).String())
+ }
+ relocs := d.ldr.Relocs(dsym)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At(i)
+ if r.Type() != objabi.R_DWARFSECREF {
+ continue
+ }
+ rsym := r.Sym()
+ // If there is an entry for the symbol in our rtmap, then it
+ // means we've processed the type already, and can skip this one.
+ if _, ok := d.rtmap[rsym]; ok {
+ // type already generated
+ continue
+ }
+ // FIXME: is there a way we could avoid materializing the
+ // symbol name here?
+ sn := d.ldr.SymName(rsym)
+ tn := sn[len(dwarf.InfoPrefix):]
+ ts := d.ldr.Lookup("type:"+tn, 0)
+ d.defgotype(ts)
+ }
+}
+
+func expandFile(fname string) string {
+ if strings.HasPrefix(fname, src.FileSymPrefix) {
+ fname = fname[len(src.FileSymPrefix):]
+ }
+ return expandGoroot(fname)
+}
+
+// writeDirFileTables emits the portion of the DWARF line table
+// prologue containing the include directories and file names,
+// described in section 6.2.4 of the DWARF 4 standard. It walks the
+// filepaths for the unit to discover any common directories, which
+// are emitted to the directory table first, then the file table is
+// emitted after that.
+func (d *dwctxt) writeDirFileTables(unit *sym.CompilationUnit, lsu *loader.SymbolBuilder) {
+ type fileDir struct {
+ base string
+ dir int
+ }
+ dirNums := make(map[string]int)
+ dirs := []string{""}
+ files := []fileDir{}
+
+ // Preprocess files to collect directories. This assumes that the
+ // file table is already de-duped.
+ for i, name := range unit.FileTable {
+ name := expandFile(name)
+ if len(name) == 0 {
+ // Can't have empty filenames, and having a unique
+ // filename is quite useful for debugging.
+ name = fmt.Sprintf("<missing>_%d", i)
+ }
+ // Note the use of "path" here and not "filepath". The compiler
+ // hard-codes to use "/" in DWARF paths (even for Windows), so we
+ // want to maintain that here.
+ file := path.Base(name)
+ dir := path.Dir(name)
+ dirIdx, ok := dirNums[dir]
+ if !ok && dir != "." {
+ dirIdx = len(dirNums) + 1
+ dirNums[dir] = dirIdx
+ dirs = append(dirs, dir)
+ }
+ files = append(files, fileDir{base: file, dir: dirIdx})
+
+ // We can't use something that may be dead-code
+ // eliminated from a binary here. proc.go contains
+ // main and the scheduler, so it's not going anywhere.
+ if i := strings.Index(name, "runtime/proc.go"); i >= 0 && unit.Lib.Pkg == "runtime" {
+ d.dwmu.Lock()
+ if gdbscript == "" {
+ k := strings.Index(name, "runtime/proc.go")
+ gdbscript = name[:k] + "runtime/runtime-gdb.py"
+ }
+ d.dwmu.Unlock()
+ }
+ }
+
+ // Emit directory section. This is a series of nul terminated
+ // strings, followed by a single zero byte.
+ lsDwsym := dwSym(lsu.Sym())
+ for k := 1; k < len(dirs); k++ {
+ d.AddString(lsDwsym, dirs[k])
+ }
+ lsu.AddUint8(0) // terminator
+
+ // Emit file section.
+ for k := 0; k < len(files); k++ {
+ d.AddString(lsDwsym, files[k].base)
+ dwarf.Uleb128put(d, lsDwsym, int64(files[k].dir))
+ lsu.AddUint8(0) // mtime
+ lsu.AddUint8(0) // length
+ }
+ lsu.AddUint8(0) // terminator
+}
+
+// writelines collects up and chains together the symbols needed to
+// form the DWARF line table for the specified compilation unit,
+// returning a list of symbols. The returned list will include an
+// initial symbol containing the line table header and prologue (with
+// file table), then a series of compiler-emitted line table symbols
+// (one per live function), and finally an epilog symbol containing an
+// end-of-sequence operator. The prologue and epilog symbols are passed
+// in (having been created earlier); here we add content to them.
+func (d *dwctxt) writelines(unit *sym.CompilationUnit, lineProlog loader.Sym) []loader.Sym {
+ is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles.
+
+ unitstart := int64(-1)
+ headerstart := int64(-1)
+ headerend := int64(-1)
+
+ syms := make([]loader.Sym, 0, len(unit.Textp)+2)
+ syms = append(syms, lineProlog)
+ lsu := d.ldr.MakeSymbolUpdater(lineProlog)
+ lsDwsym := dwSym(lineProlog)
+ newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, 0, lsDwsym)
+
+ // Write .debug_line Line Number Program Header (sec 6.2.4)
+ // Fields marked with (*) must be changed for 64-bit dwarf
+ unitLengthOffset := lsu.Size()
+ d.createUnitLength(lsu, 0) // unit_length (*), filled in at end
+ unitstart = lsu.Size()
+ lsu.AddUint16(d.arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05
+ headerLengthOffset := lsu.Size()
+ d.addDwarfAddrField(lsu, 0) // header_length (*), filled in at end
+ headerstart = lsu.Size()
+
+ // cpos == unitstart + 4 + 2 + 4
+ lsu.AddUint8(1) // minimum_instruction_length
+ lsu.AddUint8(is_stmt) // default_is_stmt
+ lsu.AddUint8(LINE_BASE & 0xFF) // line_base
+ lsu.AddUint8(LINE_RANGE) // line_range
+ lsu.AddUint8(OPCODE_BASE) // opcode_base
+ lsu.AddUint8(0) // standard_opcode_lengths[1]
+ lsu.AddUint8(1) // standard_opcode_lengths[2]
+ lsu.AddUint8(1) // standard_opcode_lengths[3]
+ lsu.AddUint8(1) // standard_opcode_lengths[4]
+ lsu.AddUint8(1) // standard_opcode_lengths[5]
+ lsu.AddUint8(0) // standard_opcode_lengths[6]
+ lsu.AddUint8(0) // standard_opcode_lengths[7]
+ lsu.AddUint8(0) // standard_opcode_lengths[8]
+ lsu.AddUint8(1) // standard_opcode_lengths[9]
+ lsu.AddUint8(0) // standard_opcode_lengths[10]
+
+ // Call helper to emit dir and file sections.
+ d.writeDirFileTables(unit, lsu)
+
+ // capture length at end of file names.
+ headerend = lsu.Size()
+ unitlen := lsu.Size() - unitstart
+
+ // Output the state machine for each function remaining.
+ for _, s := range unit.Textp {
+ fnSym := loader.Sym(s)
+ _, _, _, lines := d.ldr.GetFuncDwarfAuxSyms(fnSym)
+
+ // Chain the line symbol onto the list.
+ if lines != 0 {
+ syms = append(syms, lines)
+ unitlen += int64(len(d.ldr.Data(lines)))
+ }
+ }
+
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(unitlen))
+ }
+
+ if isDwarf64(d.linkctxt) {
+ lsu.SetUint(d.arch, unitLengthOffset+4, uint64(unitlen)) // +4 because of 0xFFFFFFFF
+ lsu.SetUint(d.arch, headerLengthOffset, uint64(headerend-headerstart))
+ } else {
+ lsu.SetUint32(d.arch, unitLengthOffset, uint32(unitlen))
+ lsu.SetUint32(d.arch, headerLengthOffset, uint32(headerend-headerstart))
+ }
+
+ return syms
+}
+
+// writepcranges generates the DW_AT_ranges table for compilation unit
+// "unit", and returns a collection of ranges symbols (one for the
+// compilation unit DIE itself and the remainder from functions in the unit).
+func (d *dwctxt) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, rangeProlog loader.Sym) []loader.Sym {
+
+ syms := make([]loader.Sym, 0, len(unit.RangeSyms)+1)
+ syms = append(syms, rangeProlog)
+ rsu := d.ldr.MakeSymbolUpdater(rangeProlog)
+ rDwSym := dwSym(rangeProlog)
+
+ // Create PC ranges for the compilation unit DIE.
+ newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, rsu.Size(), rDwSym)
+ newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, 0, dwSym(base))
+ dwarf.PutBasedRanges(d, rDwSym, pcs)
+
+ // Collect up the ranges for functions in the unit.
+ rsize := uint64(rsu.Size())
+ for _, ls := range unit.RangeSyms {
+ s := loader.Sym(ls)
+ syms = append(syms, s)
+ rsize += uint64(d.ldr.SymSize(s))
+ }
+
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, rsize)
+ }
+
+ return syms
+}
+
+/*
+ * Emit .debug_frame
+ */
+const (
+ dataAlignmentFactor = -4
+)
+
+// appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice.
+func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte {
+ b = append(b, dwarf.DW_CFA_def_cfa_offset_sf)
+ b = dwarf.AppendSleb128(b, cfa/dataAlignmentFactor)
+
+ switch {
+ case deltapc < 0x40:
+ b = append(b, uint8(dwarf.DW_CFA_advance_loc+deltapc))
+ case deltapc < 0x100:
+ b = append(b, dwarf.DW_CFA_advance_loc1)
+ b = append(b, uint8(deltapc))
+ case deltapc < 0x10000:
+ b = append(b, dwarf.DW_CFA_advance_loc2, 0, 0)
+ arch.ByteOrder.PutUint16(b[len(b)-2:], uint16(deltapc))
+ default:
+ b = append(b, dwarf.DW_CFA_advance_loc4, 0, 0, 0, 0)
+ arch.ByteOrder.PutUint32(b[len(b)-4:], uint32(deltapc))
+ }
+ return b
+}
+
+func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo {
+ fsd := dwSym(fs)
+ fsu := d.ldr.MakeSymbolUpdater(fs)
+ fsu.SetType(sym.SDWARFSECT)
+ isdw64 := isDwarf64(d.linkctxt)
+ haslr := d.linkctxt.Arch.HasLR
+
+ // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64
+ lengthFieldSize := int64(4)
+ if isdw64 {
+ lengthFieldSize += 8
+ }
+
+ // Emit the CIE, Section 6.4.1
+ cieReserve := uint32(16)
+ if haslr {
+ cieReserve = 32
+ }
+ if isdw64 {
+ cieReserve += 4 // 4 bytes added for cid
+ }
+ d.createUnitLength(fsu, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize
+ d.addDwarfAddrField(fsu, ^uint64(0)) // cid
+ fsu.AddUint8(3) // dwarf version (appendix F)
+ fsu.AddUint8(0) // augmentation ""
+ dwarf.Uleb128put(d, fsd, 1) // code_alignment_factor
+ dwarf.Sleb128put(d, fsd, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // return_address_register
+
+ fsu.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address..
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)...
+ if haslr {
+ dwarf.Uleb128put(d, fsd, int64(0)) // ...plus a 0 offset.
+
+ fsu.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue.
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr))
+
+ fsu.AddUint8(dwarf.DW_CFA_val_offset) // The previous value...
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...of the platform's SP register...
+ dwarf.Uleb128put(d, fsd, int64(0)) // ...is CFA+0.
+ } else {
+ dwarf.Uleb128put(d, fsd, int64(d.arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
+
+ fsu.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value...
+ dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // ...of the return address...
+ dwarf.Uleb128put(d, fsd, int64(-d.arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)].
+ }
+
+ pad := int64(cieReserve) + lengthFieldSize - int64(len(d.ldr.Data(fs)))
+
+ if pad < 0 {
+ Exitf("dwarf: cieReserve too small by %d bytes.", -pad)
+ }
+
+ internalExec := d.linkctxt.BuildMode == BuildModeExe && d.linkctxt.IsInternal()
+ addAddrPlus := loader.GenAddAddrPlusFunc(internalExec)
+
+ fsu.AddBytes(zeros[:pad])
+
+ var deltaBuf []byte
+ pcsp := obj.NewPCIter(uint32(d.arch.MinLC))
+ for _, s := range d.linkctxt.Textp {
+ fn := loader.Sym(s)
+ fi := d.ldr.FuncInfo(fn)
+ if !fi.Valid() {
+ continue
+ }
+ fpcsp := d.ldr.Pcsp(s)
+
+ // Emit a FDE, Section 6.4.1.
+ // First build the section contents into a byte buffer.
+ deltaBuf = deltaBuf[:0]
+ if haslr && fi.TopFrame() {
+ // Mark the link register as having an undefined value.
+ // This stops call stack unwinders progressing any further.
+ // TODO: similar mark on non-LR architectures.
+ deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined)
+ deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
+ }
+
+ for pcsp.Init(d.linkctxt.loader.Data(fpcsp)); !pcsp.Done; pcsp.Next() {
+ nextpc := pcsp.NextPC
+
+ // pciterinit goes up to the end of the function,
+ // but DWARF expects us to stop just before the end.
+ if int64(nextpc) == int64(len(d.ldr.Data(fn))) {
+ nextpc--
+ if nextpc < pcsp.PC {
+ continue
+ }
+ }
+
+ spdelta := int64(pcsp.Value)
+ if !haslr {
+ // Return address has been pushed onto stack.
+ spdelta += int64(d.arch.PtrSize)
+ }
+
+ if haslr && !fi.TopFrame() {
+ // TODO(bryanpkc): This is imprecise. In general, the instruction
+ // that stores the return address to the stack frame is not the
+ // same one that allocates the frame.
+ if pcsp.Value > 0 {
+ // The return address is preserved at (CFA-frame_size)
+ // after a stack frame has been allocated.
+ deltaBuf = append(deltaBuf, dwarf.DW_CFA_offset_extended_sf)
+ deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
+ deltaBuf = dwarf.AppendSleb128(deltaBuf, -spdelta/dataAlignmentFactor)
+ } else {
+ // The return address is restored into the link register
+ // when a stack frame has been de-allocated.
+ deltaBuf = append(deltaBuf, dwarf.DW_CFA_same_value)
+ deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
+ }
+ }
+
+ deltaBuf = appendPCDeltaCFA(d.arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta)
+ }
+ pad := int(Rnd(int64(len(deltaBuf)), int64(d.arch.PtrSize))) - len(deltaBuf)
+ deltaBuf = append(deltaBuf, zeros[:pad]...)
+
+ // Emit the FDE header, Section 6.4.1.
+ // 4 bytes: length, must be multiple of thearch.ptrsize
+ // 4/8 bytes: Pointer to the CIE above, at offset 0
+ // ptrsize: initial location
+ // ptrsize: address range
+
+ fdeLength := uint64(4 + 2*d.arch.PtrSize + len(deltaBuf))
+ if isdw64 {
+ fdeLength += 4 // 4 bytes added for CIE pointer
+ }
+ d.createUnitLength(fsu, fdeLength)
+
+ if d.linkctxt.LinkMode == LinkExternal {
+ d.addDwarfAddrRef(fsu, fs)
+ } else {
+ d.addDwarfAddrField(fsu, 0) // CIE offset
+ }
+ addAddrPlus(fsu, d.arch, s, 0)
+ fsu.AddUintXX(d.arch, uint64(len(d.ldr.Data(fn))), d.arch.PtrSize) // address range
+ fsu.AddBytes(deltaBuf)
+
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_frame", d.ldr.SymPkg(fn), fdeLength+uint64(lengthFieldSize))
+ }
+ }
+
+ return dwarfSecInfo{syms: []loader.Sym{fs}}
+}
+
+/*
+ * Walk DWarfDebugInfoEntries, and emit .debug_info
+ */
+
+const (
+ COMPUNITHEADERSIZE = 4 + 2 + 4 + 1
+)
+
+// appendSyms appends the syms from 'src' into 'syms' and returns the
+// result. This can go away once we do away with sym.LoaderSym
+// entirely.
+func appendSyms(syms []loader.Sym, src []sym.LoaderSym) []loader.Sym {
+ for _, s := range src {
+ syms = append(syms, loader.Sym(s))
+ }
+ return syms
+}
+
+func (d *dwctxt) writeUnitInfo(u *sym.CompilationUnit, abbrevsym loader.Sym, infoEpilog loader.Sym) []loader.Sym {
+ syms := []loader.Sym{}
+ if len(u.Textp) == 0 && u.DWInfo.Child == nil && len(u.VarDIEs) == 0 {
+ return syms
+ }
+
+ compunit := u.DWInfo
+ s := d.dtolsym(compunit.Sym)
+ su := d.ldr.MakeSymbolUpdater(s)
+
+ // Write .debug_info Compilation Unit Header (sec 7.5.1)
+ // Fields marked with (*) must be changed for 64-bit dwarf
+ // This must match COMPUNITHEADERSIZE above.
+ d.createUnitLength(su, 0) // unit_length (*), will be filled in later.
+ su.AddUint16(d.arch, 4) // dwarf version (appendix F)
+
+ // debug_abbrev_offset (*)
+ d.addDwarfAddrRef(su, abbrevsym)
+
+ su.AddUint8(uint8(d.arch.PtrSize)) // address_size
+
+ ds := dwSym(s)
+ dwarf.Uleb128put(d, ds, int64(compunit.Abbrev))
+ dwarf.PutAttrs(d, ds, compunit.Abbrev, compunit.Attr)
+
+ // This is an under-estimate; more will be needed for type DIEs.
+ cu := make([]loader.Sym, 0, len(u.AbsFnDIEs)+len(u.FuncDIEs))
+ cu = append(cu, s)
+ cu = appendSyms(cu, u.AbsFnDIEs)
+ cu = appendSyms(cu, u.FuncDIEs)
+ if u.Consts != 0 {
+ cu = append(cu, loader.Sym(u.Consts))
+ }
+ cu = appendSyms(cu, u.VarDIEs)
+ var cusize int64
+ for _, child := range cu {
+ cusize += int64(len(d.ldr.Data(child)))
+ }
+
+ for die := compunit.Child; die != nil; die = die.Link {
+ l := len(cu)
+ lastSymSz := int64(len(d.ldr.Data(cu[l-1])))
+ cu = d.putdie(cu, die)
+ if lastSymSz != int64(len(d.ldr.Data(cu[l-1]))) {
+ // putdie will sometimes append directly to the last symbol of the list
+ cusize = cusize - lastSymSz + int64(len(d.ldr.Data(cu[l-1])))
+ }
+ for _, child := range cu[l:] {
+ cusize += int64(len(d.ldr.Data(child)))
+ }
+ }
+
+ culu := d.ldr.MakeSymbolUpdater(infoEpilog)
+ culu.AddUint8(0) // closes compilation unit DIE
+ cu = append(cu, infoEpilog)
+ cusize++
+
+ // Save size for AIX symbol table.
+ if d.linkctxt.HeadType == objabi.Haix {
+ addDwsectCUSize(".debug_info", d.getPkgFromCUSym(s), uint64(cusize))
+ }
+ if isDwarf64(d.linkctxt) {
+ cusize -= 12 // exclude the length field.
+ su.SetUint(d.arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF
+ } else {
+ cusize -= 4 // exclude the length field.
+ su.SetUint32(d.arch, 0, uint32(cusize))
+ }
+ return append(syms, cu...)
+}
+
+func (d *dwctxt) writegdbscript() dwarfSecInfo {
+ // TODO (aix): make it available
+ if d.linkctxt.HeadType == objabi.Haix {
+ return dwarfSecInfo{}
+ }
+ if d.linkctxt.LinkMode == LinkExternal && d.linkctxt.HeadType == objabi.Hwindows && d.linkctxt.BuildMode == BuildModeCArchive {
+ // gcc on Windows places .debug_gdb_scripts in the wrong location, which
+ // causes the program not to run. See https://golang.org/issue/20183
+ // Non c-archives can avoid this issue via a linker script
+ // (see fix near writeGDBLinkerScript).
+ // c-archive users would need to specify the linker script manually.
+ // For UX it's better not to deal with this.
+ return dwarfSecInfo{}
+ }
+ if gdbscript == "" {
+ return dwarfSecInfo{}
+ }
+
+ gs := d.ldr.CreateSymForUpdate(".debug_gdb_scripts", 0)
+ gs.SetType(sym.SDWARFSECT)
+
+ gs.AddUint8(GdbScriptPythonFileId)
+ gs.Addstring(gdbscript)
+ return dwarfSecInfo{syms: []loader.Sym{gs.Sym()}}
+}
+
+// FIXME: might be worth looking replacing this map with a function
+// that switches based on symbol instead.
+
+var prototypedies map[string]*dwarf.DWDie
+
+func dwarfEnabled(ctxt *Link) bool {
+ if *FlagW { // disable dwarf
+ return false
+ }
+ if *FlagS && ctxt.HeadType != objabi.Hdarwin {
+ return false
+ }
+ if ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hjs {
+ return false
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ switch {
+ case ctxt.IsELF:
+ case ctxt.HeadType == objabi.Hdarwin:
+ case ctxt.HeadType == objabi.Hwindows:
+ case ctxt.HeadType == objabi.Haix:
+ res, err := dwarf.IsDWARFEnabledOnAIXLd(ctxt.extld())
+ if err != nil {
+ Exitf("%v", err)
+ }
+ return res
+ default:
+ return false
+ }
+ }
+
+ return true
+}
+
+// mkBuiltinType populates the dwctxt2 sym lookup maps for the
+// newly created builtin type DIE 'typeDie'.
+func (d *dwctxt) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie {
+ // create type DIE
+ die := d.newdie(&dwtypes, abrv, tname)
+
+ // Look up type symbol.
+ gotype := d.lookupOrDiag("type:" + tname)
+
+ // Map from die sym to type sym
+ ds := loader.Sym(die.Sym.(dwSym))
+ d.rtmap[ds] = gotype
+
+ // Map from type to def sym
+ d.tdmap[gotype] = ds
+
+ return die
+}
+
+// dwarfVisitFunction takes a function (text) symbol and processes the
+// subprogram DIE for the function and picks up any other DIEs
+// (absfns, types) that it references.
+func (d *dwctxt) dwarfVisitFunction(fnSym loader.Sym, unit *sym.CompilationUnit) {
+ // The DWARF subprogram DIE symbol is listed as an aux sym
+ // of the text (fcn) symbol, so ask the loader to retrieve it,
+ // as well as the associated range symbol.
+ infosym, _, rangesym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym)
+ if infosym == 0 {
+ return
+ }
+ d.ldr.SetAttrNotInSymbolTable(infosym, true)
+ d.ldr.SetAttrReachable(infosym, true)
+ unit.FuncDIEs = append(unit.FuncDIEs, sym.LoaderSym(infosym))
+ if rangesym != 0 {
+ d.ldr.SetAttrNotInSymbolTable(rangesym, true)
+ d.ldr.SetAttrReachable(rangesym, true)
+ unit.RangeSyms = append(unit.RangeSyms, sym.LoaderSym(rangesym))
+ }
+
+ // Walk the relocations of the subprogram DIE symbol to discover
+ // references to abstract function DIEs, Go type DIES, and
+ // (via R_USETYPE relocs) types that were originally assigned to
+ // locals/params but were optimized away.
+ drelocs := d.ldr.Relocs(infosym)
+ for ri := 0; ri < drelocs.Count(); ri++ {
+ r := drelocs.At(ri)
+ // Look for "use type" relocs.
+ if r.Type() == objabi.R_USETYPE {
+ d.defgotype(r.Sym())
+ continue
+ }
+ if r.Type() != objabi.R_DWARFSECREF {
+ continue
+ }
+
+ rsym := r.Sym()
+ rst := d.ldr.SymType(rsym)
+
+ // Look for abstract function references.
+ if rst == sym.SDWARFABSFCN {
+ if !d.ldr.AttrOnList(rsym) {
+ // abstract function
+ d.ldr.SetAttrOnList(rsym, true)
+ unit.AbsFnDIEs = append(unit.AbsFnDIEs, sym.LoaderSym(rsym))
+ d.importInfoSymbol(rsym)
+ }
+ continue
+ }
+
+ // Look for type references.
+ if rst != sym.SDWARFTYPE && rst != sym.Sxxx {
+ continue
+ }
+ if _, ok := d.rtmap[rsym]; ok {
+ // type already generated
+ continue
+ }
+
+ rsn := d.ldr.SymName(rsym)
+ tn := rsn[len(dwarf.InfoPrefix):]
+ ts := d.ldr.Lookup("type:"+tn, 0)
+ d.defgotype(ts)
+ }
+}
+
+// dwarfGenerateDebugInfo generated debug info entries for all types,
+// variables and functions in the program.
+// Along with dwarfGenerateDebugSyms they are the two main entry points into
+// dwarf generation: dwarfGenerateDebugInfo does all the work that should be
+// done before symbol names are mangled while dwarfGenerateDebugSyms does
+// all the work that can only be done after addresses have been assigned to
+// text symbols.
+func dwarfGenerateDebugInfo(ctxt *Link) {
+ if !dwarfEnabled(ctxt) {
+ return
+ }
+
+ d := &dwctxt{
+ linkctxt: ctxt,
+ ldr: ctxt.loader,
+ arch: ctxt.Arch,
+ tmap: make(map[string]loader.Sym),
+ tdmap: make(map[loader.Sym]loader.Sym),
+ rtmap: make(map[loader.Sym]loader.Sym),
+ }
+ d.typeRuntimeEface = d.lookupOrDiag("type:runtime.eface")
+ d.typeRuntimeIface = d.lookupOrDiag("type:runtime.iface")
+
+ if ctxt.HeadType == objabi.Haix {
+ // Initial map used to store package size for each DWARF section.
+ dwsectCUSize = make(map[string]uint64)
+ }
+
+ // For ctxt.Diagnostic messages.
+ newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes")
+
+ // Unspecified type. There are no references to this in the symbol table.
+ d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>")
+
+ // Some types that must exist to define other ones (uintptr in particular
+ // is needed for array size)
+ d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer")
+ die := d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BASETYPE, "uintptr")
+ newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
+ newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(d.arch.PtrSize), 0)
+ newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0)
+ newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, dwSym(d.lookupOrDiag("type:uintptr")))
+
+ d.uintptrInfoSym = d.mustFind("uintptr")
+
+ // Prototypes needed for type synthesis.
+ prototypedies = map[string]*dwarf.DWDie{
+ "type:runtime.stringStructDWARF": nil,
+ "type:runtime.slice": nil,
+ "type:runtime.hmap": nil,
+ "type:runtime.bmap": nil,
+ "type:runtime.sudog": nil,
+ "type:runtime.waitq": nil,
+ "type:runtime.hchan": nil,
+ }
+
+ // Needed by the prettyprinter code for interface inspection.
+ for _, typ := range []string{
+ "type:runtime._type",
+ "type:runtime.arraytype",
+ "type:runtime.chantype",
+ "type:runtime.functype",
+ "type:runtime.maptype",
+ "type:runtime.ptrtype",
+ "type:runtime.slicetype",
+ "type:runtime.structtype",
+ "type:runtime.interfacetype",
+ "type:runtime.itab",
+ "type:runtime.imethod"} {
+ d.defgotype(d.lookupOrDiag(typ))
+ }
+
+ // fake root DIE for compile unit DIEs
+ var dwroot dwarf.DWDie
+ flagVariants := make(map[string]bool)
+
+ for _, lib := range ctxt.Library {
+
+ consts := d.ldr.Lookup(dwarf.ConstInfoPrefix+lib.Pkg, 0)
+ for _, unit := range lib.Units {
+ // We drop the constants into the first CU.
+ if consts != 0 {
+ unit.Consts = sym.LoaderSym(consts)
+ d.importInfoSymbol(consts)
+ consts = 0
+ }
+ ctxt.compUnits = append(ctxt.compUnits, unit)
+
+ // We need at least one runtime unit.
+ if unit.Lib.Pkg == "runtime" {
+ ctxt.runtimeCU = unit
+ }
+
+ cuabrv := dwarf.DW_ABRV_COMPUNIT
+ if len(unit.Textp) == 0 {
+ cuabrv = dwarf.DW_ABRV_COMPUNIT_TEXTLESS
+ }
+ unit.DWInfo = d.newdie(&dwroot, cuabrv, unit.Lib.Pkg)
+ newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0)
+ // OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
+ compDir := getCompilationDir()
+ // TODO: Make this be the actual compilation directory, not
+ // the linker directory. If we move CU construction into the
+ // compiler, this should happen naturally.
+ newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir)
+
+ var peData []byte
+ if producerExtra := d.ldr.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0); producerExtra != 0 {
+ peData = d.ldr.Data(producerExtra)
+ }
+ producer := "Go cmd/compile " + buildcfg.Version
+ if len(peData) > 0 {
+ // We put a semicolon before the flags to clearly
+ // separate them from the version, which can be long
+ // and have lots of weird things in it in development
+ // versions. We promise not to put a semicolon in the
+ // version, so it should be safe for readers to scan
+ // forward to the semicolon.
+ producer += "; " + string(peData)
+ flagVariants[string(peData)] = true
+ } else {
+ flagVariants[""] = true
+ }
+
+ newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer)
+
+ var pkgname string
+ if pnSymIdx := d.ldr.Lookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); pnSymIdx != 0 {
+ pnsData := d.ldr.Data(pnSymIdx)
+ pkgname = string(pnsData)
+ }
+ newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname)
+
+ // Scan all functions in this compilation unit, create
+ // DIEs for all referenced types, find all referenced
+ // abstract functions, visit range symbols. Note that
+ // Textp has been dead-code-eliminated already.
+ for _, s := range unit.Textp {
+ d.dwarfVisitFunction(loader.Sym(s), unit)
+ }
+ }
+ }
+
+ // Fix for 31034: if the objects feeding into this link were compiled
+ // with different sets of flags, then don't issue an error if
+ // the -strictdups checks fail.
+ if checkStrictDups > 1 && len(flagVariants) > 1 {
+ checkStrictDups = 1
+ }
+
+ // Make a pass through all data symbols, looking for those
+ // corresponding to reachable, Go-generated, user-visible
+ // global variables. For each global of this sort, locate
+ // the corresponding compiler-generated DIE symbol and tack
+ // it onto the list associated with the unit.
+ // Also looks for dictionary symbols and generates DIE symbols for each
+ // type they reference.
+ for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ {
+ if !d.ldr.AttrReachable(idx) ||
+ d.ldr.AttrNotInSymbolTable(idx) ||
+ d.ldr.SymVersion(idx) >= sym.SymVerStatic {
+ continue
+ }
+ t := d.ldr.SymType(idx)
+ switch t {
+ case sym.SRODATA, sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS:
+ // ok
+ default:
+ continue
+ }
+ // Skip things with no type, unless it's a dictionary
+ gt := d.ldr.SymGoType(idx)
+ if gt == 0 {
+ if t == sym.SRODATA {
+ if d.ldr.IsDict(idx) {
+ // This is a dictionary, make sure that all types referenced by this dictionary are reachable
+ relocs := d.ldr.Relocs(idx)
+ for i := 0; i < relocs.Count(); i++ {
+ reloc := relocs.At(i)
+ if reloc.Type() == objabi.R_USEIFACE {
+ d.defgotype(reloc.Sym())
+ }
+ }
+ }
+ }
+ continue
+ }
+ // Skip file local symbols (this includes static tmps, stack
+ // object symbols, and local symbols in assembler src files).
+ if d.ldr.IsFileLocal(idx) {
+ continue
+ }
+ sn := d.ldr.SymName(idx)
+ if sn == "" {
+ // skip aux symbols
+ continue
+ }
+
+ // Find compiler-generated DWARF info sym for global in question,
+ // and tack it onto the appropriate unit. Note that there are
+ // circumstances under which we can't find the compiler-generated
+ // symbol-- this typically happens as a result of compiler options
+ // (e.g. compile package X with "-dwarf=0").
+
+ // FIXME: use an aux sym or a relocation here instead of a
+ // name lookup.
+ varDIE := d.ldr.Lookup(dwarf.InfoPrefix+sn, 0)
+ if varDIE != 0 {
+ unit := d.ldr.SymUnit(idx)
+ d.defgotype(gt)
+ unit.VarDIEs = append(unit.VarDIEs, sym.LoaderSym(varDIE))
+ }
+ }
+
+ d.synthesizestringtypes(ctxt, dwtypes.Child)
+ d.synthesizeslicetypes(ctxt, dwtypes.Child)
+ d.synthesizemaptypes(ctxt, dwtypes.Child)
+ d.synthesizechantypes(ctxt, dwtypes.Child)
+}
+
+// dwarfGenerateDebugSyms constructs debug_line, debug_frame, and
+// debug_loc. It also writes out the debug_info section using symbols
+// generated in dwarfGenerateDebugInfo2.
+func dwarfGenerateDebugSyms(ctxt *Link) {
+ if !dwarfEnabled(ctxt) {
+ return
+ }
+ d := &dwctxt{
+ linkctxt: ctxt,
+ ldr: ctxt.loader,
+ arch: ctxt.Arch,
+ dwmu: new(sync.Mutex),
+ }
+ d.dwarfGenerateDebugSyms()
+}
+
+// dwUnitSyms stores input and output symbols for DWARF generation
+// for a given compilation unit.
+type dwUnitSyms struct {
+ // Inputs for a given unit.
+ lineProlog loader.Sym
+ rangeProlog loader.Sym
+ infoEpilog loader.Sym
+
+ // Outputs for a given unit.
+ linesyms []loader.Sym
+ infosyms []loader.Sym
+ locsyms []loader.Sym
+ rangessyms []loader.Sym
+}
+
+// dwUnitPortion assembles the DWARF content for a given compilation
+// unit: debug_info, debug_lines, debug_ranges, debug_loc (debug_frame
+// is handled elsewere). Order is important; the calls to writelines
+// and writepcranges below make updates to the compilation unit DIE,
+// hence they have to happen before the call to writeUnitInfo.
+func (d *dwctxt) dwUnitPortion(u *sym.CompilationUnit, abbrevsym loader.Sym, us *dwUnitSyms) {
+ if u.DWInfo.Abbrev != dwarf.DW_ABRV_COMPUNIT_TEXTLESS {
+ us.linesyms = d.writelines(u, us.lineProlog)
+ base := loader.Sym(u.Textp[0])
+ us.rangessyms = d.writepcranges(u, base, u.PCs, us.rangeProlog)
+ us.locsyms = d.collectUnitLocs(u)
+ }
+ us.infosyms = d.writeUnitInfo(u, abbrevsym, us.infoEpilog)
+}
+
+func (d *dwctxt) dwarfGenerateDebugSyms() {
+ abbrevSec := d.writeabbrev()
+ dwarfp = append(dwarfp, abbrevSec)
+ d.calcCompUnitRanges()
+ sort.Sort(compilationUnitByStartPC(d.linkctxt.compUnits))
+
+ // newdie adds DIEs to the *beginning* of the parent's DIE list.
+ // Now that we're done creating DIEs, reverse the trees so DIEs
+ // appear in the order they were created.
+ for _, u := range d.linkctxt.compUnits {
+ reversetree(&u.DWInfo.Child)
+ }
+ reversetree(&dwtypes.Child)
+ movetomodule(d.linkctxt, &dwtypes)
+
+ mkSecSym := func(name string) loader.Sym {
+ s := d.ldr.CreateSymForUpdate(name, 0)
+ s.SetType(sym.SDWARFSECT)
+ s.SetReachable(true)
+ return s.Sym()
+ }
+ mkAnonSym := func(kind sym.SymKind) loader.Sym {
+ s := d.ldr.MakeSymbolUpdater(d.ldr.CreateExtSym("", 0))
+ s.SetType(kind)
+ s.SetReachable(true)
+ return s.Sym()
+ }
+
+ // Create the section symbols.
+ frameSym := mkSecSym(".debug_frame")
+ locSym := mkSecSym(".debug_loc")
+ lineSym := mkSecSym(".debug_line")
+ rangesSym := mkSecSym(".debug_ranges")
+ infoSym := mkSecSym(".debug_info")
+
+ // Create the section objects
+ lineSec := dwarfSecInfo{syms: []loader.Sym{lineSym}}
+ locSec := dwarfSecInfo{syms: []loader.Sym{locSym}}
+ rangesSec := dwarfSecInfo{syms: []loader.Sym{rangesSym}}
+ frameSec := dwarfSecInfo{syms: []loader.Sym{frameSym}}
+ infoSec := dwarfSecInfo{syms: []loader.Sym{infoSym}}
+
+ // Create any new symbols that will be needed during the
+ // parallel portion below.
+ ncu := len(d.linkctxt.compUnits)
+ unitSyms := make([]dwUnitSyms, ncu)
+ for i := 0; i < ncu; i++ {
+ us := &unitSyms[i]
+ us.lineProlog = mkAnonSym(sym.SDWARFLINES)
+ us.rangeProlog = mkAnonSym(sym.SDWARFRANGE)
+ us.infoEpilog = mkAnonSym(sym.SDWARFFCN)
+ }
+
+ var wg sync.WaitGroup
+ sema := make(chan struct{}, runtime.GOMAXPROCS(0))
+
+ // Kick off generation of .debug_frame, since it doesn't have
+ // any entanglements and can be started right away.
+ wg.Add(1)
+ go func() {
+ sema <- struct{}{}
+ defer func() {
+ <-sema
+ wg.Done()
+ }()
+ frameSec = d.writeframes(frameSym)
+ }()
+
+ // Create a goroutine per comp unit to handle the generation that
+ // unit's portion of .debug_line, .debug_loc, .debug_ranges, and
+ // .debug_info.
+ wg.Add(len(d.linkctxt.compUnits))
+ for i := 0; i < ncu; i++ {
+ go func(u *sym.CompilationUnit, us *dwUnitSyms) {
+ sema <- struct{}{}
+ defer func() {
+ <-sema
+ wg.Done()
+ }()
+ d.dwUnitPortion(u, abbrevSec.secSym(), us)
+ }(d.linkctxt.compUnits[i], &unitSyms[i])
+ }
+ wg.Wait()
+
+ markReachable := func(syms []loader.Sym) []loader.Sym {
+ for _, s := range syms {
+ d.ldr.SetAttrNotInSymbolTable(s, true)
+ d.ldr.SetAttrReachable(s, true)
+ }
+ return syms
+ }
+
+ // Stitch together the results.
+ for i := 0; i < ncu; i++ {
+ r := &unitSyms[i]
+ lineSec.syms = append(lineSec.syms, markReachable(r.linesyms)...)
+ infoSec.syms = append(infoSec.syms, markReachable(r.infosyms)...)
+ locSec.syms = append(locSec.syms, markReachable(r.locsyms)...)
+ rangesSec.syms = append(rangesSec.syms, markReachable(r.rangessyms)...)
+ }
+ dwarfp = append(dwarfp, lineSec)
+ dwarfp = append(dwarfp, frameSec)
+ gdbScriptSec := d.writegdbscript()
+ if gdbScriptSec.secSym() != 0 {
+ dwarfp = append(dwarfp, gdbScriptSec)
+ }
+ dwarfp = append(dwarfp, infoSec)
+ if len(locSec.syms) > 1 {
+ dwarfp = append(dwarfp, locSec)
+ }
+ dwarfp = append(dwarfp, rangesSec)
+
+ // Check to make sure we haven't listed any symbols more than once
+ // in the info section. This used to be done by setting and
+ // checking the OnList attribute in "putdie", but that strategy
+ // was not friendly for concurrency.
+ seen := loader.MakeBitmap(d.ldr.NSym())
+ for _, s := range infoSec.syms {
+ if seen.Has(s) {
+ log.Fatalf("symbol %s listed multiple times", d.ldr.SymName(s))
+ }
+ seen.Set(s)
+ }
+}
+
+func (d *dwctxt) collectUnitLocs(u *sym.CompilationUnit) []loader.Sym {
+ syms := []loader.Sym{}
+ for _, fn := range u.FuncDIEs {
+ relocs := d.ldr.Relocs(loader.Sym(fn))
+ for i := 0; i < relocs.Count(); i++ {
+ reloc := relocs.At(i)
+ if reloc.Type() != objabi.R_DWARFSECREF {
+ continue
+ }
+ rsym := reloc.Sym()
+ if d.ldr.SymType(rsym) == sym.SDWARFLOC {
+ syms = append(syms, rsym)
+ // One location list entry per function, but many relocations to it. Don't duplicate.
+ break
+ }
+ }
+ }
+ return syms
+}
+
+/*
+ * Elf.
+ */
+func dwarfaddshstrings(ctxt *Link, shstrtab *loader.SymbolBuilder) {
+ if *FlagW { // disable dwarf
+ return
+ }
+
+ secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts", "ranges"}
+ for _, sec := range secs {
+ shstrtab.Addstring(".debug_" + sec)
+ if ctxt.IsExternal() {
+ shstrtab.Addstring(elfRelType + ".debug_" + sec)
+ } else {
+ shstrtab.Addstring(".zdebug_" + sec)
+ }
+ }
+}
+
+func dwarfaddelfsectionsyms(ctxt *Link) {
+ if *FlagW { // disable dwarf
+ return
+ }
+ if ctxt.LinkMode != LinkExternal {
+ return
+ }
+
+ ldr := ctxt.loader
+ for _, si := range dwarfp {
+ s := si.secSym()
+ sect := ldr.SymSect(si.secSym())
+ putelfsectionsym(ctxt, ctxt.Out, s, sect.Elfsect.(*ElfShdr).shnum)
+ }
+}
+
+// dwarfcompress compresses the DWARF sections. Relocations are applied
+// on the fly. After this, dwarfp will contain a different (new) set of
+// symbols, and sections may have been replaced.
+func dwarfcompress(ctxt *Link) {
+ // compressedSect is a helper type for parallelizing compression.
+ type compressedSect struct {
+ index int
+ compressed []byte
+ syms []loader.Sym
+ }
+
+ supported := ctxt.IsELF || ctxt.IsWindows() || ctxt.IsDarwin()
+ if !ctxt.compressDWARF || !supported || ctxt.IsExternal() {
+ return
+ }
+
+ var compressedCount int
+ resChannel := make(chan compressedSect)
+ for i := range dwarfp {
+ go func(resIndex int, syms []loader.Sym) {
+ resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms}
+ }(compressedCount, dwarfp[i].syms)
+ compressedCount++
+ }
+ res := make([]compressedSect, compressedCount)
+ for ; compressedCount > 0; compressedCount-- {
+ r := <-resChannel
+ res[r.index] = r
+ }
+
+ ldr := ctxt.loader
+ var newDwarfp []dwarfSecInfo
+ Segdwarf.Sections = Segdwarf.Sections[:0]
+ for _, z := range res {
+ s := z.syms[0]
+ if z.compressed == nil {
+ // Compression didn't help.
+ ds := dwarfSecInfo{syms: z.syms}
+ newDwarfp = append(newDwarfp, ds)
+ Segdwarf.Sections = append(Segdwarf.Sections, ldr.SymSect(s))
+ } else {
+ var compressedSegName string
+ if ctxt.IsELF {
+ compressedSegName = ldr.SymSect(s).Name
+ } else {
+ compressedSegName = ".zdebug_" + ldr.SymSect(s).Name[len(".debug_"):]
+ }
+ sect := addsection(ctxt.loader, ctxt.Arch, &Segdwarf, compressedSegName, 04)
+ sect.Align = int32(ctxt.Arch.Alignment)
+ sect.Length = uint64(len(z.compressed))
+ sect.Compressed = true
+ newSym := ldr.MakeSymbolBuilder(compressedSegName)
+ ldr.SetAttrReachable(s, true)
+ newSym.SetData(z.compressed)
+ newSym.SetSize(int64(len(z.compressed)))
+ ldr.SetSymSect(newSym.Sym(), sect)
+ ds := dwarfSecInfo{syms: []loader.Sym{newSym.Sym()}}
+ newDwarfp = append(newDwarfp, ds)
+
+ // compressed symbols are no longer needed.
+ for _, s := range z.syms {
+ ldr.SetAttrReachable(s, false)
+ ldr.FreeSym(s)
+ }
+ }
+ }
+ dwarfp = newDwarfp
+
+ // Re-compute the locations of the compressed DWARF symbols
+ // and sections, since the layout of these within the file is
+ // based on Section.Vaddr and Symbol.Value.
+ pos := Segdwarf.Vaddr
+ var prevSect *sym.Section
+ for _, si := range dwarfp {
+ for _, s := range si.syms {
+ ldr.SetSymValue(s, int64(pos))
+ sect := ldr.SymSect(s)
+ if sect != prevSect {
+ sect.Vaddr = uint64(pos)
+ prevSect = sect
+ }
+ if ldr.SubSym(s) != 0 {
+ log.Fatalf("%s: unexpected sub-symbols", ldr.SymName(s))
+ }
+ pos += uint64(ldr.SymSize(s))
+ if ctxt.IsWindows() {
+ pos = uint64(Rnd(int64(pos), PEFILEALIGN))
+ }
+ }
+ }
+ Segdwarf.Length = pos - Segdwarf.Vaddr
+}
+
+type compilationUnitByStartPC []*sym.CompilationUnit
+
+func (v compilationUnitByStartPC) Len() int { return len(v) }
+func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
+
+func (v compilationUnitByStartPC) Less(i, j int) bool {
+ switch {
+ case len(v[i].Textp) == 0 && len(v[j].Textp) == 0:
+ return v[i].Lib.Pkg < v[j].Lib.Pkg
+ case len(v[i].Textp) != 0 && len(v[j].Textp) == 0:
+ return true
+ case len(v[i].Textp) == 0 && len(v[j].Textp) != 0:
+ return false
+ default:
+ return v[i].PCs[0].Start < v[j].PCs[0].Start
+ }
+}
+
+// getPkgFromCUSym returns the package name for the compilation unit
+// represented by s.
+// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get
+// the package name.
+func (d *dwctxt) getPkgFromCUSym(s loader.Sym) string {
+ return strings.TrimPrefix(d.ldr.SymName(s), dwarf.InfoPrefix+".pkg.")
+}
+
+// On AIX, the symbol table needs to know where are the compilation units parts
+// for a specific package in each .dw section.
+// dwsectCUSize map will save the size of a compilation unit for
+// the corresponding .dw section.
+// This size can later be retrieved with the index "sectionName.pkgName".
+var dwsectCUSizeMu sync.Mutex
+var dwsectCUSize map[string]uint64
+
+// getDwsectCUSize retrieves the corresponding package size inside the current section.
+func getDwsectCUSize(sname string, pkgname string) uint64 {
+ return dwsectCUSize[sname+"."+pkgname]
+}
+
+func addDwsectCUSize(sname string, pkgname string, size uint64) {
+ dwsectCUSizeMu.Lock()
+ defer dwsectCUSizeMu.Unlock()
+ dwsectCUSize[sname+"."+pkgname] += size
+}
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
new file mode 100644
index 0000000..a3db4a9
--- /dev/null
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -0,0 +1,1971 @@
+// Copyright 2017 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.
+
+package ld
+
+import (
+ "debug/dwarf"
+ "debug/pe"
+ "fmt"
+ "internal/testenv"
+ "io"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "testing"
+
+ intdwarf "cmd/internal/dwarf"
+ objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function
+ "cmd/link/internal/dwtest"
+)
+
+const (
+ DefaultOpt = "-gcflags="
+ NoOpt = "-gcflags=-l -N"
+ OptInl4 = "-gcflags=-l=4"
+ OptAllInl4 = "-gcflags=all=-l=4"
+)
+
+func TestRuntimeTypesPresent(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, `package main; func main() { }`, NoOpt)
+ defer f.Close()
+
+ dwarf, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ want := map[string]bool{
+ "runtime._type": true,
+ "runtime.arraytype": true,
+ "runtime.chantype": true,
+ "runtime.functype": true,
+ "runtime.maptype": true,
+ "runtime.ptrtype": true,
+ "runtime.slicetype": true,
+ "runtime.structtype": true,
+ "runtime.interfacetype": true,
+ "runtime.itab": true,
+ "runtime.imethod": true,
+ }
+
+ found := findTypes(t, dwarf, want)
+ if len(found) != len(want) {
+ t.Errorf("found %v, want %v", found, want)
+ }
+}
+
+func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) {
+ found = make(map[string]bool)
+ rdr := dw.Reader()
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ switch entry.Tag {
+ case dwarf.TagTypedef:
+ if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] {
+ found[name] = true
+ }
+ }
+ }
+ return
+}
+
+type builtFile struct {
+ *objfilepkg.File
+ path string
+}
+
+func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile {
+ src := filepath.Join(dir, "test.go")
+ dst := filepath.Join(dir, "out.exe")
+
+ if err := os.WriteFile(src, []byte(testfile), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", gcflags, "-o", dst, src)
+ b, err := cmd.CombinedOutput()
+ if len(b) != 0 {
+ t.Logf("## build output:\n%s", b)
+ }
+ if err != nil {
+ t.Fatalf("build error: %v", err)
+ }
+
+ f, err := objfilepkg.Open(dst)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return &builtFile{f, dst}
+}
+
+// Similar to gobuild() above, but uses a main package instead of a test.go file.
+
+func gobuildTestdata(t *testing.T, tdir string, pkgDir string, gcflags string) *builtFile {
+ dst := filepath.Join(tdir, "out.exe")
+
+ // Run a build with an updated GOPATH
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", gcflags, "-o", dst)
+ cmd.Dir = pkgDir
+ if b, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("build: %s\n", b)
+ t.Fatalf("build error: %v", err)
+ }
+
+ f, err := objfilepkg.Open(dst)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return &builtFile{f, dst}
+}
+
+func TestEmbeddedStructMarker(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ const prog = `
+package main
+
+import "fmt"
+
+type Foo struct { v int }
+type Bar struct {
+ Foo
+ name string
+}
+type Baz struct {
+ *Foo
+ name string
+}
+
+func main() {
+ bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"}
+ baz := Baz{ Foo: &bar.Foo, name: "123" }
+ fmt.Println(bar, baz)
+}`
+
+ want := map[string]map[string]bool{
+ "main.Foo": {"v": false},
+ "main.Bar": {"Foo": true, "name": false},
+ "main.Baz": {"Foo": true, "name": false},
+ }
+
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, NoOpt)
+
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ switch entry.Tag {
+ case dwarf.TagStructType:
+ name := entry.Val(dwarf.AttrName).(string)
+ wantMembers := want[name]
+ if wantMembers == nil {
+ continue
+ }
+ gotMembers, err := findMembers(rdr)
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ if !reflect.DeepEqual(gotMembers, wantMembers) {
+ t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers)
+ }
+ delete(want, name)
+ }
+ }
+ if len(want) != 0 {
+ t.Errorf("failed to check all expected types: missing types = %+v", want)
+ }
+}
+
+func findMembers(rdr *dwarf.Reader) (map[string]bool, error) {
+ memberEmbedded := map[string]bool{}
+ // TODO(hyangah): define in debug/dwarf package
+ const goEmbeddedStruct = dwarf.Attr(intdwarf.DW_AT_go_embedded_field)
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ return nil, err
+ }
+ switch entry.Tag {
+ case dwarf.TagMember:
+ name := entry.Val(dwarf.AttrName).(string)
+ embedded := entry.Val(goEmbeddedStruct).(bool)
+ memberEmbedded[name] = embedded
+ case 0:
+ return memberEmbedded, nil
+ }
+ }
+ return memberEmbedded, nil
+}
+
+func TestSizes(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ // External linking may bring in C symbols with unknown size. Skip.
+ testenv.MustInternalLink(t)
+
+ t.Parallel()
+
+ // DWARF sizes should never be -1.
+ // See issue #21097
+ const prog = `
+package main
+var x func()
+var y [4]func()
+func main() {
+ x = nil
+ y[0] = nil
+}
+`
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ rdr := d.Reader()
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ switch entry.Tag {
+ case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef:
+ default:
+ continue
+ }
+ typ, err := d.Type(entry.Offset)
+ if err != nil {
+ t.Fatalf("can't read type: %v", err)
+ }
+ if typ.Size() < 0 {
+ t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ)
+ }
+ }
+}
+
+func TestFieldOverlap(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ // This test grew out of issue 21094, where specific sudog<T> DWARF types
+ // had elem fields set to values instead of pointers.
+ const prog = `
+package main
+
+var c chan string
+
+func main() {
+ c <- "foo"
+}
+`
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if entry.Tag != dwarf.TagStructType {
+ continue
+ }
+ typ, err := d.Type(entry.Offset)
+ if err != nil {
+ t.Fatalf("can't read type: %v", err)
+ }
+ s := typ.(*dwarf.StructType)
+ for i := 0; i < len(s.Field); i++ {
+ end := s.Field[i].ByteOffset + s.Field[i].Type.Size()
+ var limit int64
+ if i == len(s.Field)-1 {
+ limit = s.Size()
+ } else {
+ limit = s.Field[i+1].ByteOffset
+ }
+ if end > limit {
+ name := entry.Val(dwarf.AttrName).(string)
+ t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name)
+ }
+ }
+ }
+}
+
+func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFile string, expectLine int, directive string) {
+ t.Parallel()
+
+ prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive)
+
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.main DIE
+ mains := ex.Named("main.main")
+ if len(mains) == 0 {
+ t.Fatalf("unable to locate DIE for main.main")
+ }
+ if len(mains) != 1 {
+ t.Fatalf("more than one main.main DIE")
+ }
+ maindie := mains[0]
+
+ // Vet the main.main DIE
+ if maindie.Tag != dwarf.TagSubprogram {
+ t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
+ }
+
+ // Walk main's children and select variable "i".
+ mainIdx := ex.IdxFromOffset(maindie.Offset)
+ childDies := ex.Children(mainIdx)
+ var iEntry *dwarf.Entry
+ for _, child := range childDies {
+ if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" {
+ iEntry = child
+ break
+ }
+ }
+ if iEntry == nil {
+ t.Fatalf("didn't find DW_TAG_variable for i in main.main")
+ }
+
+ // Verify line/file attributes.
+ line := iEntry.Val(dwarf.AttrDeclLine)
+ if line == nil || line.(int64) != int64(expectLine) {
+ t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine)
+ }
+
+ fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64)
+ if !fileIdxOK {
+ t.Errorf("missing or invalid DW_AT_decl_file for main")
+ }
+ file, err := ex.FileRef(d, mainIdx, fileIdx)
+ if err != nil {
+ t.Fatalf("FileRef: %v", err)
+ }
+ base := filepath.Base(file)
+ if base != expectFile {
+ t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile)
+ }
+}
+
+func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoords", "test.go", 5, "")
+}
+
+func TestVarDeclCoordsWithLineDirective(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoordsWithLineDirective",
+ "foobar.go", 202, "//line /foobar.go:200")
+}
+
+func TestInlinedRoutineRecords(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ const prog = `
+package main
+
+var G int
+
+func noinline(x int) int {
+ defer func() { G += x }()
+ return x
+}
+
+func cand(x, y int) int {
+ return noinline(x+y) ^ (y - x)
+}
+
+func main() {
+ x := cand(G*G,G|7%G)
+ G = x
+}
+`
+ dir := t.TempDir()
+
+ // Note: this is a build with "-l=4", as opposed to "-l -N". The
+ // test is intended to verify DWARF that is only generated when
+ // the inliner is active. We're only going to look at the DWARF for
+ // main.main, however, hence we build with "-gcflags=-l=4" as opposed
+ // to "-gcflags=all=-l=4".
+ f := gobuild(t, dir, prog, OptInl4)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // The inlined subroutines we expect to visit
+ expectedInl := []string{"main.cand"}
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.main DIE
+ mains := ex.Named("main.main")
+ if len(mains) == 0 {
+ t.Fatalf("unable to locate DIE for main.main")
+ }
+ if len(mains) != 1 {
+ t.Fatalf("more than one main.main DIE")
+ }
+ maindie := mains[0]
+
+ // Vet the main.main DIE
+ if maindie.Tag != dwarf.TagSubprogram {
+ t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
+ }
+
+ // Walk main's children and pick out the inlined subroutines
+ mainIdx := ex.IdxFromOffset(maindie.Offset)
+ childDies := ex.Children(mainIdx)
+ exCount := 0
+ for _, child := range childDies {
+ if child.Tag == dwarf.TagInlinedSubroutine {
+ // Found an inlined subroutine, locate abstract origin.
+ ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+ if !originOK {
+ t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
+ }
+ originDIE := ex.EntryFromOffset(ooff)
+ if originDIE == nil {
+ t.Fatalf("can't locate origin DIE at off %v", ooff)
+ }
+
+ // Walk the children of the abstract subroutine. We expect
+ // to see child variables there, even if (perhaps due to
+ // optimization) there are no references to them from the
+ // inlined subroutine DIE.
+ absFcnIdx := ex.IdxFromOffset(ooff)
+ absFcnChildDies := ex.Children(absFcnIdx)
+ if len(absFcnChildDies) != 2 {
+ t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies))
+ }
+ formalCount := 0
+ for _, absChild := range absFcnChildDies {
+ if absChild.Tag == dwarf.TagFormalParameter {
+ formalCount += 1
+ continue
+ }
+ t.Fatalf("abstract function child DIE: expected formal, got %v", absChild.Tag)
+ }
+ if formalCount != 2 {
+ t.Fatalf("abstract function DIE: expected 2 formals, got %d", formalCount)
+ }
+
+ if exCount >= len(expectedInl) {
+ t.Fatalf("too many inlined subroutines found in main.main")
+ }
+
+ // Name should check out.
+ expected := expectedInl[exCount]
+ if name, ok := originDIE.Val(dwarf.AttrName).(string); ok {
+ if name != expected {
+ t.Fatalf("expected inlined routine %s got %s", name, expected)
+ }
+ }
+ exCount++
+
+ // Verify that the call_file attribute for the inlined
+ // instance is ok. In this case it should match the file
+ // for the main routine. To do this we need to locate the
+ // compilation unit DIE that encloses what we're looking
+ // at; this can be done with the examiner.
+ cf, cfOK := child.Val(dwarf.AttrCallFile).(int64)
+ if !cfOK {
+ t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset)
+ }
+ file, err := ex.FileRef(d, mainIdx, cf)
+ if err != nil {
+ t.Errorf("FileRef: %v", err)
+ continue
+ }
+ base := filepath.Base(file)
+ if base != "test.go" {
+ t.Errorf("bad call_file attribute, found '%s', want '%s'",
+ file, "test.go")
+ }
+
+ omap := make(map[dwarf.Offset]bool)
+
+ // Walk the child variables of the inlined routine. Each
+ // of them should have a distinct abstract origin-- if two
+ // vars point to the same origin things are definitely broken.
+ inlIdx := ex.IdxFromOffset(child.Offset)
+ inlChildDies := ex.Children(inlIdx)
+ for _, k := range inlChildDies {
+ ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+ if !originOK {
+ t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset)
+ }
+ if _, found := omap[ooff]; found {
+ t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset)
+ }
+ omap[ooff] = true
+ }
+ }
+ }
+ if exCount != len(expectedInl) {
+ t.Fatalf("not enough inlined subroutines found in main.main")
+ }
+}
+
+func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ // Build with inlining, to exercise DWARF inlining support.
+ f := gobuildTestdata(t, dir, filepath.Join(pkgDir, "main"), flags)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Make a pass through all DIEs looking for abstract origin
+ // references.
+ abscount := 0
+ for i, die := range ex.DIEs() {
+ // Does it have an abstract origin?
+ ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+ if !originOK {
+ continue
+ }
+
+ // All abstract origin references should be resolvable.
+ abscount += 1
+ originDIE := ex.EntryFromOffset(ooff)
+ if originDIE == nil {
+ ex.DumpEntry(i, false, 0)
+ t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset)
+ }
+
+ // Suppose that DIE X has parameter/variable children {K1,
+ // K2, ... KN}. If X has an abstract origin of A, then for
+ // each KJ, the abstract origin of KJ should be a child of A.
+ // Note that this same rule doesn't hold for non-variable DIEs.
+ pidx := ex.IdxFromOffset(die.Offset)
+ if pidx < 0 {
+ t.Fatalf("can't locate DIE id")
+ }
+ kids := ex.Children(pidx)
+ for _, kid := range kids {
+ if kid.Tag != dwarf.TagVariable &&
+ kid.Tag != dwarf.TagFormalParameter {
+ continue
+ }
+ kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
+ if !originOK {
+ continue
+ }
+ childOriginDIE := ex.EntryFromOffset(kooff)
+ if childOriginDIE == nil {
+ ex.DumpEntry(i, false, 0)
+ t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset)
+ }
+ coidx := ex.IdxFromOffset(childOriginDIE.Offset)
+ childOriginParent := ex.Parent(coidx)
+ if childOriginParent != originDIE {
+ ex.DumpEntry(i, false, 0)
+ t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset)
+ }
+ }
+ }
+ if abscount == 0 {
+ t.Fatalf("no abstract origin refs found, something is wrong")
+ }
+}
+
+func TestAbstractOriginSanity(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ if wd, err := os.Getwd(); err == nil {
+ gopathdir := filepath.Join(wd, "testdata", "httptest")
+ abstractOriginSanity(t, gopathdir, OptAllInl4)
+ } else {
+ t.Fatalf("os.Getwd() failed %v", err)
+ }
+}
+
+func TestAbstractOriginSanityIssue25459(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ if runtime.GOARCH != "amd64" && runtime.GOARCH != "386" {
+ t.Skip("skipping on not-amd64 not-386; location lists not supported")
+ }
+
+ if wd, err := os.Getwd(); err == nil {
+ gopathdir := filepath.Join(wd, "testdata", "issue25459")
+ abstractOriginSanity(t, gopathdir, DefaultOpt)
+ } else {
+ t.Fatalf("os.Getwd() failed %v", err)
+ }
+}
+
+func TestAbstractOriginSanityIssue26237(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ if wd, err := os.Getwd(); err == nil {
+ gopathdir := filepath.Join(wd, "testdata", "issue26237")
+ abstractOriginSanity(t, gopathdir, DefaultOpt)
+ } else {
+ t.Fatalf("os.Getwd() failed %v", err)
+ }
+}
+
+func TestRuntimeTypeAttrInternal(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustInternalLink(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ if runtime.GOOS == "windows" {
+ t.Skip("skipping on windows; test is incompatible with relocatable binaries")
+ }
+
+ testRuntimeTypeAttr(t, "-ldflags=-linkmode=internal")
+}
+
+// External linking requires a host linker (https://golang.org/src/cmd/cgo/doc.go l.732)
+func TestRuntimeTypeAttrExternal(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ // Explicitly test external linking, for dsymutil compatibility on Darwin.
+ if runtime.GOARCH == "ppc64" {
+ t.Skip("-linkmode=external not supported on ppc64")
+ }
+
+ if runtime.GOOS == "windows" {
+ t.Skip("skipping on windows; test is incompatible with relocatable binaries")
+ }
+
+ testRuntimeTypeAttr(t, "-ldflags=-linkmode=external")
+}
+
+func testRuntimeTypeAttr(t *testing.T, flags string) {
+ t.Parallel()
+
+ const prog = `
+package main
+
+import "unsafe"
+
+type X struct{ _ int }
+
+func main() {
+ var x interface{} = &X{}
+ p := *(*uintptr)(unsafe.Pointer(&x))
+ print(p)
+}
+`
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, flags)
+ defer f.Close()
+
+ out, err := testenv.Command(t, f.path).CombinedOutput()
+ if err != nil {
+ t.Fatalf("could not run test program: %v", err)
+ }
+ addr, err := strconv.ParseUint(string(out), 10, 64)
+ if err != nil {
+ t.Fatalf("could not parse type address from program output %q: %v", out, err)
+ }
+
+ symbols, err := f.Symbols()
+ if err != nil {
+ t.Fatalf("error reading symbols: %v", err)
+ }
+ var types *objfilepkg.Sym
+ for _, sym := range symbols {
+ if sym.Name == "runtime.types" {
+ types = &sym
+ break
+ }
+ }
+ if types == nil {
+ t.Fatal("couldn't find runtime.types in symbols")
+ }
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ dies := ex.Named("*main.X")
+ if len(dies) != 1 {
+ t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies))
+ }
+ rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type)
+ if rtAttr == nil {
+ t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
+ }
+
+ if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
+ return // everything is PIE on ARM64, addresses are relocated
+ }
+ if rtAttr.(uint64)+types.Addr != addr {
+ t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
+ }
+}
+
+func TestIssue27614(t *testing.T) {
+ // Type references in debug_info should always use the DW_TAG_typedef_type
+ // for the type, when that's generated.
+
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ const prog = `package main
+
+import "fmt"
+
+type astruct struct {
+ X int
+}
+
+type bstruct struct {
+ X float32
+}
+
+var globalptr *astruct
+var globalvar astruct
+var bvar0, bvar1, bvar2 bstruct
+
+func main() {
+ fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2)
+}
+`
+
+ f := gobuild(t, dir, prog, NoOpt)
+
+ defer f.Close()
+
+ data, err := f.DWARF()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rdr := data.Reader()
+
+ var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry
+ var globalptrDIE, globalvarDIE *dwarf.Entry
+ var bvarDIE [3]*dwarf.Entry
+
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if e == nil {
+ break
+ }
+
+ name, _ := e.Val(dwarf.AttrName).(string)
+
+ switch e.Tag {
+ case dwarf.TagTypedef:
+ switch name {
+ case "main.astruct":
+ astructTypeDIE = e
+ case "main.bstruct":
+ bstructTypeDIE = e
+ }
+ case dwarf.TagPointerType:
+ if name == "*main.astruct" {
+ ptrastructTypeDIE = e
+ }
+ case dwarf.TagVariable:
+ switch name {
+ case "main.globalptr":
+ globalptrDIE = e
+ case "main.globalvar":
+ globalvarDIE = e
+ default:
+ const bvarprefix = "main.bvar"
+ if strings.HasPrefix(name, bvarprefix) {
+ i, _ := strconv.Atoi(name[len(bvarprefix):])
+ bvarDIE[i] = e
+ }
+ }
+ }
+ }
+
+ typedieof := func(e *dwarf.Entry) dwarf.Offset {
+ return e.Val(dwarf.AttrType).(dwarf.Offset)
+ }
+
+ if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset {
+ t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
+ }
+
+ if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset {
+ t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset)
+ }
+
+ if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset {
+ t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
+ }
+
+ for i := range bvarDIE {
+ if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset {
+ t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset)
+ }
+ }
+}
+
+func TestStaticTmp(t *testing.T) {
+ // Checks that statictmp variables do not appear in debug_info or the
+ // symbol table.
+ // Also checks that statictmp variables do not collide with user defined
+ // variables (issue #25113)
+
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ const prog = `package main
+
+var stmp_0 string
+var a []int
+
+func init() {
+ a = []int{ 7 }
+}
+
+func main() {
+ println(a[0])
+}
+`
+
+ f := gobuild(t, dir, prog, NoOpt)
+
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagVariable {
+ continue
+ }
+ name, ok := e.Val(dwarf.AttrName).(string)
+ if !ok {
+ continue
+ }
+ if strings.Contains(name, "stmp") {
+ t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset)
+ }
+ }
+
+ // When external linking, we put all symbols in the symbol table (so the
+ // external linker can find them). Skip the symbol table check.
+ // TODO: maybe there is some way to tell the external linker not to put
+ // those symbols in the executable's symbol table? Prefix the symbol name
+ // with "." or "L" to pretend it is a label?
+ if !testenv.CanInternalLink() {
+ return
+ }
+
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Fatalf("error reading symbols: %v", err)
+ }
+ for _, sym := range syms {
+ if strings.Contains(sym.Name, "stmp") {
+ t.Errorf("statictmp variable found in symbol table: %s", sym.Name)
+ }
+ }
+}
+
+func TestPackageNameAttr(t *testing.T) {
+ const dwarfAttrGoPackageName = dwarf.Attr(0x2905)
+ const dwarfGoLanguage = 22
+
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n"
+
+ f := gobuild(t, dir, prog, NoOpt)
+
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ runtimeUnitSeen := false
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage {
+ continue
+ }
+
+ pn, ok := e.Val(dwarfAttrGoPackageName).(string)
+ if !ok {
+ name, _ := e.Val(dwarf.AttrName).(string)
+ t.Errorf("found compile unit without package name: %s", name)
+
+ }
+ if pn == "" {
+ name, _ := e.Val(dwarf.AttrName).(string)
+ t.Errorf("found compile unit with empty package name: %s", name)
+ } else {
+ if pn == "runtime" {
+ runtimeUnitSeen = true
+ }
+ }
+ }
+
+ // Something is wrong if there's no runtime compilation unit.
+ if !runtimeUnitSeen {
+ t.Errorf("no package name for runtime unit")
+ }
+}
+
+func TestMachoIssue32233(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ if runtime.GOOS != "darwin" {
+ t.Skip("skipping; test only interesting on darwin")
+ }
+
+ tmpdir := t.TempDir()
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("where am I? %v", err)
+ }
+ pdir := filepath.Join(wd, "testdata", "issue32233", "main")
+ f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+ f.Close()
+}
+
+func TestWindowsIssue36495(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ if runtime.GOOS != "windows" {
+ t.Skip("skipping: test only on windows")
+ }
+
+ dir := t.TempDir()
+
+ prog := `
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello World")
+}`
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+ exe, err := pe.Open(f.path)
+ if err != nil {
+ t.Fatalf("error opening pe file: %v", err)
+ }
+ defer exe.Close()
+ dw, err := exe.DWARF()
+ if err != nil {
+ t.Fatalf("error parsing DWARF: %v", err)
+ }
+ rdr := dw.Reader()
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ lnrdr, err := dw.LineReader(e)
+ if err != nil {
+ t.Fatalf("error creating DWARF line reader: %v", err)
+ }
+ if lnrdr != nil {
+ var lne dwarf.LineEntry
+ for {
+ err := lnrdr.Next(&lne)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatalf("error reading next DWARF line: %v", err)
+ }
+ if strings.Contains(lne.File.Name, `\`) {
+ t.Errorf("filename should not contain backslash: %v", lne.File.Name)
+ }
+ }
+ }
+ rdr.SkipChildren()
+ }
+}
+
+func TestIssue38192(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ // Build a test program that contains a translation unit whose
+ // text (from am assembly source) contains only a single instruction.
+ tmpdir := t.TempDir()
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("where am I? %v", err)
+ }
+ pdir := filepath.Join(wd, "testdata", "issue38192")
+ f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+ defer f.Close()
+
+ // Open the resulting binary and examine the DWARF it contains.
+ // Look for the function of interest ("main.singleInstruction")
+ // and verify that the line table has an entry not just for the
+ // single instruction but also a dummy instruction following it,
+ // so as to test that whoever is emitting the DWARF doesn't
+ // emit an end-sequence op immediately after the last instruction
+ // in the translation unit.
+ //
+ // NB: another way to write this test would have been to run the
+ // resulting executable under GDB, set a breakpoint in
+ // "main.singleInstruction", then verify that GDB displays the
+ // correct line/file information. Given the headache and flakiness
+ // associated with GDB-based tests these days, a direct read of
+ // the line table seems more desirable.
+ rows := []dwarf.LineEntry{}
+ dw, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error parsing DWARF: %v", err)
+ }
+ rdr := dw.Reader()
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ // NB: there can be multiple compile units named "main".
+ name := e.Val(dwarf.AttrName).(string)
+ if name != "main" {
+ continue
+ }
+ lnrdr, err := dw.LineReader(e)
+ if err != nil {
+ t.Fatalf("error creating DWARF line reader: %v", err)
+ }
+ if lnrdr != nil {
+ var lne dwarf.LineEntry
+ for {
+ err := lnrdr.Next(&lne)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatalf("error reading next DWARF line: %v", err)
+ }
+ if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") {
+ continue
+ }
+ rows = append(rows, lne)
+ }
+ }
+ rdr.SkipChildren()
+ }
+ f.Close()
+
+ // Make sure that:
+ // - main.singleInstruction appears in the line table
+ // - more than one PC value appears the line table for
+ // that compilation unit.
+ // - at least one row has the correct line number (8)
+ pcs := make(map[uint64]bool)
+ line8seen := false
+ for _, r := range rows {
+ pcs[r.Address] = true
+ if r.Line == 8 {
+ line8seen = true
+ }
+ }
+ failed := false
+ if len(pcs) < 2 {
+ failed = true
+ t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs))
+ }
+ if !line8seen {
+ failed = true
+ t.Errorf("line table does not contain correct line for main.singleInstruction")
+ }
+ if !failed {
+ return
+ }
+ for i, r := range rows {
+ t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
+ }
+}
+
+func TestIssue39757(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ // In this bug the DWARF line table contents for the last couple of
+ // instructions in a function were incorrect (bad file/line). This
+ // test verifies that all of the line table rows for a function
+ // of interest have the same file (no "autogenerated").
+ //
+ // Note: the function in this test was written with an eye towards
+ // ensuring that there are no inlined routines from other packages
+ // (which could introduce other source files into the DWARF); it's
+ // possible that at some point things could evolve in the
+ // compiler/runtime in ways that aren't happening now, so this
+ // might be something to check for if it does start failing.
+
+ tmpdir := t.TempDir()
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("where am I? %v", err)
+ }
+ pdir := filepath.Join(wd, "testdata", "issue39757")
+ f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+ defer f.Close()
+
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var addr uint64
+ for _, sym := range syms {
+ if sym.Name == "main.main" {
+ addr = sym.Addr
+ break
+ }
+ }
+ if addr == 0 {
+ t.Fatal("cannot find main.main in symbols")
+ }
+
+ // Open the resulting binary and examine the DWARF it contains.
+ // Look for the function of interest ("main.main")
+ // and verify that all line table entries show the same source
+ // file.
+ dw, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error parsing DWARF: %v", err)
+ }
+ rdr := dw.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.main DIE
+ mains := ex.Named("main.main")
+ if len(mains) == 0 {
+ t.Fatalf("unable to locate DIE for main.main")
+ }
+ if len(mains) != 1 {
+ t.Fatalf("more than one main.main DIE")
+ }
+ maindie := mains[0]
+
+ // Collect the start/end PC for main.main
+ lowpc := maindie.Val(dwarf.AttrLowpc).(uint64)
+ highpc := maindie.Val(dwarf.AttrHighpc).(uint64)
+
+ // Now read the line table for the 'main' compilation unit.
+ mainIdx := ex.IdxFromOffset(maindie.Offset)
+ cuentry := ex.Parent(mainIdx)
+ if cuentry == nil {
+ t.Fatalf("main.main DIE appears orphaned")
+ }
+ lnrdr, lerr := dw.LineReader(cuentry)
+ if lerr != nil {
+ t.Fatalf("error creating DWARF line reader: %v", err)
+ }
+ if lnrdr == nil {
+ t.Fatalf("no line table for main.main compilation unit")
+ }
+ rows := []dwarf.LineEntry{}
+ mainrows := 0
+ var lne dwarf.LineEntry
+ for {
+ err := lnrdr.Next(&lne)
+ if err == io.EOF {
+ break
+ }
+ rows = append(rows, lne)
+ if err != nil {
+ t.Fatalf("error reading next DWARF line: %v", err)
+ }
+ if lne.Address < lowpc || lne.Address > highpc {
+ continue
+ }
+ if !strings.HasSuffix(lne.File.Name, "issue39757main.go") {
+ t.Errorf("found row with file=%s (not issue39757main.go)", lne.File.Name)
+ }
+ mainrows++
+ }
+ f.Close()
+
+ // Make sure we saw a few rows.
+ if mainrows < 3 {
+ t.Errorf("not enough line table rows for main.main (got %d, wanted > 3", mainrows)
+ for i, r := range rows {
+ t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
+ }
+ }
+}
+
+func TestIssue42484(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ tmpdir, err := os.MkdirTemp("", "TestIssue42484")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("where am I? %v", err)
+ }
+ pdir := filepath.Join(wd, "testdata", "issue42484")
+ f := gobuildTestdata(t, tmpdir, pdir, NoOpt)
+
+ var lastAddr uint64
+ var lastFile string
+ var lastLine int
+
+ dw, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error parsing DWARF: %v", err)
+ }
+ rdr := dw.Reader()
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if e == nil {
+ break
+ }
+ if e.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ lnrdr, err := dw.LineReader(e)
+ if err != nil {
+ t.Fatalf("error creating DWARF line reader: %v", err)
+ }
+ if lnrdr != nil {
+ var lne dwarf.LineEntry
+ for {
+ err := lnrdr.Next(&lne)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatalf("error reading next DWARF line: %v", err)
+ }
+ if lne.EndSequence {
+ continue
+ }
+ if lne.Address == lastAddr && (lne.File.Name != lastFile || lne.Line != lastLine) {
+ t.Errorf("address %#x is assigned to both %s:%d and %s:%d", lastAddr, lastFile, lastLine, lne.File.Name, lne.Line)
+ }
+ lastAddr = lne.Address
+ lastFile = lne.File.Name
+ lastLine = lne.Line
+ }
+ }
+ rdr.SkipChildren()
+ }
+ f.Close()
+}
+
+// processParams examines the formal parameter children of subprogram
+// DIE "die" using the explorer "ex" and returns a string that
+// captures the name, order, and classification of the subprogram's
+// input and output parameters. For example, for the go function
+//
+// func foo(i1 int, f1 float64) (string, bool) {
+//
+// this function would return a string something like
+//
+// 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
+ // param, and <varparam> is an integer:
+ //
+ // -1: varparm attr not found
+ // 1: varparm found with value false
+ // 2: varparm found with value true
+ //
+ foundParams := make(map[string]string)
+
+ // Walk the subprogram DIE's children looking for params.
+ pIdx := ex.IdxFromOffset(die.Offset)
+ childDies := ex.Children(pIdx)
+ idx := 0
+ for _, child := range childDies {
+ if child.Tag == dwarf.TagFormalParameter {
+ // NB: a setting of DW_AT_variable_parameter indicates
+ // that the param in question is an output parameter; we
+ // want to see this attribute set to TRUE for all Go
+ // return params. It would be OK to have it missing for
+ // input parameters, but for the moment we verify that the
+ // attr is present but set to false.
+ st := -1
+ if vp, ok := child.Val(dwarf.AttrVarParam).(bool); ok {
+ if vp {
+ st = 2
+ } else {
+ st = 1
+ }
+ }
+ if name, ok := child.Val(dwarf.AttrName).(string); ok {
+ foundParams[name] = fmt.Sprintf("%d:%d", idx, st)
+ idx++
+ }
+ }
+ }
+
+ found := make([]string, 0, len(foundParams))
+ for k, v := range foundParams {
+ found = append(found, fmt.Sprintf("%s:%s", k, v))
+ }
+ sort.Strings(found)
+
+ return fmt.Sprintf("%+v", found)
+}
+
+func TestOutputParamAbbrevAndAttr(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ // This test verifies that the compiler is selecting the correct
+ // DWARF abbreviation for output parameters, and that the
+ // variable parameter attribute is correct for in-params and
+ // out-params.
+
+ const prog = `
+package main
+
+//go:noinline
+func ABC(c1, c2, c3 int, d1, d2, d3, d4 string, f1, f2, f3 float32, g1 [1024]int) (r1 int, r2 int, r3 [1024]int, r4 byte, r5 string, r6 float32) {
+ g1[0] = 6
+ r1, r2, r3, r4, r5, r6 = c3, c2+c1, g1, 'a', d1+d2+d3+d4, f1+f2+f3
+ return
+}
+
+func main() {
+ a := [1024]int{}
+ v1, v2, v3, v4, v5, v6 := ABC(1, 2, 3, "a", "b", "c", "d", 1.0, 2.0, 1.0, a)
+ println(v1, v2, v3[0], v4, v5, v6)
+}
+`
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.ABC DIE
+ abcs := ex.Named("main.ABC")
+ if len(abcs) == 0 {
+ t.Fatalf("unable to locate DIE for main.ABC")
+ }
+ if len(abcs) != 1 {
+ t.Fatalf("more than one main.ABC DIE")
+ }
+ abcdie := abcs[0]
+
+ // Vet the DIE
+ if abcdie.Tag != dwarf.TagSubprogram {
+ t.Fatalf("unexpected tag %v on main.ABC DIE", abcdie.Tag)
+ }
+
+ // Call a helper to collect param info.
+ found := processParams(abcdie, &ex)
+
+ // Make sure we see all of the expected params in the proper
+ // order, that they have the varparam attr, and the varparam is
+ // set for the returns.
+ expected := "[c1:0:1 c2:1:1 c3:2:1 d1:3:1 d2:4:1 d3:5:1 d4:6:1 f1:7:1 f2:8:1 f3:9:1 g1:10:1 r1:11:2 r2:12:2 r3:13:2 r4:14:2 r5:15:2 r6:16:2]"
+ if found != expected {
+ t.Errorf("param check failed, wanted:\n%s\ngot:\n%s\n",
+ expected, found)
+ }
+}
+
+func TestDictIndex(t *testing.T) {
+ // Check that variables with a parametric type have a dictionary index
+ // attribute and that types that are only referenced through dictionaries
+ // have DIEs.
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ const prog = `
+package main
+
+import "fmt"
+
+type CustomInt int
+
+func testfn[T any](arg T) {
+ var mapvar = make(map[int]T)
+ mapvar[0] = arg
+ fmt.Println(arg, mapvar)
+}
+
+func main() {
+ testfn(CustomInt(3))
+}
+`
+
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, NoOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ found := false
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ name, _ := entry.Val(dwarf.AttrName).(string)
+ if strings.HasPrefix(name, "main.testfn") {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Fatalf("could not find main.testfn")
+ }
+
+ offs := []dwarf.Offset{}
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if entry.Tag == 0 {
+ break
+ }
+ name, _ := entry.Val(dwarf.AttrName).(string)
+ switch name {
+ case "arg", "mapvar":
+ offs = append(offs, entry.Val(dwarf.AttrType).(dwarf.Offset))
+ }
+ }
+ if len(offs) != 2 {
+ t.Errorf("wrong number of variables found in main.testfn %d", len(offs))
+ }
+ for _, off := range offs {
+ rdr.Seek(off)
+ entry, err := rdr.Next()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if _, ok := entry.Val(intdwarf.DW_AT_go_dict_index).(int64); !ok {
+ t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index))
+ }
+ }
+
+ rdr.Seek(0)
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ for _, typeName := range []string{"main.CustomInt", "map[int]main.CustomInt"} {
+ dies := ex.Named(typeName)
+ if len(dies) != 1 {
+ t.Errorf("wanted 1 DIE named %s, found %v", typeName, len(dies))
+ }
+ if dies[0].Val(intdwarf.DW_AT_go_runtime_type).(uint64) == 0 {
+ t.Errorf("type %s does not have DW_AT_go_runtime_type", typeName)
+ }
+ }
+}
+
+func TestOptimizedOutParamHandling(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ // This test is intended to verify that the compiler emits DWARF
+ // DIE entries for all input and output parameters, and that:
+ //
+ // - attributes are set correctly for output params,
+ // - things appear in the proper order
+ // - things work properly for both register-resident
+ // params and params passed on the stack
+ // - things work for both referenced and unreferenced params
+ // - things work for named return values un-named return vals
+ //
+ // The scenarios below don't cover all possible permutations and
+ // combinations, but they hit a bunch of the high points.
+
+ const prog = `
+package main
+
+// First testcase. All input params in registers, all params used.
+
+//go:noinline
+func tc1(p1, p2 int, p3 string) (int, string) {
+ return p1 + p2, p3 + "foo"
+}
+
+// Second testcase. Some params in registers, some on stack.
+
+//go:noinline
+func tc2(p1 int, p2 [128]int, p3 string) (int, string, [128]int) {
+ return p1 + p2[p1], p3 + "foo", [128]int{p1}
+}
+
+// Third testcase. Named return params.
+
+//go:noinline
+func tc3(p1 int, p2 [128]int, p3 string) (r1 int, r2 bool, r3 string, r4 [128]int) {
+ if p1 == 101 {
+ r1 = p1 + p2[p1]
+ r2 = p3 == "foo"
+ r4 = [128]int{p1}
+ return
+ } else {
+ return p1 - p2[p1+3], false, "bar", [128]int{p1 + 2}
+ }
+}
+
+// Fourth testcase. Some thing are used, some are unused.
+
+//go:noinline
+func tc4(p1, p1un int, p2, p2un [128]int, p3, p3un string) (r1 int, r1un int, r2 bool, r3 string, r4, r4un [128]int) {
+ if p1 == 101 {
+ r1 = p1 + p2[p2[0]]
+ r2 = p3 == "foo"
+ r4 = [128]int{p1}
+ return
+ } else {
+ return p1, -1, true, "plex", [128]int{p1 + 2}, [128]int{-1}
+ }
+}
+
+func main() {
+ {
+ r1, r2 := tc1(3, 4, "five")
+ println(r1, r2)
+ }
+ {
+ x := [128]int{9}
+ r1, r2, r3 := tc2(3, x, "five")
+ println(r1, r2, r3[0])
+ }
+ {
+ x := [128]int{9}
+ r1, r2, r3, r4 := tc3(3, x, "five")
+ println(r1, r2, r3, r4[0])
+ }
+ {
+ x := [128]int{3}
+ y := [128]int{7}
+ r1, r1u, r2, r3, r4, r4u := tc4(0, 1, x, y, "a", "b")
+ println(r1, r1u, r2, r3, r4[0], r4u[1])
+ }
+
+}
+`
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, DefaultOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ testcases := []struct {
+ tag string
+ expected string
+ }{
+ {
+ tag: "tc1",
+ expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2]",
+ },
+ {
+ tag: "tc2",
+ expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2 ~r2:5:2]",
+ },
+ {
+ tag: "tc3",
+ expected: "[p1:0:1 p2:1:1 p3:2:1 r1:3:2 r2:4:2 r3:5:2 r4:6:2]",
+ },
+ {
+ tag: "tc4",
+ expected: "[p1:0:1 p1un:1:1 p2:2:1 p2un:3:1 p3:4:1 p3un:5:1 r1:6:2 r1un:7:2 r2:8:2 r3:9:2 r4:10:2 r4un:11:2]",
+ },
+ }
+
+ for _, tc := range testcases {
+ // Locate the proper DIE
+ which := fmt.Sprintf("main.%s", tc.tag)
+ tcs := ex.Named(which)
+ if len(tcs) == 0 {
+ t.Fatalf("unable to locate DIE for " + which)
+ }
+ if len(tcs) != 1 {
+ t.Fatalf("more than one " + which + " DIE")
+ }
+ die := tcs[0]
+
+ // Vet the DIE
+ if die.Tag != dwarf.TagSubprogram {
+ t.Fatalf("unexpected tag %v on "+which+" DIE", die.Tag)
+ }
+
+ // Examine params for this subprogram.
+ foundParams := processParams(die, &ex)
+ if foundParams != tc.expected {
+ t.Errorf("check failed for testcase %s -- wanted:\n%s\ngot:%s\n",
+ tc.tag, tc.expected, foundParams)
+ }
+ }
+}
+func TestIssue54320(t *testing.T) {
+ // Check that when trampolines are used, the DWARF LPT is correctly
+ // emitted in the final binary
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ t.Parallel()
+
+ const prog = `
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Printf("Hello world\n");
+}
+`
+
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, "-ldflags=-debugtramp=2")
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ found := false
+ var entry *dwarf.Entry
+ for entry, err = rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+ if entry.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ name, _ := entry.Val(dwarf.AttrName).(string)
+ if name == "main" {
+ found = true
+ break
+ }
+ rdr.SkipChildren()
+ }
+
+ if !found {
+ t.Fatalf("could not find main compile unit")
+ }
+ lr, err := d.LineReader(entry)
+ if err != nil {
+ t.Fatalf("error obtaining linereader: %v", err)
+ }
+
+ var le dwarf.LineEntry
+ found = false
+ for {
+ if err := lr.Next(&le); err != nil {
+ if err == io.EOF {
+ break
+ }
+ t.Fatalf("error reading linentry: %v", err)
+ }
+ // check LE contains an entry to test.go
+ if le.File == nil {
+ continue
+ }
+ file := filepath.Base(le.File.Name)
+ if file == "test.go" {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("no LPT entries for test.go")
+ }
+}
+
+func TestZeroSizedVariable(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ // This test verifies that the compiler emits DIEs for zero sized variables
+ // (for example variables of type 'struct {}').
+ // See go.dev/issues/54615.
+
+ const prog = `
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ zeroSizedVariable := struct{}{}
+ fmt.Println(zeroSizedVariable)
+}
+`
+
+ for _, opt := range []string{NoOpt, DefaultOpt} {
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, opt)
+ defer f.Close()
+ defer os.RemoveAll(dir)
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.zeroSizedVariable DIE
+ abcs := ex.Named("zeroSizedVariable")
+ if len(abcs) == 0 {
+ t.Fatalf("unable to locate DIE for zeroSizedVariable")
+ }
+ if len(abcs) != 1 {
+ t.Fatalf("more than one zeroSizedVariable DIE")
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
new file mode 100644
index 0000000..a1ae7ea
--- /dev/null
+++ b/src/cmd/link/internal/ld/elf.go
@@ -0,0 +1,2395 @@
+// Copyright 2009 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.
+
+package ld
+
+import (
+ "cmd/internal/notsha256"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "internal/buildcfg"
+ "os"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+)
+
+/*
+ * Derived from:
+ * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $
+ * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $
+ * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $
+ * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $
+ * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $
+ * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $
+ * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $
+ * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $
+ * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $
+ *
+ * Copyright (c) 1996-1998 John D. Polstra. All rights reserved.
+ * Copyright (c) 2001 David E. O'Brien
+ * Portions Copyright 2009 The Go Authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * ELF definitions that are independent of architecture or word size.
+ */
+
+/*
+ * Note header. The ".note" section contains an array of notes. Each
+ * begins with this header, aligned to a word boundary. Immediately
+ * following the note header is n_namesz bytes of name, padded to the
+ * next word boundary. Then comes n_descsz bytes of descriptor, again
+ * padded to a word boundary. The values of n_namesz and n_descsz do
+ * not include the padding.
+ */
+type elfNote struct {
+ nNamesz uint32
+ nDescsz uint32
+ nType uint32
+}
+
+/* For accessing the fields of r_info. */
+
+/* For constructing r_info from field values. */
+
+/*
+ * Relocation types.
+ */
+const (
+ ARM_MAGIC_TRAMP_NUMBER = 0x5c000003
+)
+
+/*
+ * Symbol table entries.
+ */
+
+/* For accessing the fields of st_info. */
+
+/* For constructing st_info from field values. */
+
+/* For accessing the fields of st_other. */
+
+/*
+ * ELF header.
+ */
+type ElfEhdr elf.Header64
+
+/*
+ * Section header.
+ */
+type ElfShdr struct {
+ elf.Section64
+ shnum elf.SectionIndex
+}
+
+/*
+ * Program header.
+ */
+type ElfPhdr elf.ProgHeader
+
+/* For accessing the fields of r_info. */
+
+/* For constructing r_info from field values. */
+
+/*
+ * Symbol table entries.
+ */
+
+/* For accessing the fields of st_info. */
+
+/* For constructing st_info from field values. */
+
+/* For accessing the fields of st_other. */
+
+/*
+ * Go linker interface
+ */
+const (
+ ELF64HDRSIZE = 64
+ ELF64PHDRSIZE = 56
+ ELF64SHDRSIZE = 64
+ ELF64RELSIZE = 16
+ ELF64RELASIZE = 24
+ ELF64SYMSIZE = 24
+ ELF32HDRSIZE = 52
+ ELF32PHDRSIZE = 32
+ ELF32SHDRSIZE = 40
+ ELF32SYMSIZE = 16
+ ELF32RELSIZE = 8
+)
+
+/*
+ * The interface uses the 64-bit structures always,
+ * to avoid code duplication. The writers know how to
+ * marshal a 32-bit representation from the 64-bit structure.
+ */
+
+var Elfstrdat []byte
+
+/*
+ * Total amount of space to reserve at the start of the file
+ * for Header, PHeaders, SHeaders, and interp.
+ * May waste some.
+ * On FreeBSD, cannot be larger than a page.
+ */
+const (
+ ELFRESERVE = 4096
+)
+
+/*
+ * We use the 64-bit data structures on both 32- and 64-bit machines
+ * in order to write the code just once. The 64-bit data structure is
+ * written in the 32-bit format on the 32-bit machines.
+ */
+const (
+ NSECT = 400
+)
+
+var (
+ Nelfsym = 1
+
+ elf64 bool
+ // Either ".rel" or ".rela" depending on which type of relocation the
+ // target platform uses.
+ elfRelType string
+
+ ehdr ElfEhdr
+ phdr [NSECT]*ElfPhdr
+ shdr [NSECT]*ElfShdr
+
+ interp string
+)
+
+type Elfstring struct {
+ s string
+ off int
+}
+
+var elfstr [100]Elfstring
+
+var nelfstr int
+
+var buildinfo []byte
+
+/*
+Initialize the global variable that describes the ELF header. It will be updated as
+we write section and prog headers.
+*/
+func Elfinit(ctxt *Link) {
+ ctxt.IsELF = true
+
+ if ctxt.Arch.InFamily(sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) {
+ elfRelType = ".rela"
+ } else {
+ elfRelType = ".rel"
+ }
+
+ switch ctxt.Arch.Family {
+ // 64-bit architectures
+ case sys.PPC64, sys.S390X:
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ ehdr.Flags = 1 /* Version 1 ABI */
+ } else {
+ ehdr.Flags = 2 /* Version 2 ABI */
+ }
+ fallthrough
+ case sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.RISCV64:
+ if ctxt.Arch.Family == sys.MIPS64 {
+ ehdr.Flags = 0x20000004 /* MIPS 3 CPIC */
+ }
+ if ctxt.Arch.Family == sys.Loong64 {
+ ehdr.Flags = 0x3 /* LoongArch lp64d */
+ }
+ if ctxt.Arch.Family == sys.RISCV64 {
+ ehdr.Flags = 0x4 /* RISCV Float ABI Double */
+ }
+ elf64 = true
+
+ ehdr.Phoff = ELF64HDRSIZE /* Must be ELF64HDRSIZE: first PHdr must follow ELF header */
+ ehdr.Shoff = ELF64HDRSIZE /* Will move as we add PHeaders */
+ ehdr.Ehsize = ELF64HDRSIZE /* Must be ELF64HDRSIZE */
+ ehdr.Phentsize = ELF64PHDRSIZE /* Must be ELF64PHDRSIZE */
+ ehdr.Shentsize = ELF64SHDRSIZE /* Must be ELF64SHDRSIZE */
+
+ // 32-bit architectures
+ case sys.ARM, sys.MIPS:
+ if ctxt.Arch.Family == sys.ARM {
+ // we use EABI on linux/arm, freebsd/arm, netbsd/arm.
+ if ctxt.HeadType == objabi.Hlinux || ctxt.HeadType == objabi.Hfreebsd || ctxt.HeadType == objabi.Hnetbsd {
+ // We set a value here that makes no indication of which
+ // float ABI the object uses, because this is information
+ // used by the dynamic linker to compare executables and
+ // shared libraries -- so it only matters for cgo calls, and
+ // the information properly comes from the object files
+ // produced by the host C compiler. parseArmAttributes in
+ // ldelf.go reads that information and updates this field as
+ // appropriate.
+ ehdr.Flags = 0x5000002 // has entry point, Version5 EABI
+ }
+ } else if ctxt.Arch.Family == sys.MIPS {
+ ehdr.Flags = 0x50001004 /* MIPS 32 CPIC O32*/
+ }
+ fallthrough
+ default:
+ ehdr.Phoff = ELF32HDRSIZE
+ /* Must be ELF32HDRSIZE: first PHdr must follow ELF header */
+ ehdr.Shoff = ELF32HDRSIZE /* Will move as we add PHeaders */
+ ehdr.Ehsize = ELF32HDRSIZE /* Must be ELF32HDRSIZE */
+ ehdr.Phentsize = ELF32PHDRSIZE /* Must be ELF32PHDRSIZE */
+ ehdr.Shentsize = ELF32SHDRSIZE /* Must be ELF32SHDRSIZE */
+ }
+}
+
+// Make sure PT_LOAD is aligned properly and
+// that there is no gap,
+// correct ELF loaders will do this implicitly,
+// but buggy ELF loaders like the one in some
+// versions of QEMU and UPX won't.
+func fixElfPhdr(e *ElfPhdr) {
+ frag := int(e.Vaddr & (e.Align - 1))
+
+ e.Off -= uint64(frag)
+ e.Vaddr -= uint64(frag)
+ e.Paddr -= uint64(frag)
+ e.Filesz += uint64(frag)
+ e.Memsz += uint64(frag)
+}
+
+func elf64phdr(out *OutBuf, e *ElfPhdr) {
+ if e.Type == elf.PT_LOAD {
+ fixElfPhdr(e)
+ }
+
+ out.Write32(uint32(e.Type))
+ out.Write32(uint32(e.Flags))
+ out.Write64(e.Off)
+ out.Write64(e.Vaddr)
+ out.Write64(e.Paddr)
+ out.Write64(e.Filesz)
+ out.Write64(e.Memsz)
+ out.Write64(e.Align)
+}
+
+func elf32phdr(out *OutBuf, e *ElfPhdr) {
+ if e.Type == elf.PT_LOAD {
+ fixElfPhdr(e)
+ }
+
+ out.Write32(uint32(e.Type))
+ out.Write32(uint32(e.Off))
+ out.Write32(uint32(e.Vaddr))
+ out.Write32(uint32(e.Paddr))
+ out.Write32(uint32(e.Filesz))
+ out.Write32(uint32(e.Memsz))
+ out.Write32(uint32(e.Flags))
+ out.Write32(uint32(e.Align))
+}
+
+func elf64shdr(out *OutBuf, e *ElfShdr) {
+ out.Write32(e.Name)
+ out.Write32(uint32(e.Type))
+ out.Write64(uint64(e.Flags))
+ out.Write64(e.Addr)
+ out.Write64(e.Off)
+ out.Write64(e.Size)
+ out.Write32(e.Link)
+ out.Write32(e.Info)
+ out.Write64(e.Addralign)
+ out.Write64(e.Entsize)
+}
+
+func elf32shdr(out *OutBuf, e *ElfShdr) {
+ out.Write32(e.Name)
+ out.Write32(uint32(e.Type))
+ out.Write32(uint32(e.Flags))
+ out.Write32(uint32(e.Addr))
+ out.Write32(uint32(e.Off))
+ out.Write32(uint32(e.Size))
+ out.Write32(e.Link)
+ out.Write32(e.Info)
+ out.Write32(uint32(e.Addralign))
+ out.Write32(uint32(e.Entsize))
+}
+
+func elfwriteshdrs(out *OutBuf) uint32 {
+ if elf64 {
+ for i := 0; i < int(ehdr.Shnum); i++ {
+ elf64shdr(out, shdr[i])
+ }
+ return uint32(ehdr.Shnum) * ELF64SHDRSIZE
+ }
+
+ for i := 0; i < int(ehdr.Shnum); i++ {
+ elf32shdr(out, shdr[i])
+ }
+ return uint32(ehdr.Shnum) * ELF32SHDRSIZE
+}
+
+func elfsetstring(ctxt *Link, s loader.Sym, str string, off int) {
+ if nelfstr >= len(elfstr) {
+ ctxt.Errorf(s, "too many elf strings")
+ errorexit()
+ }
+
+ elfstr[nelfstr].s = str
+ elfstr[nelfstr].off = off
+ nelfstr++
+}
+
+func elfwritephdrs(out *OutBuf) uint32 {
+ if elf64 {
+ for i := 0; i < int(ehdr.Phnum); i++ {
+ elf64phdr(out, phdr[i])
+ }
+ return uint32(ehdr.Phnum) * ELF64PHDRSIZE
+ }
+
+ for i := 0; i < int(ehdr.Phnum); i++ {
+ elf32phdr(out, phdr[i])
+ }
+ return uint32(ehdr.Phnum) * ELF32PHDRSIZE
+}
+
+func newElfPhdr() *ElfPhdr {
+ e := new(ElfPhdr)
+ if ehdr.Phnum >= NSECT {
+ Errorf(nil, "too many phdrs")
+ } else {
+ phdr[ehdr.Phnum] = e
+ ehdr.Phnum++
+ }
+ if elf64 {
+ ehdr.Shoff += ELF64PHDRSIZE
+ } else {
+ ehdr.Shoff += ELF32PHDRSIZE
+ }
+ return e
+}
+
+func newElfShdr(name int64) *ElfShdr {
+ e := new(ElfShdr)
+ e.Name = uint32(name)
+ e.shnum = elf.SectionIndex(ehdr.Shnum)
+ if ehdr.Shnum >= NSECT {
+ Errorf(nil, "too many shdrs")
+ } else {
+ shdr[ehdr.Shnum] = e
+ ehdr.Shnum++
+ }
+
+ return e
+}
+
+func getElfEhdr() *ElfEhdr {
+ return &ehdr
+}
+
+func elf64writehdr(out *OutBuf) uint32 {
+ out.Write(ehdr.Ident[:])
+ out.Write16(uint16(ehdr.Type))
+ out.Write16(uint16(ehdr.Machine))
+ out.Write32(uint32(ehdr.Version))
+ out.Write64(ehdr.Entry)
+ out.Write64(ehdr.Phoff)
+ out.Write64(ehdr.Shoff)
+ out.Write32(ehdr.Flags)
+ out.Write16(ehdr.Ehsize)
+ out.Write16(ehdr.Phentsize)
+ out.Write16(ehdr.Phnum)
+ out.Write16(ehdr.Shentsize)
+ out.Write16(ehdr.Shnum)
+ out.Write16(ehdr.Shstrndx)
+ return ELF64HDRSIZE
+}
+
+func elf32writehdr(out *OutBuf) uint32 {
+ out.Write(ehdr.Ident[:])
+ out.Write16(uint16(ehdr.Type))
+ out.Write16(uint16(ehdr.Machine))
+ out.Write32(uint32(ehdr.Version))
+ out.Write32(uint32(ehdr.Entry))
+ out.Write32(uint32(ehdr.Phoff))
+ out.Write32(uint32(ehdr.Shoff))
+ out.Write32(ehdr.Flags)
+ out.Write16(ehdr.Ehsize)
+ out.Write16(ehdr.Phentsize)
+ out.Write16(ehdr.Phnum)
+ out.Write16(ehdr.Shentsize)
+ out.Write16(ehdr.Shnum)
+ out.Write16(ehdr.Shstrndx)
+ return ELF32HDRSIZE
+}
+
+func elfwritehdr(out *OutBuf) uint32 {
+ if elf64 {
+ return elf64writehdr(out)
+ }
+ return elf32writehdr(out)
+}
+
+/* Taken directly from the definition document for ELF64. */
+func elfhash(name string) uint32 {
+ var h uint32
+ for i := 0; i < len(name); i++ {
+ h = (h << 4) + uint32(name[i])
+ if g := h & 0xf0000000; g != 0 {
+ h ^= g >> 24
+ }
+ h &= 0x0fffffff
+ }
+ return h
+}
+
+func elfWriteDynEntSym(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) {
+ Elfwritedynentsymplus(ctxt, s, tag, t, 0)
+}
+
+func Elfwritedynent(arch *sys.Arch, s *loader.SymbolBuilder, tag elf.DynTag, val uint64) {
+ if elf64 {
+ s.AddUint64(arch, uint64(tag))
+ s.AddUint64(arch, val)
+ } else {
+ s.AddUint32(arch, uint32(tag))
+ s.AddUint32(arch, uint32(val))
+ }
+}
+
+func Elfwritedynentsymplus(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym, add int64) {
+ if elf64 {
+ s.AddUint64(ctxt.Arch, uint64(tag))
+ } else {
+ s.AddUint32(ctxt.Arch, uint32(tag))
+ }
+ s.AddAddrPlus(ctxt.Arch, t, add)
+}
+
+func elfwritedynentsymsize(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) {
+ if elf64 {
+ s.AddUint64(ctxt.Arch, uint64(tag))
+ } else {
+ s.AddUint32(ctxt.Arch, uint32(tag))
+ }
+ s.AddSize(ctxt.Arch, t)
+}
+
+func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int {
+ interp = p
+ n := len(interp) + 1
+ sh.Addr = startva + resoff - uint64(n)
+ sh.Off = resoff - uint64(n)
+ sh.Size = uint64(n)
+
+ return n
+}
+
+func elfwriteinterp(out *OutBuf) int {
+ sh := elfshname(".interp")
+ out.SeekSet(int64(sh.Off))
+ out.WriteString(interp)
+ out.Write8(0)
+ return int(sh.Size)
+}
+
+// member of .gnu.attributes of MIPS for fpAbi
+const (
+ // No floating point is present in the module (default)
+ MIPS_FPABI_NONE = 0
+ // FP code in the module uses the FP32 ABI for a 32-bit ABI
+ MIPS_FPABI_ANY = 1
+ // FP code in the module only uses single precision ABI
+ MIPS_FPABI_SINGLE = 2
+ // FP code in the module uses soft-float ABI
+ MIPS_FPABI_SOFT = 3
+ // FP code in the module assumes an FPU with FR=1 and has 12
+ // callee-saved doubles. Historic, no longer supported.
+ MIPS_FPABI_HIST = 4
+ // FP code in the module uses the FPXX ABI
+ MIPS_FPABI_FPXX = 5
+ // FP code in the module uses the FP64 ABI
+ MIPS_FPABI_FP64 = 6
+ // FP code in the module uses the FP64A ABI
+ MIPS_FPABI_FP64A = 7
+)
+
+func elfMipsAbiFlags(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := 24
+ sh.Addr = startva + resoff - uint64(n)
+ sh.Off = resoff - uint64(n)
+ sh.Size = uint64(n)
+ sh.Type = uint32(elf.SHT_MIPS_ABIFLAGS)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+
+ return n
+}
+
+// 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))
+ ctxt.Out.Write16(0) // version
+ ctxt.Out.Write8(32) // isaLevel
+ ctxt.Out.Write8(1) // isaRev
+ ctxt.Out.Write8(1) // gprSize
+ ctxt.Out.Write8(1) // cpr1Size
+ ctxt.Out.Write8(0) // cpr2Size
+ if buildcfg.GOMIPS == "softfloat" {
+ ctxt.Out.Write8(MIPS_FPABI_SOFT) // fpAbi
+ } else {
+ // Go cannot make sure non odd-number-fpr is used (ie, in load a double from memory).
+ // So, we mark the object is MIPS I style paired float/double register scheme,
+ // aka MIPS_FPABI_ANY. If we mark the object as FPXX, the kernel may use FR=1 mode,
+ // then we meet some problem.
+ // Note: MIPS_FPABI_ANY is bad naming: in fact it is MIPS I style FPR usage.
+ // It is not for 'ANY'.
+ // TODO: switch to FPXX after be sure that no odd-number-fpr is used.
+ ctxt.Out.Write8(MIPS_FPABI_ANY) // fpAbi
+ }
+ ctxt.Out.Write32(0) // isaExt
+ ctxt.Out.Write32(0) // ases
+ ctxt.Out.Write32(0) // flags1
+ ctxt.Out.Write32(0) // flags2
+ return int(sh.Size)
+}
+
+func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sizes ...int) int {
+ n := resoff % 4
+ // if section contains multiple notes (as is the case with FreeBSD signature),
+ // multiple note sizes can be specified
+ for _, sz := range sizes {
+ n += 3*4 + uint64(sz)
+ }
+
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 4
+ sh.Addr = startva + resoff - n
+ sh.Off = resoff - n
+ sh.Size = n - resoff%4
+
+ return int(n)
+}
+
+func elfwritenotehdr(out *OutBuf, str string, namesz uint32, descsz uint32, tag uint32) *ElfShdr {
+ sh := elfshname(str)
+
+ // Write Elf_Note header.
+ out.SeekSet(int64(sh.Off))
+
+ out.Write32(namesz)
+ out.Write32(descsz)
+ out.Write32(tag)
+
+ return sh
+}
+
+// NetBSD Signature (as per sys/exec_elf.h)
+const (
+ ELF_NOTE_NETBSD_NAMESZ = 7
+ ELF_NOTE_NETBSD_DESCSZ = 4
+ ELF_NOTE_NETBSD_TAG = 1
+ ELF_NOTE_NETBSD_VERSION = 700000000 /* NetBSD 7.0 */
+)
+
+var ELF_NOTE_NETBSD_NAME = []byte("NetBSD\x00")
+
+func elfnetbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := int(Rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + Rnd(ELF_NOTE_NETBSD_DESCSZ, 4))
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfwritenetbsdsig(out *OutBuf) int {
+ // Write Elf_Note header.
+ sh := elfwritenotehdr(out, ".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG)
+
+ if sh == nil {
+ return 0
+ }
+
+ // Followed by NetBSD string and version.
+ out.Write(ELF_NOTE_NETBSD_NAME)
+ out.Write8(0)
+ out.Write32(ELF_NOTE_NETBSD_VERSION)
+
+ return int(sh.Size)
+}
+
+// The race detector can't handle ASLR (address space layout randomization).
+// ASLR is on by default for NetBSD, so we turn the ASLR off explicitly
+// using a magic elf Note when building race binaries.
+
+func elfnetbsdpax(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := int(Rnd(4, 4) + Rnd(4, 4))
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfwritenetbsdpax(out *OutBuf) int {
+ sh := elfwritenotehdr(out, ".note.netbsd.pax", 4 /* length of PaX\x00 */, 4 /* length of flags */, 0x03 /* PaX type */)
+ if sh == nil {
+ return 0
+ }
+ out.Write([]byte("PaX\x00"))
+ out.Write32(0x20) // 0x20 = Force disable ASLR
+ return int(sh.Size)
+}
+
+// OpenBSD Signature
+const (
+ ELF_NOTE_OPENBSD_NAMESZ = 8
+ ELF_NOTE_OPENBSD_DESCSZ = 4
+ ELF_NOTE_OPENBSD_TAG = 1
+ ELF_NOTE_OPENBSD_VERSION = 0
+)
+
+var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00")
+
+func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfwriteopenbsdsig(out *OutBuf) int {
+ // Write Elf_Note header.
+ sh := elfwritenotehdr(out, ".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG)
+
+ if sh == nil {
+ return 0
+ }
+
+ // Followed by OpenBSD string and version.
+ out.Write(ELF_NOTE_OPENBSD_NAME)
+
+ out.Write32(ELF_NOTE_OPENBSD_VERSION)
+
+ return int(sh.Size)
+}
+
+// FreeBSD Signature (as per sys/elf_common.h)
+const (
+ ELF_NOTE_FREEBSD_NAMESZ = 8
+ ELF_NOTE_FREEBSD_DESCSZ = 4
+ ELF_NOTE_FREEBSD_ABI_TAG = 1
+ ELF_NOTE_FREEBSD_NOINIT_TAG = 2
+ ELF_NOTE_FREEBSD_FEATURE_CTL_TAG = 4
+ ELF_NOTE_FREEBSD_VERSION = 1203000 // 12.3-RELEASE
+ ELF_NOTE_FREEBSD_FCTL_ASLR_DISABLE = 0x1
+)
+
+const ELF_NOTE_FREEBSD_NAME = "FreeBSD\x00"
+
+func elffreebsdsig(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := ELF_NOTE_FREEBSD_NAMESZ + ELF_NOTE_FREEBSD_DESCSZ
+ // FreeBSD signature section contains 3 equally sized notes
+ return elfnote(sh, startva, resoff, n, n, n)
+}
+
+// elfwritefreebsdsig writes FreeBSD .note section.
+//
+// See https://www.netbsd.org/docs/kernel/elf-notes.html for the description of
+// a Note element format and
+// https://github.com/freebsd/freebsd-src/blob/main/sys/sys/elf_common.h#L790
+// for the FreeBSD-specific values.
+func elfwritefreebsdsig(out *OutBuf) int {
+ sh := elfshname(".note.tag")
+ if sh == nil {
+ return 0
+ }
+ out.SeekSet(int64(sh.Off))
+
+ // NT_FREEBSD_ABI_TAG
+ out.Write32(ELF_NOTE_FREEBSD_NAMESZ)
+ out.Write32(ELF_NOTE_FREEBSD_DESCSZ)
+ out.Write32(ELF_NOTE_FREEBSD_ABI_TAG)
+ out.WriteString(ELF_NOTE_FREEBSD_NAME)
+ out.Write32(ELF_NOTE_FREEBSD_VERSION)
+
+ // NT_FREEBSD_NOINIT_TAG
+ out.Write32(ELF_NOTE_FREEBSD_NAMESZ)
+ out.Write32(ELF_NOTE_FREEBSD_DESCSZ)
+ out.Write32(ELF_NOTE_FREEBSD_NOINIT_TAG)
+ out.WriteString(ELF_NOTE_FREEBSD_NAME)
+ out.Write32(0)
+
+ // NT_FREEBSD_FEATURE_CTL
+ out.Write32(ELF_NOTE_FREEBSD_NAMESZ)
+ out.Write32(ELF_NOTE_FREEBSD_DESCSZ)
+ out.Write32(ELF_NOTE_FREEBSD_FEATURE_CTL_TAG)
+ out.WriteString(ELF_NOTE_FREEBSD_NAME)
+ if *flagRace {
+ // The race detector can't handle ASLR, turn the ASLR off when compiling with -race.
+ out.Write32(ELF_NOTE_FREEBSD_FCTL_ASLR_DISABLE)
+ } else {
+ out.Write32(0)
+ }
+
+ return int(sh.Size)
+}
+
+func addbuildinfo(val string) {
+ if !strings.HasPrefix(val, "0x") {
+ Exitf("-B argument must start with 0x: %s", val)
+ }
+
+ ov := val
+ val = val[2:]
+
+ const maxLen = 32
+ if hex.DecodedLen(len(val)) > maxLen {
+ Exitf("-B option too long (max %d digits): %s", maxLen, ov)
+ }
+
+ b, err := hex.DecodeString(val)
+ if err != nil {
+ if err == hex.ErrLength {
+ Exitf("-B argument must have even number of digits: %s", ov)
+ }
+ if inv, ok := err.(hex.InvalidByteError); ok {
+ Exitf("-B argument contains invalid hex digit %c: %s", byte(inv), ov)
+ }
+ Exitf("-B argument contains invalid hex: %s", ov)
+ }
+
+ buildinfo = b
+}
+
+// Build info note
+const (
+ ELF_NOTE_BUILDINFO_NAMESZ = 4
+ ELF_NOTE_BUILDINFO_TAG = 3
+)
+
+var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00")
+
+func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4))
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfgobuildid(sh *ElfShdr, startva uint64, resoff uint64) int {
+ n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(*flagBuildid)), 4))
+ return elfnote(sh, startva, resoff, n)
+}
+
+func elfwritebuildinfo(out *OutBuf) int {
+ sh := elfwritenotehdr(out, ".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG)
+ if sh == nil {
+ return 0
+ }
+
+ out.Write(ELF_NOTE_BUILDINFO_NAME)
+ out.Write(buildinfo)
+ var zero = make([]byte, 4)
+ out.Write(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))])
+
+ return int(sh.Size)
+}
+
+func elfwritegobuildid(out *OutBuf) int {
+ sh := elfwritenotehdr(out, ".note.go.buildid", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(*flagBuildid)), ELF_NOTE_GOBUILDID_TAG)
+ if sh == nil {
+ return 0
+ }
+
+ out.Write(ELF_NOTE_GO_NAME)
+ out.Write([]byte(*flagBuildid))
+ var zero = make([]byte, 4)
+ out.Write(zero[:int(Rnd(int64(len(*flagBuildid)), 4)-int64(len(*flagBuildid)))])
+
+ return int(sh.Size)
+}
+
+// Go specific notes
+const (
+ ELF_NOTE_GOPKGLIST_TAG = 1
+ ELF_NOTE_GOABIHASH_TAG = 2
+ ELF_NOTE_GODEPS_TAG = 3
+ ELF_NOTE_GOBUILDID_TAG = 4
+)
+
+var ELF_NOTE_GO_NAME = []byte("Go\x00\x00")
+
+var elfverneed int
+
+type Elfaux struct {
+ next *Elfaux
+ num int
+ vers string
+}
+
+type Elflib struct {
+ next *Elflib
+ aux *Elfaux
+ file string
+}
+
+func addelflib(list **Elflib, file string, vers string) *Elfaux {
+ var lib *Elflib
+
+ for lib = *list; lib != nil; lib = lib.next {
+ if lib.file == file {
+ goto havelib
+ }
+ }
+ lib = new(Elflib)
+ lib.next = *list
+ lib.file = file
+ *list = lib
+
+havelib:
+ for aux := lib.aux; aux != nil; aux = aux.next {
+ if aux.vers == vers {
+ return aux
+ }
+ }
+ aux := new(Elfaux)
+ aux.next = lib.aux
+ aux.vers = vers
+ lib.aux = aux
+
+ return aux
+}
+
+func elfdynhash(ctxt *Link) {
+ if !ctxt.IsELF {
+ return
+ }
+
+ nsym := Nelfsym
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate(".hash", 0)
+ s.SetType(sym.SELFROSECT)
+
+ i := nsym
+ nbucket := 1
+ for i > 0 {
+ nbucket++
+ i >>= 1
+ }
+
+ var needlib *Elflib
+ need := make([]*Elfaux, nsym)
+ chain := make([]uint32, nsym)
+ buckets := make([]uint32, nbucket)
+
+ for _, sy := range ldr.DynidSyms() {
+
+ dynid := ldr.SymDynid(sy)
+ if ldr.SymDynimpvers(sy) != "" {
+ need[dynid] = addelflib(&needlib, ldr.SymDynimplib(sy), ldr.SymDynimpvers(sy))
+ }
+
+ name := ldr.SymExtname(sy)
+ hc := elfhash(name)
+
+ b := hc % uint32(nbucket)
+ chain[dynid] = buckets[b]
+ buckets[b] = uint32(dynid)
+ }
+
+ // s390x (ELF64) hash table entries are 8 bytes
+ if ctxt.Arch.Family == sys.S390X {
+ s.AddUint64(ctxt.Arch, uint64(nbucket))
+ s.AddUint64(ctxt.Arch, uint64(nsym))
+ for i := 0; i < nbucket; i++ {
+ s.AddUint64(ctxt.Arch, uint64(buckets[i]))
+ }
+ for i := 0; i < nsym; i++ {
+ s.AddUint64(ctxt.Arch, uint64(chain[i]))
+ }
+ } else {
+ s.AddUint32(ctxt.Arch, uint32(nbucket))
+ s.AddUint32(ctxt.Arch, uint32(nsym))
+ for i := 0; i < nbucket; i++ {
+ s.AddUint32(ctxt.Arch, buckets[i])
+ }
+ for i := 0; i < nsym; i++ {
+ s.AddUint32(ctxt.Arch, chain[i])
+ }
+ }
+
+ dynstr := ldr.CreateSymForUpdate(".dynstr", 0)
+
+ // version symbols
+ gnuVersionR := ldr.CreateSymForUpdate(".gnu.version_r", 0)
+ s = gnuVersionR
+ i = 2
+ nfile := 0
+ for l := needlib; l != nil; l = l.next {
+ nfile++
+
+ // header
+ s.AddUint16(ctxt.Arch, 1) // table version
+ j := 0
+ for x := l.aux; x != nil; x = x.next {
+ j++
+ }
+ s.AddUint16(ctxt.Arch, uint16(j)) // aux count
+ s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(l.file))) // file string offset
+ s.AddUint32(ctxt.Arch, 16) // offset from header to first aux
+ if l.next != nil {
+ s.AddUint32(ctxt.Arch, 16+uint32(j)*16) // offset from this header to next
+ } else {
+ s.AddUint32(ctxt.Arch, 0)
+ }
+
+ for x := l.aux; x != nil; x = x.next {
+ x.num = i
+ i++
+
+ // aux struct
+ s.AddUint32(ctxt.Arch, elfhash(x.vers)) // hash
+ s.AddUint16(ctxt.Arch, 0) // flags
+ s.AddUint16(ctxt.Arch, uint16(x.num)) // other - index we refer to this by
+ s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(x.vers))) // version string offset
+ if x.next != nil {
+ s.AddUint32(ctxt.Arch, 16) // offset from this aux to next
+ } else {
+ s.AddUint32(ctxt.Arch, 0)
+ }
+ }
+ }
+
+ // version references
+ gnuVersion := ldr.CreateSymForUpdate(".gnu.version", 0)
+ s = gnuVersion
+
+ for i := 0; i < nsym; i++ {
+ if i == 0 {
+ s.AddUint16(ctxt.Arch, 0) // first entry - no symbol
+ } else if need[i] == nil {
+ s.AddUint16(ctxt.Arch, 1) // global
+ } else {
+ s.AddUint16(ctxt.Arch, uint16(need[i].num))
+ }
+ }
+
+ s = ldr.CreateSymForUpdate(".dynamic", 0)
+ if ctxt.BuildMode == BuildModePIE {
+ // https://github.com/bminor/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/elf/elf.h#L986
+ const DTFLAGS_1_PIE = 0x08000000
+ Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(DTFLAGS_1_PIE))
+ }
+ elfverneed = nfile
+ if elfverneed != 0 {
+ elfWriteDynEntSym(ctxt, s, elf.DT_VERNEED, gnuVersionR.Sym())
+ Elfwritedynent(ctxt.Arch, s, elf.DT_VERNEEDNUM, uint64(nfile))
+ elfWriteDynEntSym(ctxt, s, elf.DT_VERSYM, gnuVersion.Sym())
+ }
+
+ sy := ldr.CreateSymForUpdate(elfRelType+".plt", 0)
+ if sy.Size() > 0 {
+ if elfRelType == ".rela" {
+ Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_RELA))
+ } else {
+ Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_REL))
+ }
+ elfwritedynentsymsize(ctxt, s, elf.DT_PLTRELSZ, sy.Sym())
+ elfWriteDynEntSym(ctxt, s, elf.DT_JMPREL, sy.Sym())
+ }
+
+ Elfwritedynent(ctxt.Arch, s, elf.DT_NULL, 0)
+}
+
+func elfphload(seg *sym.Segment) *ElfPhdr {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_LOAD
+ if seg.Rwx&4 != 0 {
+ ph.Flags |= elf.PF_R
+ }
+ if seg.Rwx&2 != 0 {
+ ph.Flags |= elf.PF_W
+ }
+ if seg.Rwx&1 != 0 {
+ ph.Flags |= elf.PF_X
+ }
+ ph.Vaddr = seg.Vaddr
+ ph.Paddr = seg.Vaddr
+ ph.Memsz = seg.Length
+ ph.Off = seg.Fileoff
+ ph.Filesz = seg.Filelen
+ ph.Align = uint64(*FlagRound)
+
+ return ph
+}
+
+func elfphrelro(seg *sym.Segment) {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_GNU_RELRO
+ ph.Vaddr = seg.Vaddr
+ ph.Paddr = seg.Vaddr
+ ph.Memsz = seg.Length
+ ph.Off = seg.Fileoff
+ ph.Filesz = seg.Filelen
+ ph.Align = uint64(*FlagRound)
+}
+
+func elfshname(name string) *ElfShdr {
+ for i := 0; i < nelfstr; i++ {
+ if name != elfstr[i].s {
+ continue
+ }
+ off := elfstr[i].off
+ for i = 0; i < int(ehdr.Shnum); i++ {
+ sh := shdr[i]
+ if sh.Name == uint32(off) {
+ return sh
+ }
+ }
+ return newElfShdr(int64(off))
+ }
+ Exitf("cannot find elf name %s", name)
+ return nil
+}
+
+// Create an ElfShdr for the section with name.
+// Create a duplicate if one already exists with that name.
+func elfshnamedup(name string) *ElfShdr {
+ for i := 0; i < nelfstr; i++ {
+ if name == elfstr[i].s {
+ off := elfstr[i].off
+ return newElfShdr(int64(off))
+ }
+ }
+
+ Errorf(nil, "cannot find elf name %s", name)
+ errorexit()
+ return nil
+}
+
+func elfshalloc(sect *sym.Section) *ElfShdr {
+ sh := elfshname(sect.Name)
+ sect.Elfsect = sh
+ return sh
+}
+
+func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr {
+ var sh *ElfShdr
+
+ if sect.Name == ".text" {
+ if sect.Elfsect == nil {
+ sect.Elfsect = elfshnamedup(sect.Name)
+ }
+ sh = sect.Elfsect.(*ElfShdr)
+ } else {
+ sh = elfshalloc(sect)
+ }
+
+ // If this section has already been set up as a note, we assume type_ and
+ // flags are already correct, but the other fields still need filling in.
+ if sh.Type == uint32(elf.SHT_NOTE) {
+ if linkmode != LinkExternal {
+ // TODO(mwhudson): the approach here will work OK when
+ // linking internally for notes that we want to be included
+ // in a loadable segment (e.g. the abihash note) but not for
+ // notes that we do not want to be mapped (e.g. the package
+ // list note). The real fix is probably to define new values
+ // for Symbol.Type corresponding to mapped and unmapped notes
+ // and handle them in dodata().
+ Errorf(nil, "sh.Type == SHT_NOTE in elfshbits when linking internally")
+ }
+ sh.Addralign = uint64(sect.Align)
+ sh.Size = sect.Length
+ sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
+ return sh
+ }
+ if sh.Type > 0 {
+ return sh
+ }
+
+ if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
+ switch sect.Name {
+ case ".init_array":
+ sh.Type = uint32(elf.SHT_INIT_ARRAY)
+ default:
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ }
+ } else {
+ sh.Type = uint32(elf.SHT_NOBITS)
+ }
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ if sect.Rwx&1 != 0 {
+ sh.Flags |= uint64(elf.SHF_EXECINSTR)
+ }
+ if sect.Rwx&2 != 0 {
+ sh.Flags |= uint64(elf.SHF_WRITE)
+ }
+ if sect.Name == ".tbss" {
+ 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)
+ }
+ }
+
+ sh.Addralign = uint64(sect.Align)
+ sh.Size = sect.Length
+ if sect.Name != ".tbss" {
+ sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
+ }
+
+ return sh
+}
+
+func elfshreloc(arch *sys.Arch, sect *sym.Section) *ElfShdr {
+ // If main section is SHT_NOBITS, nothing to relocate.
+ // Also nothing to relocate in .shstrtab or notes.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return nil
+ }
+ if sect.Name == ".shstrtab" || sect.Name == ".tbss" {
+ return nil
+ }
+ if sect.Elfsect.(*ElfShdr).Type == uint32(elf.SHT_NOTE) {
+ return nil
+ }
+
+ typ := elf.SHT_REL
+ if elfRelType == ".rela" {
+ typ = elf.SHT_RELA
+ }
+
+ sh := elfshname(elfRelType + sect.Name)
+ // There could be multiple text sections but each needs
+ // its own .rela.text.
+
+ if sect.Name == ".text" {
+ if sh.Info != 0 && sh.Info != uint32(sect.Elfsect.(*ElfShdr).shnum) {
+ sh = elfshnamedup(elfRelType + sect.Name)
+ }
+ }
+
+ sh.Type = uint32(typ)
+ sh.Entsize = uint64(arch.RegSize) * 2
+ if typ == elf.SHT_RELA {
+ sh.Entsize += uint64(arch.RegSize)
+ }
+ sh.Link = uint32(elfshname(".symtab").shnum)
+ sh.Info = uint32(sect.Elfsect.(*ElfShdr).shnum)
+ sh.Off = sect.Reloff
+ sh.Size = sect.Rellen
+ sh.Addralign = uint64(arch.RegSize)
+ return sh
+}
+
+func elfrelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) {
+ // If main section is SHT_NOBITS, nothing to relocate.
+ // Also nothing to relocate in .shstrtab.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return
+ }
+ if sect.Name == ".shstrtab" {
+ return
+ }
+
+ ldr := ctxt.loader
+ for i, s := range syms {
+ if !ldr.AttrReachable(s) {
+ panic("should never happen")
+ }
+ if uint64(ldr.SymValue(s)) >= sect.Vaddr {
+ syms = syms[i:]
+ break
+ }
+ }
+
+ eaddr := sect.Vaddr + sect.Length
+ for _, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymValue(s) >= int64(eaddr) {
+ break
+ }
+
+ // Compute external relocations on the go, and pass to Elfreloc1
+ // to stream out.
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rr, ok := extreloc(ctxt, ldr, s, r)
+ if !ok {
+ continue
+ }
+ if rr.Xsym == 0 {
+ ldr.Errorf(s, "missing xsym in relocation")
+ continue
+ }
+ esr := ElfSymForReloc(ctxt, rr.Xsym)
+ if esr == 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-elf symbol %s (outer=%s) %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymType(r.Sym()).String())
+ }
+ if !ldr.AttrReachable(rr.Xsym) {
+ ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym))
+ }
+ if !thearch.Elfreloc1(ctxt, out, ldr, s, rr, ri, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) {
+ ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym()))
+ }
+ }
+ }
+
+ // sanity check
+ if uint64(out.Offset()) != sect.Reloff+sect.Rellen {
+ panic(fmt.Sprintf("elfrelocsect: size mismatch %d != %d + %d", out.Offset(), sect.Reloff, sect.Rellen))
+ }
+}
+
+func elfEmitReloc(ctxt *Link) {
+ for ctxt.Out.Offset()&7 != 0 {
+ ctxt.Out.Write8(0)
+ }
+
+ sizeExtRelocs(ctxt, thearch.ElfrelocSize)
+ relocSect, wg := relocSectFn(ctxt, elfrelocsect)
+
+ for _, sect := range Segtext.Sections {
+ if sect.Name == ".text" {
+ relocSect(ctxt, sect, ctxt.Textp)
+ } else {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ }
+
+ for _, sect := range Segrodata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for _, sect := range Segrelrodata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for _, sect := range Segdata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for i := 0; i < len(Segdwarf.Sections); i++ {
+ sect := Segdwarf.Sections[i]
+ si := dwarfp[i]
+ if si.secSym() != loader.Sym(sect.Sym) ||
+ ctxt.loader.SymSect(si.secSym()) != sect {
+ panic("inconsistency between dwarfp and Segdwarf")
+ }
+ relocSect(ctxt, sect, si.syms)
+ }
+ wg.Wait()
+}
+
+func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) {
+ ldr := ctxt.loader
+ s := ldr.CreateSymForUpdate(sectionName, 0)
+ s.SetType(sym.SELFROSECT)
+ // namesz
+ s.AddUint32(ctxt.Arch, uint32(len(ELF_NOTE_GO_NAME)))
+ // descsz
+ s.AddUint32(ctxt.Arch, uint32(len(desc)))
+ // tag
+ s.AddUint32(ctxt.Arch, tag)
+ // name + padding
+ s.AddBytes(ELF_NOTE_GO_NAME)
+ for len(s.Data())%4 != 0 {
+ s.AddUint8(0)
+ }
+ // desc + padding
+ s.AddBytes(desc)
+ for len(s.Data())%4 != 0 {
+ s.AddUint8(0)
+ }
+ s.SetSize(int64(len(s.Data())))
+ s.SetAlign(4)
+}
+
+func (ctxt *Link) doelf() {
+ ldr := ctxt.loader
+
+ /* predefine strings we need for section headers */
+ shstrtab := ldr.CreateSymForUpdate(".shstrtab", 0)
+
+ shstrtab.SetType(sym.SELFROSECT)
+
+ shstrtab.Addstring("")
+ shstrtab.Addstring(".text")
+ shstrtab.Addstring(".noptrdata")
+ shstrtab.Addstring(".data")
+ shstrtab.Addstring(".bss")
+ shstrtab.Addstring(".noptrbss")
+ shstrtab.Addstring(".go.fuzzcntrs")
+ shstrtab.Addstring(".go.buildinfo")
+ if ctxt.IsMIPS() {
+ shstrtab.Addstring(".MIPS.abiflags")
+ shstrtab.Addstring(".gnu.attributes")
+ }
+
+ // generate .tbss section for dynamic internal linker or external
+ // linking, so that various binutils could correctly calculate
+ // PT_TLS size. See https://golang.org/issue/5200.
+ if !*FlagD || ctxt.IsExternal() {
+ shstrtab.Addstring(".tbss")
+ }
+ if ctxt.IsNetbsd() {
+ shstrtab.Addstring(".note.netbsd.ident")
+ if *flagRace {
+ shstrtab.Addstring(".note.netbsd.pax")
+ }
+ }
+ if ctxt.IsOpenbsd() {
+ shstrtab.Addstring(".note.openbsd.ident")
+ }
+ if ctxt.IsFreebsd() {
+ shstrtab.Addstring(".note.tag")
+ }
+ if len(buildinfo) > 0 {
+ shstrtab.Addstring(".note.gnu.build-id")
+ }
+ if *flagBuildid != "" {
+ shstrtab.Addstring(".note.go.buildid")
+ }
+ shstrtab.Addstring(".elfdata")
+ shstrtab.Addstring(".rodata")
+ // See the comment about data.rel.ro.FOO section names in data.go.
+ relro_prefix := ""
+ if ctxt.UseRelro() {
+ shstrtab.Addstring(".data.rel.ro")
+ relro_prefix = ".data.rel.ro"
+ }
+ shstrtab.Addstring(relro_prefix + ".typelink")
+ shstrtab.Addstring(relro_prefix + ".itablink")
+ shstrtab.Addstring(relro_prefix + ".gosymtab")
+ shstrtab.Addstring(relro_prefix + ".gopclntab")
+
+ if ctxt.IsExternal() {
+ *FlagD = true
+
+ shstrtab.Addstring(elfRelType + ".text")
+ shstrtab.Addstring(elfRelType + ".rodata")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".typelink")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".itablink")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".gosymtab")
+ shstrtab.Addstring(elfRelType + relro_prefix + ".gopclntab")
+ shstrtab.Addstring(elfRelType + ".noptrdata")
+ shstrtab.Addstring(elfRelType + ".data")
+ if ctxt.UseRelro() {
+ shstrtab.Addstring(elfRelType + ".data.rel.ro")
+ }
+ shstrtab.Addstring(elfRelType + ".go.buildinfo")
+ if ctxt.IsMIPS() {
+ shstrtab.Addstring(elfRelType + ".MIPS.abiflags")
+ shstrtab.Addstring(elfRelType + ".gnu.attributes")
+ }
+
+ // add a .note.GNU-stack section to mark the stack as non-executable
+ shstrtab.Addstring(".note.GNU-stack")
+
+ if ctxt.IsShared() {
+ shstrtab.Addstring(".note.go.abihash")
+ shstrtab.Addstring(".note.go.pkg-list")
+ shstrtab.Addstring(".note.go.deps")
+ }
+ }
+
+ hasinitarr := ctxt.linkShared
+
+ /* shared library initializer */
+ switch ctxt.BuildMode {
+ case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin:
+ hasinitarr = true
+ }
+
+ if hasinitarr {
+ shstrtab.Addstring(".init_array")
+ shstrtab.Addstring(elfRelType + ".init_array")
+ }
+
+ if !*FlagS {
+ shstrtab.Addstring(".symtab")
+ shstrtab.Addstring(".strtab")
+ dwarfaddshstrings(ctxt, shstrtab)
+ }
+
+ shstrtab.Addstring(".shstrtab")
+
+ if !*FlagD { /* -d suppresses dynamic loader format */
+ shstrtab.Addstring(".interp")
+ shstrtab.Addstring(".hash")
+ shstrtab.Addstring(".got")
+ if ctxt.IsPPC64() {
+ shstrtab.Addstring(".glink")
+ }
+ shstrtab.Addstring(".got.plt")
+ shstrtab.Addstring(".dynamic")
+ shstrtab.Addstring(".dynsym")
+ shstrtab.Addstring(".dynstr")
+ shstrtab.Addstring(elfRelType)
+ shstrtab.Addstring(elfRelType + ".plt")
+
+ shstrtab.Addstring(".plt")
+ shstrtab.Addstring(".gnu.version")
+ shstrtab.Addstring(".gnu.version_r")
+
+ /* dynamic symbol table - first entry all zeros */
+ dynsym := ldr.CreateSymForUpdate(".dynsym", 0)
+
+ dynsym.SetType(sym.SELFROSECT)
+ if elf64 {
+ dynsym.SetSize(dynsym.Size() + ELF64SYMSIZE)
+ } else {
+ dynsym.SetSize(dynsym.Size() + ELF32SYMSIZE)
+ }
+
+ /* dynamic string table */
+ dynstr := ldr.CreateSymForUpdate(".dynstr", 0)
+
+ dynstr.SetType(sym.SELFROSECT)
+ if dynstr.Size() == 0 {
+ dynstr.Addstring("")
+ }
+
+ /* relocation table */
+ s := ldr.CreateSymForUpdate(elfRelType, 0)
+ s.SetType(sym.SELFROSECT)
+
+ /* global offset table */
+ got := ldr.CreateSymForUpdate(".got", 0)
+ got.SetType(sym.SELFGOT) // writable
+
+ /* ppc64 glink resolver */
+ if ctxt.IsPPC64() {
+ s := ldr.CreateSymForUpdate(".glink", 0)
+ s.SetType(sym.SELFRXSECT)
+ }
+
+ /* hash */
+ hash := ldr.CreateSymForUpdate(".hash", 0)
+ hash.SetType(sym.SELFROSECT)
+
+ gotplt := ldr.CreateSymForUpdate(".got.plt", 0)
+ gotplt.SetType(sym.SELFSECT) // writable
+
+ plt := ldr.CreateSymForUpdate(".plt", 0)
+ if ctxt.IsPPC64() {
+ // In the ppc64 ABI, .plt is a data section
+ // written by the dynamic linker.
+ plt.SetType(sym.SELFSECT)
+ } else {
+ plt.SetType(sym.SELFRXSECT)
+ }
+
+ s = ldr.CreateSymForUpdate(elfRelType+".plt", 0)
+ s.SetType(sym.SELFROSECT)
+
+ s = ldr.CreateSymForUpdate(".gnu.version", 0)
+ s.SetType(sym.SELFROSECT)
+
+ s = ldr.CreateSymForUpdate(".gnu.version_r", 0)
+ s.SetType(sym.SELFROSECT)
+
+ /* define dynamic elf table */
+ dynamic := ldr.CreateSymForUpdate(".dynamic", 0)
+ dynamic.SetType(sym.SELFSECT) // writable
+
+ if ctxt.IsS390X() {
+ // S390X uses .got instead of .got.plt
+ gotplt = got
+ }
+ thearch.Elfsetupplt(ctxt, plt, gotplt, dynamic.Sym())
+
+ /*
+ * .dynamic table
+ */
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_HASH, hash.Sym())
+
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_SYMTAB, dynsym.Sym())
+ if elf64 {
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF64SYMSIZE)
+ } else {
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF32SYMSIZE)
+ }
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_STRTAB, dynstr.Sym())
+ elfwritedynentsymsize(ctxt, dynamic, elf.DT_STRSZ, dynstr.Sym())
+ if elfRelType == ".rela" {
+ rela := ldr.LookupOrCreateSym(".rela", 0)
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_RELA, rela)
+ elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELASZ, rela)
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELAENT, ELF64RELASIZE)
+ } else {
+ rel := ldr.LookupOrCreateSym(".rel", 0)
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_REL, rel)
+ elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELSZ, rel)
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELENT, ELF32RELSIZE)
+ }
+
+ if rpath.val != "" {
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RUNPATH, uint64(dynstr.Addstring(rpath.val)))
+ }
+
+ if ctxt.IsPPC64() {
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_PLTGOT, plt.Sym())
+ } else {
+ elfWriteDynEntSym(ctxt, dynamic, elf.DT_PLTGOT, gotplt.Sym())
+ }
+
+ if ctxt.IsPPC64() {
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_PPC64_OPT, 0)
+ }
+
+ // Solaris dynamic linker can't handle an empty .rela.plt if
+ // DT_JMPREL is emitted so we have to defer generation of elf.DT_PLTREL,
+ // DT_PLTRELSZ, and elf.DT_JMPREL dynamic entries until after we know the
+ // size of .rel(a).plt section.
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_DEBUG, 0)
+ }
+
+ if ctxt.IsShared() {
+ // The go.link.abihashbytes symbol will be pointed at the appropriate
+ // part of the .note.go.abihash section in data.go:func address().
+ s := ldr.LookupOrCreateSym("go:link.abihashbytes", 0)
+ sb := ldr.MakeSymbolUpdater(s)
+ ldr.SetAttrLocal(s, true)
+ sb.SetType(sym.SRODATA)
+ ldr.SetAttrSpecial(s, true)
+ sb.SetReachable(true)
+ sb.SetSize(notsha256.Size)
+
+ sort.Sort(byPkg(ctxt.Library))
+ h := notsha256.New()
+ for _, l := range ctxt.Library {
+ h.Write(l.Fingerprint[:])
+ }
+ addgonote(ctxt, ".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{}))
+ addgonote(ctxt, ".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote)
+ var deplist []string
+ for _, shlib := range ctxt.Shlibs {
+ deplist = append(deplist, filepath.Base(shlib.Path))
+ }
+ addgonote(ctxt, ".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n")))
+ }
+
+ if ctxt.LinkMode == LinkExternal && *flagBuildid != "" {
+ addgonote(ctxt, ".note.go.buildid", ELF_NOTE_GOBUILDID_TAG, []byte(*flagBuildid))
+ }
+
+ //type mipsGnuAttributes struct {
+ // version uint8 // 'A'
+ // length uint32 // 15 including itself
+ // gnu [4]byte // "gnu\0"
+ // tag uint8 // 1:file, 2: section, 3: symbol, 1 here
+ // taglen uint32 // tag length, including tag, 7 here
+ // tagfp uint8 // 4
+ // fpAbi uint8 // see .MIPS.abiflags
+ //}
+ if ctxt.IsMIPS() {
+ gnuattributes := ldr.CreateSymForUpdate(".gnu.attributes", 0)
+ gnuattributes.SetType(sym.SELFROSECT)
+ gnuattributes.SetReachable(true)
+ gnuattributes.AddUint8('A') // version 'A'
+ gnuattributes.AddUint32(ctxt.Arch, 15) // length 15 including itself
+ gnuattributes.AddBytes([]byte("gnu\x00")) // "gnu\0"
+ gnuattributes.AddUint8(1) // 1:file, 2: section, 3: symbol, 1 here
+ gnuattributes.AddUint32(ctxt.Arch, 7) // tag length, including tag, 7 here
+ gnuattributes.AddUint8(4) // 4 for FP, 8 for MSA
+ if buildcfg.GOMIPS == "softfloat" {
+ gnuattributes.AddUint8(MIPS_FPABI_SOFT)
+ } else {
+ // Note: MIPS_FPABI_ANY is bad naming: in fact it is MIPS I style FPR usage.
+ // It is not for 'ANY'.
+ // TODO: switch to FPXX after be sure that no odd-number-fpr is used.
+ gnuattributes.AddUint8(MIPS_FPABI_ANY)
+ }
+ }
+}
+
+// Do not write DT_NULL. elfdynhash will finish it.
+func shsym(sh *ElfShdr, ldr *loader.Loader, s loader.Sym) {
+ if s == 0 {
+ panic("bad symbol in shsym2")
+ }
+ addr := ldr.SymValue(s)
+ if sh.Flags&uint64(elf.SHF_ALLOC) != 0 {
+ sh.Addr = uint64(addr)
+ }
+ sh.Off = uint64(datoff(ldr, s, addr))
+ sh.Size = uint64(ldr.SymSize(s))
+}
+
+func phsh(ph *ElfPhdr, sh *ElfShdr) {
+ ph.Vaddr = sh.Addr
+ ph.Paddr = ph.Vaddr
+ ph.Off = sh.Off
+ ph.Filesz = sh.Size
+ ph.Memsz = sh.Size
+ ph.Align = sh.Addralign
+}
+
+func Asmbelfsetup() {
+ /* This null SHdr must appear before all others */
+ elfshname("")
+
+ for _, sect := range Segtext.Sections {
+ // There could be multiple .text sections. Instead check the Elfsect
+ // field to determine if already has an ElfShdr and if not, create one.
+ if sect.Name == ".text" {
+ if sect.Elfsect == nil {
+ sect.Elfsect = elfshnamedup(sect.Name)
+ }
+ } else {
+ elfshalloc(sect)
+ }
+ }
+ for _, sect := range Segrodata.Sections {
+ elfshalloc(sect)
+ }
+ for _, sect := range Segrelrodata.Sections {
+ elfshalloc(sect)
+ }
+ for _, sect := range Segdata.Sections {
+ elfshalloc(sect)
+ }
+ for _, sect := range Segdwarf.Sections {
+ elfshalloc(sect)
+ }
+}
+
+func asmbElf(ctxt *Link) {
+ var symo int64
+ if !*FlagS {
+ symo = int64(Segdwarf.Fileoff + Segdwarf.Filelen)
+ symo = Rnd(symo, int64(ctxt.Arch.PtrSize))
+ ctxt.Out.SeekSet(symo)
+ asmElfSym(ctxt)
+ ctxt.Out.Write(Elfstrdat)
+ if ctxt.IsExternal() {
+ elfEmitReloc(ctxt)
+ }
+ }
+ ctxt.Out.SeekSet(0)
+
+ ldr := ctxt.loader
+ eh := getElfEhdr()
+ switch ctxt.Arch.Family {
+ default:
+ Exitf("unknown architecture in asmbelf: %v", ctxt.Arch.Family)
+ case sys.MIPS, sys.MIPS64:
+ eh.Machine = uint16(elf.EM_MIPS)
+ case sys.Loong64:
+ eh.Machine = uint16(elf.EM_LOONGARCH)
+ case sys.ARM:
+ eh.Machine = uint16(elf.EM_ARM)
+ case sys.AMD64:
+ eh.Machine = uint16(elf.EM_X86_64)
+ case sys.ARM64:
+ eh.Machine = uint16(elf.EM_AARCH64)
+ case sys.I386:
+ eh.Machine = uint16(elf.EM_386)
+ case sys.PPC64:
+ eh.Machine = uint16(elf.EM_PPC64)
+ case sys.RISCV64:
+ eh.Machine = uint16(elf.EM_RISCV)
+ case sys.S390X:
+ eh.Machine = uint16(elf.EM_S390)
+ }
+
+ elfreserve := int64(ELFRESERVE)
+
+ numtext := int64(0)
+ for _, sect := range Segtext.Sections {
+ if sect.Name == ".text" {
+ numtext++
+ }
+ }
+
+ // If there are multiple text sections, extra space is needed
+ // in the elfreserve for the additional .text and .rela.text
+ // section headers. It can handle 4 extra now. Headers are
+ // 64 bytes.
+
+ if numtext > 4 {
+ elfreserve += elfreserve + numtext*64*2
+ }
+
+ startva := *FlagTextAddr - int64(HEADR)
+ resoff := elfreserve
+
+ var pph *ElfPhdr
+ var pnote *ElfPhdr
+ getpnote := func() *ElfPhdr {
+ if pnote == nil {
+ pnote = newElfPhdr()
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
+ }
+ return pnote
+ }
+ if *flagRace && ctxt.IsNetbsd() {
+ sh := elfshname(".note.netbsd.pax")
+ resoff -= int64(elfnetbsdpax(sh, uint64(startva), uint64(resoff)))
+ phsh(getpnote(), sh)
+ }
+ if ctxt.LinkMode == LinkExternal {
+ /* skip program headers */
+ eh.Phoff = 0
+
+ eh.Phentsize = 0
+
+ if ctxt.BuildMode == BuildModeShared {
+ sh := elfshname(".note.go.pkg-list")
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh = elfshname(".note.go.abihash")
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh = elfshname(".note.go.deps")
+ sh.Type = uint32(elf.SHT_NOTE)
+ }
+
+ if *flagBuildid != "" {
+ sh := elfshname(".note.go.buildid")
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ }
+
+ goto elfobj
+ }
+
+ /* program header info */
+ pph = newElfPhdr()
+
+ pph.Type = elf.PT_PHDR
+ pph.Flags = elf.PF_R
+ pph.Off = uint64(eh.Ehsize)
+ pph.Vaddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off
+ pph.Paddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off
+ pph.Align = uint64(*FlagRound)
+
+ /*
+ * PHDR must be in a loaded segment. Adjust the text
+ * segment boundaries downwards to include it.
+ */
+ {
+ o := int64(Segtext.Vaddr - pph.Vaddr)
+ Segtext.Vaddr -= uint64(o)
+ Segtext.Length += uint64(o)
+ o = int64(Segtext.Fileoff - pph.Off)
+ Segtext.Fileoff -= uint64(o)
+ Segtext.Filelen += uint64(o)
+ }
+
+ if !*FlagD { /* -d suppresses dynamic loader format */
+ /* interpreter */
+ sh := elfshname(".interp")
+
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 1
+
+ if interpreter == "" && buildcfg.GOOS == runtime.GOOS && buildcfg.GOARCH == runtime.GOARCH && buildcfg.GO_LDSO != "" {
+ interpreter = buildcfg.GO_LDSO
+ }
+
+ if interpreter == "" {
+ switch ctxt.HeadType {
+ case objabi.Hlinux:
+ if buildcfg.GOOS == "android" {
+ interpreter = thearch.Androiddynld
+ if interpreter == "" {
+ Exitf("ELF interpreter not set")
+ }
+ } else {
+ interpreter = thearch.Linuxdynld
+ // If interpreter does not exist, try musl instead.
+ // This lets the same cmd/link binary work on
+ // both glibc-based and musl-based systems.
+ if _, err := os.Stat(interpreter); err != nil {
+ if musl := thearch.LinuxdynldMusl; musl != "" {
+ if _, err := os.Stat(musl); err == nil {
+ interpreter = musl
+ }
+ }
+ }
+ }
+
+ case objabi.Hfreebsd:
+ interpreter = thearch.Freebsddynld
+
+ case objabi.Hnetbsd:
+ interpreter = thearch.Netbsddynld
+
+ case objabi.Hopenbsd:
+ interpreter = thearch.Openbsddynld
+
+ case objabi.Hdragonfly:
+ interpreter = thearch.Dragonflydynld
+
+ case objabi.Hsolaris:
+ interpreter = thearch.Solarisdynld
+ }
+ }
+
+ resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter))
+
+ ph := newElfPhdr()
+ ph.Type = elf.PT_INTERP
+ ph.Flags = elf.PF_R
+ phsh(ph, sh)
+ }
+
+ if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd || ctxt.HeadType == objabi.Hfreebsd {
+ var sh *ElfShdr
+ switch ctxt.HeadType {
+ case objabi.Hnetbsd:
+ sh = elfshname(".note.netbsd.ident")
+ resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff)))
+
+ case objabi.Hopenbsd:
+ sh = elfshname(".note.openbsd.ident")
+ resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff)))
+
+ case objabi.Hfreebsd:
+ sh = elfshname(".note.tag")
+ resoff -= int64(elffreebsdsig(sh, uint64(startva), uint64(resoff)))
+ }
+ // NetBSD, OpenBSD and FreeBSD require ident in an independent segment.
+ pnotei := newElfPhdr()
+ pnotei.Type = elf.PT_NOTE
+ pnotei.Flags = elf.PF_R
+ phsh(pnotei, sh)
+ }
+
+ if len(buildinfo) > 0 {
+ sh := elfshname(".note.gnu.build-id")
+ resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff)))
+ phsh(getpnote(), sh)
+ }
+
+ if *flagBuildid != "" {
+ sh := elfshname(".note.go.buildid")
+ resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff)))
+ phsh(getpnote(), sh)
+ }
+
+ // Additions to the reserved area must be above this line.
+
+ elfphload(&Segtext)
+ if len(Segrodata.Sections) > 0 {
+ elfphload(&Segrodata)
+ }
+ if len(Segrelrodata.Sections) > 0 {
+ elfphload(&Segrelrodata)
+ elfphrelro(&Segrelrodata)
+ }
+ elfphload(&Segdata)
+
+ /* Dynamic linking sections */
+ if !*FlagD {
+ sh := elfshname(".dynsym")
+ sh.Type = uint32(elf.SHT_DYNSYM)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ if elf64 {
+ sh.Entsize = ELF64SYMSIZE
+ } else {
+ sh.Entsize = ELF32SYMSIZE
+ }
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".dynstr").shnum)
+
+ // sh.info is the index of first non-local symbol (number of local symbols)
+ s := ldr.Lookup(".dynsym", 0)
+ i := uint32(0)
+ for sub := s; sub != 0; sub = ldr.SubSym(sub) {
+ i++
+ if !ldr.AttrLocal(sub) {
+ break
+ }
+ }
+ sh.Info = i
+ shsym(sh, ldr, s)
+
+ sh = elfshname(".dynstr")
+ sh.Type = uint32(elf.SHT_STRTAB)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 1
+ shsym(sh, ldr, ldr.Lookup(".dynstr", 0))
+
+ if elfverneed != 0 {
+ sh := elfshname(".gnu.version")
+ sh.Type = uint32(elf.SHT_GNU_VERSYM)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 2
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ sh.Entsize = 2
+ shsym(sh, ldr, ldr.Lookup(".gnu.version", 0))
+
+ sh = elfshname(".gnu.version_r")
+ sh.Type = uint32(elf.SHT_GNU_VERNEED)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Info = uint32(elfverneed)
+ sh.Link = uint32(elfshname(".dynstr").shnum)
+ shsym(sh, ldr, ldr.Lookup(".gnu.version_r", 0))
+ }
+
+ if elfRelType == ".rela" {
+ sh := elfshname(".rela.plt")
+ sh.Type = uint32(elf.SHT_RELA)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = ELF64RELASIZE
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ sh.Info = uint32(elfshname(".plt").shnum)
+ shsym(sh, ldr, ldr.Lookup(".rela.plt", 0))
+
+ sh = elfshname(".rela")
+ sh.Type = uint32(elf.SHT_RELA)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = ELF64RELASIZE
+ sh.Addralign = 8
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ shsym(sh, ldr, ldr.Lookup(".rela", 0))
+ } else {
+ sh := elfshname(".rel.plt")
+ sh.Type = uint32(elf.SHT_REL)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = ELF32RELSIZE
+ sh.Addralign = 4
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ shsym(sh, ldr, ldr.Lookup(".rel.plt", 0))
+
+ sh = elfshname(".rel")
+ sh.Type = uint32(elf.SHT_REL)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = ELF32RELSIZE
+ sh.Addralign = 4
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ shsym(sh, ldr, ldr.Lookup(".rel", 0))
+ }
+
+ if elf.Machine(eh.Machine) == elf.EM_PPC64 {
+ sh := elfshname(".glink")
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR)
+ sh.Addralign = 4
+ shsym(sh, ldr, ldr.Lookup(".glink", 0))
+ }
+
+ sh = elfshname(".plt")
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR)
+ if elf.Machine(eh.Machine) == elf.EM_X86_64 {
+ sh.Entsize = 16
+ } else if elf.Machine(eh.Machine) == elf.EM_S390 {
+ sh.Entsize = 32
+ } else if elf.Machine(eh.Machine) == elf.EM_PPC64 {
+ // On ppc64, this is just a table of addresses
+ // filled by the dynamic linker
+ sh.Type = uint32(elf.SHT_NOBITS)
+
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
+ sh.Entsize = 8
+ } else {
+ sh.Entsize = 4
+ }
+ sh.Addralign = sh.Entsize
+ shsym(sh, ldr, ldr.Lookup(".plt", 0))
+
+ // On ppc64, .got comes from the input files, so don't
+ // create it here, and .got.plt is not used.
+ if elf.Machine(eh.Machine) != elf.EM_PPC64 {
+ sh := elfshname(".got")
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
+ sh.Entsize = uint64(ctxt.Arch.RegSize)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ shsym(sh, ldr, ldr.Lookup(".got", 0))
+
+ sh = elfshname(".got.plt")
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
+ sh.Entsize = uint64(ctxt.Arch.RegSize)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ shsym(sh, ldr, ldr.Lookup(".got.plt", 0))
+ }
+
+ sh = elfshname(".hash")
+ sh.Type = uint32(elf.SHT_HASH)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Entsize = 4
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".dynsym").shnum)
+ shsym(sh, ldr, ldr.Lookup(".hash", 0))
+
+ /* sh and elf.PT_DYNAMIC for .dynamic section */
+ sh = elfshname(".dynamic")
+
+ sh.Type = uint32(elf.SHT_DYNAMIC)
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
+ sh.Entsize = 2 * uint64(ctxt.Arch.RegSize)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".dynstr").shnum)
+ shsym(sh, ldr, ldr.Lookup(".dynamic", 0))
+ ph := newElfPhdr()
+ ph.Type = elf.PT_DYNAMIC
+ ph.Flags = elf.PF_R + elf.PF_W
+ phsh(ph, sh)
+
+ /*
+ * Thread-local storage segment (really just size).
+ */
+ tlssize := uint64(0)
+ for _, sect := range Segdata.Sections {
+ if sect.Name == ".tbss" {
+ tlssize = sect.Length
+ }
+ }
+ if tlssize != 0 {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_TLS
+ ph.Flags = elf.PF_R
+ ph.Memsz = tlssize
+ ph.Align = uint64(ctxt.Arch.RegSize)
+ }
+ }
+
+ if ctxt.HeadType == objabi.Hlinux {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_GNU_STACK
+ ph.Flags = elf.PF_W + elf.PF_R
+ ph.Align = uint64(ctxt.Arch.RegSize)
+
+ ph = newElfPhdr()
+ ph.Type = elf.PT_PAX_FLAGS
+ ph.Flags = 0x2a00 // mprotect, randexec, emutramp disabled
+ ph.Align = uint64(ctxt.Arch.RegSize)
+ } else if ctxt.HeadType == objabi.Hsolaris {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_SUNWSTACK
+ ph.Flags = elf.PF_W + elf.PF_R
+ } else if ctxt.HeadType == objabi.Hfreebsd {
+ ph := newElfPhdr()
+ ph.Type = elf.PT_GNU_STACK
+ ph.Flags = elf.PF_W + elf.PF_R
+ ph.Align = uint64(ctxt.Arch.RegSize)
+ }
+
+elfobj:
+ sh := elfshname(".shstrtab")
+ sh.Type = uint32(elf.SHT_STRTAB)
+ sh.Addralign = 1
+ shsym(sh, ldr, ldr.Lookup(".shstrtab", 0))
+ eh.Shstrndx = uint16(sh.shnum)
+
+ if ctxt.IsMIPS() {
+ sh = elfshname(".MIPS.abiflags")
+ sh.Type = uint32(elf.SHT_MIPS_ABIFLAGS)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 8
+ resoff -= int64(elfMipsAbiFlags(sh, uint64(startva), uint64(resoff)))
+
+ ph := newElfPhdr()
+ ph.Type = elf.PT_MIPS_ABIFLAGS
+ ph.Flags = elf.PF_R
+ phsh(ph, sh)
+
+ sh = elfshname(".gnu.attributes")
+ sh.Type = uint32(elf.SHT_GNU_ATTRIBUTES)
+ sh.Addralign = 1
+ ldr := ctxt.loader
+ shsym(sh, ldr, ldr.Lookup(".gnu.attributes", 0))
+ }
+
+ // put these sections early in the list
+ if !*FlagS {
+ elfshname(".symtab")
+ elfshname(".strtab")
+ }
+
+ for _, sect := range Segtext.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+ for _, sect := range Segrodata.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+ for _, sect := range Segrelrodata.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+ for _, sect := range Segdata.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+ for _, sect := range Segdwarf.Sections {
+ elfshbits(ctxt.LinkMode, sect)
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ for _, sect := range Segtext.Sections {
+ elfshreloc(ctxt.Arch, sect)
+ }
+ for _, sect := range Segrodata.Sections {
+ elfshreloc(ctxt.Arch, sect)
+ }
+ for _, sect := range Segrelrodata.Sections {
+ elfshreloc(ctxt.Arch, sect)
+ }
+ for _, sect := range Segdata.Sections {
+ elfshreloc(ctxt.Arch, sect)
+ }
+ for _, si := range dwarfp {
+ sect := ldr.SymSect(si.secSym())
+ elfshreloc(ctxt.Arch, sect)
+ }
+ // add a .note.GNU-stack section to mark the stack as non-executable
+ sh := elfshname(".note.GNU-stack")
+
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Addralign = 1
+ sh.Flags = 0
+ }
+
+ if !*FlagS {
+ sh := elfshname(".symtab")
+ sh.Type = uint32(elf.SHT_SYMTAB)
+ sh.Off = uint64(symo)
+ sh.Size = uint64(symSize)
+ sh.Addralign = uint64(ctxt.Arch.RegSize)
+ sh.Entsize = 8 + 2*uint64(ctxt.Arch.RegSize)
+ sh.Link = uint32(elfshname(".strtab").shnum)
+ sh.Info = uint32(elfglobalsymndx)
+
+ sh = elfshname(".strtab")
+ sh.Type = uint32(elf.SHT_STRTAB)
+ sh.Off = uint64(symo) + uint64(symSize)
+ sh.Size = uint64(len(Elfstrdat))
+ sh.Addralign = 1
+ }
+
+ /* Main header */
+ copy(eh.Ident[:], elf.ELFMAG)
+
+ var osabi elf.OSABI
+ switch ctxt.HeadType {
+ case objabi.Hfreebsd:
+ osabi = elf.ELFOSABI_FREEBSD
+ case objabi.Hnetbsd:
+ osabi = elf.ELFOSABI_NETBSD
+ case objabi.Hopenbsd:
+ osabi = elf.ELFOSABI_OPENBSD
+ case objabi.Hdragonfly:
+ osabi = elf.ELFOSABI_NONE
+ }
+ eh.Ident[elf.EI_OSABI] = byte(osabi)
+
+ if elf64 {
+ eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS64)
+ } else {
+ eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS32)
+ }
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2MSB)
+ } else {
+ eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2LSB)
+ }
+ eh.Ident[elf.EI_VERSION] = byte(elf.EV_CURRENT)
+
+ if ctxt.LinkMode == LinkExternal {
+ eh.Type = uint16(elf.ET_REL)
+ } else if ctxt.BuildMode == BuildModePIE {
+ eh.Type = uint16(elf.ET_DYN)
+ } else {
+ eh.Type = uint16(elf.ET_EXEC)
+ }
+
+ if ctxt.LinkMode != LinkExternal {
+ eh.Entry = uint64(Entryvalue(ctxt))
+ }
+
+ eh.Version = uint32(elf.EV_CURRENT)
+
+ if pph != nil {
+ pph.Filesz = uint64(eh.Phnum) * uint64(eh.Phentsize)
+ pph.Memsz = pph.Filesz
+ }
+
+ ctxt.Out.SeekSet(0)
+ a := int64(0)
+ a += int64(elfwritehdr(ctxt.Out))
+ a += int64(elfwritephdrs(ctxt.Out))
+ a += int64(elfwriteshdrs(ctxt.Out))
+ if !*FlagD {
+ a += int64(elfwriteinterp(ctxt.Out))
+ }
+ if ctxt.IsMIPS() {
+ a += int64(elfWriteMipsAbiFlags(ctxt))
+ }
+
+ if ctxt.LinkMode != LinkExternal {
+ if ctxt.HeadType == objabi.Hnetbsd {
+ a += int64(elfwritenetbsdsig(ctxt.Out))
+ }
+ if ctxt.HeadType == objabi.Hopenbsd {
+ a += int64(elfwriteopenbsdsig(ctxt.Out))
+ }
+ if ctxt.HeadType == objabi.Hfreebsd {
+ a += int64(elfwritefreebsdsig(ctxt.Out))
+ }
+ if len(buildinfo) > 0 {
+ a += int64(elfwritebuildinfo(ctxt.Out))
+ }
+ if *flagBuildid != "" {
+ a += int64(elfwritegobuildid(ctxt.Out))
+ }
+ }
+ if *flagRace && ctxt.IsNetbsd() {
+ a += int64(elfwritenetbsdpax(ctxt.Out))
+ }
+
+ if a > elfreserve {
+ Errorf(nil, "ELFRESERVE too small: %d > %d with %d text sections", a, elfreserve, numtext)
+ }
+
+ // Verify the amount of space allocated for the elf header is sufficient. The file offsets are
+ // already computed in layout, so we could spill into another section.
+ if a > int64(HEADR) {
+ Errorf(nil, "HEADR too small: %d > %d with %d text sections", a, HEADR, numtext)
+ }
+}
+
+func elfadddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
+ ldr.SetSymDynid(s, int32(Nelfsym))
+ Nelfsym++
+ d := ldr.MakeSymbolUpdater(syms.DynSym)
+ name := ldr.SymExtname(s)
+ dstru := ldr.MakeSymbolUpdater(syms.DynStr)
+ st := ldr.SymType(s)
+ cgoeStatic := ldr.AttrCgoExportStatic(s)
+ cgoeDynamic := ldr.AttrCgoExportDynamic(s)
+ cgoexp := (cgoeStatic || cgoeDynamic)
+
+ d.AddUint32(target.Arch, uint32(dstru.Addstring(name)))
+
+ if elf64 {
+
+ /* type */
+ var t uint8
+
+ if cgoexp && st == sym.STEXT {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
+ } else {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT)
+ }
+ d.AddUint8(t)
+
+ /* reserved */
+ d.AddUint8(0)
+
+ /* section where symbol is defined */
+ if st == sym.SDYNIMPORT {
+ d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF))
+ } else {
+ d.AddUint16(target.Arch, 1)
+ }
+
+ /* value */
+ if st == sym.SDYNIMPORT {
+ d.AddUint64(target.Arch, 0)
+ } else {
+ d.AddAddrPlus(target.Arch, s, 0)
+ }
+
+ /* size of object */
+ d.AddUint64(target.Arch, uint64(len(ldr.Data(s))))
+
+ dil := ldr.SymDynimplib(s)
+
+ if !cgoeDynamic && dil != "" && !seenlib[dil] {
+ du := ldr.MakeSymbolUpdater(syms.Dynamic)
+ Elfwritedynent(target.Arch, du, elf.DT_NEEDED, uint64(dstru.Addstring(dil)))
+ seenlib[dil] = true
+ }
+ } else {
+
+ /* value */
+ if st == sym.SDYNIMPORT {
+ d.AddUint32(target.Arch, 0)
+ } else {
+ d.AddAddrPlus(target.Arch, s, 0)
+ }
+
+ /* size of object */
+ d.AddUint32(target.Arch, uint32(len(ldr.Data(s))))
+
+ /* type */
+ var t uint8
+
+ // TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386.
+ if target.Arch.Family == sys.I386 && cgoexp && st == sym.STEXT {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
+ } else if target.Arch.Family == sys.ARM && cgoeDynamic && st == sym.STEXT {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
+ } else {
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT)
+ }
+ d.AddUint8(t)
+ d.AddUint8(0)
+
+ /* shndx */
+ if st == sym.SDYNIMPORT {
+ d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF))
+ } else {
+ d.AddUint16(target.Arch, 1)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/elf_test.go b/src/cmd/link/internal/ld/elf_test.go
new file mode 100644
index 0000000..8af0ca1
--- /dev/null
+++ b/src/cmd/link/internal/ld/elf_test.go
@@ -0,0 +1,127 @@
+// Copyright 2019 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.
+
+//go:build cgo
+// +build cgo
+
+package ld
+
+import (
+ "debug/elf"
+ "internal/testenv"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestDynSymShInfo(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ dir := t.TempDir()
+
+ const prog = `
+package main
+
+import "net"
+
+func main() {
+ net.Dial("", "")
+}
+`
+ src := filepath.Join(dir, "issue33358.go")
+ if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ binFile := filepath.Join(dir, "issue33358")
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+ }
+
+ fi, err := os.Open(binFile)
+ if err != nil {
+ t.Fatalf("failed to open built file: %v", err)
+ }
+ defer fi.Close()
+
+ elfFile, err := elf.NewFile(fi)
+ if err != nil {
+ t.Skip("The system may not support ELF, skipped.")
+ }
+
+ section := elfFile.Section(".dynsym")
+ if section == nil {
+ t.Fatal("no dynsym")
+ }
+
+ symbols, err := elfFile.DynamicSymbols()
+ if err != nil {
+ t.Fatalf("failed to get dynamic symbols: %v", err)
+ }
+
+ var numLocalSymbols uint32
+ for i, s := range symbols {
+ if elf.ST_BIND(s.Info) != elf.STB_LOCAL {
+ numLocalSymbols = uint32(i + 1)
+ break
+ }
+ }
+
+ if section.Info != numLocalSymbols {
+ t.Fatalf("Unexpected sh info, want greater than 0, got: %d", section.Info)
+ }
+}
+
+func TestNoDuplicateNeededEntries(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ // run this test on just a small set of platforms (no need to test it
+ // across the board given the nature of the test).
+ pair := runtime.GOOS + "-" + runtime.GOARCH
+ switch pair {
+ case "linux-amd64", "linux-arm64", "freebsd-amd64", "openbsd-amd64":
+ default:
+ t.Skip("no need for test on " + pair)
+ }
+
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("Failed to get working directory: %v", err)
+ }
+
+ path := filepath.Join(dir, "x")
+ argv := []string{"build", "-o", path, filepath.Join(wd, "testdata", "issue39256")}
+ out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
+ if err != nil {
+ t.Fatalf("Build failure: %s\n%s\n", err, string(out))
+ }
+
+ f, err := elf.Open(path)
+ if err != nil {
+ t.Fatalf("Failed to open ELF file: %v", err)
+ }
+ libs, err := f.ImportedLibraries()
+ if err != nil {
+ t.Fatalf("Failed to read imported libraries: %v", err)
+ }
+
+ var count int
+ for _, lib := range libs {
+ if lib == "libc.so" || strings.HasPrefix(lib, "libc.so.") {
+ count++
+ }
+ }
+
+ if got, want := count, 1; got != want {
+ t.Errorf("Got %d entries for `libc.so`, want %d", got, want)
+ }
+}
diff --git a/src/cmd/link/internal/ld/errors.go b/src/cmd/link/internal/ld/errors.go
new file mode 100644
index 0000000..b553d68
--- /dev/null
+++ b/src/cmd/link/internal/ld/errors.go
@@ -0,0 +1,67 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+ "cmd/internal/obj"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "sync"
+)
+
+type unresolvedSymKey struct {
+ from loader.Sym // Symbol that referenced unresolved "to"
+ to loader.Sym // Unresolved symbol referenced by "from"
+}
+
+type symNameFn func(s loader.Sym) string
+
+// ErrorReporter is used to make error reporting thread safe.
+type ErrorReporter struct {
+ loader.ErrorReporter
+ unresSyms map[unresolvedSymKey]bool
+ unresMutex sync.Mutex
+ SymName symNameFn
+}
+
+// errorUnresolved prints unresolved symbol error for rs that is referenced from s.
+func (reporter *ErrorReporter) errorUnresolved(ldr *loader.Loader, s, rs loader.Sym) {
+ reporter.unresMutex.Lock()
+ defer reporter.unresMutex.Unlock()
+
+ if reporter.unresSyms == nil {
+ reporter.unresSyms = make(map[unresolvedSymKey]bool)
+ }
+ k := unresolvedSymKey{from: s, to: rs}
+ if !reporter.unresSyms[k] {
+ reporter.unresSyms[k] = true
+ name := ldr.SymName(rs)
+
+ // Try to find symbol under another ABI.
+ var reqABI, haveABI obj.ABI
+ haveABI = ^obj.ABI(0)
+ reqABI, ok := sym.VersionToABI(ldr.SymVersion(rs))
+ if ok {
+ for abi := obj.ABI(0); abi < obj.ABICount; abi++ {
+ v := sym.ABIToVersion(abi)
+ if v == -1 {
+ continue
+ }
+ if rs1 := ldr.Lookup(name, v); rs1 != 0 && ldr.SymType(rs1) != sym.Sxxx && ldr.SymType(rs1) != sym.SXREF {
+ haveABI = abi
+ }
+ }
+ }
+
+ // Give a special error message for main symbol (see #24809).
+ if name == "main.main" {
+ reporter.Errorf(s, "function main is undeclared in the main package")
+ } else if haveABI != ^obj.ABI(0) {
+ reporter.Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", name, reqABI, haveABI)
+ } else {
+ reporter.Errorf(s, "relocation target %s not defined", name)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/execarchive.go b/src/cmd/link/internal/ld/execarchive.go
new file mode 100644
index 0000000..a9376e9
--- /dev/null
+++ b/src/cmd/link/internal/ld/execarchive.go
@@ -0,0 +1,38 @@
+// Copyright 2019 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.
+
+//go:build !wasm && !windows
+// +build !wasm,!windows
+
+package ld
+
+import (
+ "os"
+ "os/exec"
+ "path/filepath"
+ "syscall"
+)
+
+const syscallExecSupported = true
+
+// execArchive invokes the archiver tool with syscall.Exec(), with
+// the expectation that this is the last thing that takes place
+// in the linking operation.
+func (ctxt *Link) execArchive(argv []string) {
+ var err error
+ argv0 := argv[0]
+ if filepath.Base(argv0) == argv0 {
+ argv0, err = exec.LookPath(argv0)
+ if err != nil {
+ Exitf("cannot find %s: %v", argv[0], err)
+ }
+ }
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("invoking archiver with syscall.Exec()\n")
+ }
+ err = syscall.Exec(argv0, argv, os.Environ())
+ if err != nil {
+ Exitf("running %s failed: %v", argv[0], err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/execarchive_noexec.go b/src/cmd/link/internal/ld/execarchive_noexec.go
new file mode 100644
index 0000000..5e1f266
--- /dev/null
+++ b/src/cmd/link/internal/ld/execarchive_noexec.go
@@ -0,0 +1,14 @@
+// Copyright 2019 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.
+
+//go:build wasm || windows
+// +build wasm windows
+
+package ld
+
+const syscallExecSupported = false
+
+func (ctxt *Link) execArchive(argv []string) {
+ panic("should never arrive here")
+}
diff --git a/src/cmd/link/internal/ld/fallocate_test.go b/src/cmd/link/internal/ld/fallocate_test.go
new file mode 100644
index 0000000..1ed0eb2
--- /dev/null
+++ b/src/cmd/link/internal/ld/fallocate_test.go
@@ -0,0 +1,65 @@
+// Copyright 2020 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.
+
+//go:build darwin || linux
+// +build darwin linux
+
+package ld
+
+import (
+ "os"
+ "path/filepath"
+ "syscall"
+ "testing"
+)
+
+func TestFallocate(t *testing.T) {
+ dir := t.TempDir()
+ filename := filepath.Join(dir, "a.out")
+ out := NewOutBuf(nil)
+ err := out.Open(filename)
+ if err != nil {
+ t.Fatalf("Open file failed: %v", err)
+ }
+ defer out.Close()
+
+ // Try fallocate first.
+ for {
+ err = out.fallocate(1 << 10)
+ if err == syscall.EOPNOTSUPP { // The underlying file system may not support fallocate
+ t.Skip("fallocate is not supported")
+ }
+ if err == syscall.EINTR {
+ continue // try again
+ }
+ if err != nil {
+ t.Fatalf("fallocate failed: %v", err)
+ }
+ break
+ }
+
+ // Mmap 1 MiB initially, and grow to 2 and 3 MiB.
+ // Check if the file size and disk usage is expected.
+ for _, sz := range []int64{1 << 20, 2 << 20, 3 << 20} {
+ err = out.Mmap(uint64(sz))
+ if err != nil {
+ t.Fatalf("Mmap failed: %v", err)
+ }
+ stat, err := os.Stat(filename)
+ if err != nil {
+ t.Fatalf("Stat failed: %v", err)
+ }
+ if got := stat.Size(); got != sz {
+ t.Errorf("unexpected file size: got %d, want %d", got, sz)
+ }
+ // The number of blocks must be enough for the requested size.
+ // We used to require an exact match, but it appears that
+ // some file systems allocate a few extra blocks in some cases.
+ // See issue #41127.
+ if got, want := stat.Sys().(*syscall.Stat_t).Blocks, (sz+511)/512; got < want {
+ t.Errorf("unexpected disk usage: got %d blocks, want at least %d", got, want)
+ }
+ out.munmap()
+ }
+}
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
new file mode 100644
index 0000000..9dfb0f7
--- /dev/null
+++ b/src/cmd/link/internal/ld/go.go
@@ -0,0 +1,499 @@
+// Copyright 2009 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.
+
+// go-specific code shared across loaders (5l, 6l, 8l).
+
+package ld
+
+import (
+ "cmd/internal/bio"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// go-specific code shared across loaders (5l, 6l, 8l).
+
+// TODO:
+// generate debugging section in binary.
+// once the dust settles, try to move some code to
+// libmach, so that other linkers and ar can share.
+
+func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename string) {
+ if *flagG {
+ return
+ }
+
+ if int64(int(length)) != length {
+ fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
+ return
+ }
+
+ bdata := make([]byte, length)
+ if _, err := io.ReadFull(f, bdata); err != nil {
+ fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
+ return
+ }
+ data := string(bdata)
+
+ // process header lines
+ for data != "" {
+ var line string
+ if i := strings.Index(data, "\n"); i >= 0 {
+ line, data = data[:i], data[i+1:]
+ } else {
+ line, data = data, ""
+ }
+ if line == "main" {
+ lib.Main = true
+ }
+ if line == "" {
+ break
+ }
+ }
+
+ // look for cgo section
+ p0 := strings.Index(data, "\n$$ // cgo")
+ var p1 int
+ if p0 >= 0 {
+ p0 += p1
+ i := strings.IndexByte(data[p0+1:], '\n')
+ if i < 0 {
+ fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
+ return
+ }
+ p0 += 1 + i
+
+ p1 = strings.Index(data[p0:], "\n$$")
+ if p1 < 0 {
+ p1 = strings.Index(data[p0:], "\n!\n")
+ }
+ if p1 < 0 {
+ fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
+ return
+ }
+ p1 += p0
+ loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
+ }
+}
+
+func loadcgo(ctxt *Link, file string, pkg string, p string) {
+ var directives [][]string
+ if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
+ fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
+ nerrors++
+ return
+ }
+
+ // Record the directives. We'll process them later after Symbols are created.
+ ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
+}
+
+// Set symbol attributes or flags based on cgo directives.
+// Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'.
+func setCgoAttr(ctxt *Link, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) {
+ l := ctxt.loader
+ for _, f := range directives {
+ switch f[0] {
+ case "cgo_import_dynamic":
+ if len(f) < 2 || len(f) > 4 {
+ break
+ }
+
+ local := f[1]
+ remote := local
+ if len(f) > 2 {
+ remote = f[2]
+ }
+ lib := ""
+ if len(f) > 3 {
+ lib = f[3]
+ }
+
+ if *FlagD {
+ fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
+ nerrors++
+ return
+ }
+
+ if local == "_" && remote == "_" {
+ // allow #pragma dynimport _ _ "foo.so"
+ // to force a link of foo.so.
+ havedynamic = 1
+
+ if ctxt.HeadType == objabi.Hdarwin {
+ machoadddynlib(lib, ctxt.LinkMode)
+ } else {
+ dynlib = append(dynlib, lib)
+ }
+ continue
+ }
+
+ q := ""
+ if i := strings.Index(remote, "#"); i >= 0 {
+ remote, q = remote[:i], remote[i+1:]
+ }
+ s := l.LookupOrCreateSym(local, 0)
+ st := l.SymType(s)
+ if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ {
+ l.SetSymDynimplib(s, lib)
+ l.SetSymExtname(s, remote)
+ l.SetSymDynimpvers(s, q)
+ if st != sym.SHOSTOBJ {
+ su := l.MakeSymbolUpdater(s)
+ su.SetType(sym.SDYNIMPORT)
+ } else {
+ hostObjSyms[s] = struct{}{}
+ }
+ havedynamic = 1
+ if lib != "" && ctxt.IsDarwin() {
+ machoadddynlib(lib, ctxt.LinkMode)
+ }
+ }
+
+ continue
+
+ case "cgo_import_static":
+ if len(f) != 2 {
+ break
+ }
+ local := f[1]
+
+ s := l.LookupOrCreateSym(local, 0)
+ su := l.MakeSymbolUpdater(s)
+ su.SetType(sym.SHOSTOBJ)
+ su.SetSize(0)
+ hostObjSyms[s] = struct{}{}
+ continue
+
+ case "cgo_export_static", "cgo_export_dynamic":
+ if len(f) < 2 || len(f) > 4 {
+ break
+ }
+ local := f[1]
+ remote := local
+ if len(f) > 2 {
+ remote = f[2]
+ }
+ // The compiler adds a fourth argument giving
+ // the definition ABI of function symbols.
+ abi := obj.ABI0
+ if len(f) > 3 {
+ var ok bool
+ abi, ok = obj.ParseABI(f[3])
+ if !ok {
+ fmt.Fprintf(os.Stderr, "%s: bad ABI in cgo_export directive %s\n", os.Args[0], f)
+ nerrors++
+ return
+ }
+ }
+
+ s := l.LookupOrCreateSym(local, sym.ABIToVersion(abi))
+
+ if l.SymType(s) == sym.SHOSTOBJ {
+ hostObjSyms[s] = struct{}{}
+ }
+
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
+ if s == l.Lookup("main", 0) {
+ continue
+ }
+ }
+
+ // export overrides import, for openbsd/cgo.
+ // see issue 4878.
+ if l.SymDynimplib(s) != "" {
+ l.SetSymDynimplib(s, "")
+ l.SetSymDynimpvers(s, "")
+ l.SetSymExtname(s, "")
+ var su *loader.SymbolBuilder
+ su = l.MakeSymbolUpdater(s)
+ su.SetType(0)
+ }
+
+ if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) {
+ l.SetSymExtname(s, remote)
+ } else if l.SymExtname(s) != remote {
+ fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote)
+ nerrors++
+ return
+ }
+
+ // Mark exported symbols and also add them to
+ // the lists used for roots in the deadcode pass.
+ if f[0] == "cgo_export_static" {
+ if ctxt.LinkMode == LinkExternal && !l.AttrCgoExportStatic(s) {
+ // Static cgo exports appear
+ // in the exported symbol table.
+ ctxt.dynexp = append(ctxt.dynexp, s)
+ }
+ if ctxt.LinkMode == LinkInternal {
+ // For internal linking, we're
+ // responsible for resolving
+ // relocations from host objects.
+ // Record the right Go symbol
+ // version to use.
+ l.AddCgoExport(s)
+ }
+ l.SetAttrCgoExportStatic(s, true)
+ } else {
+ if ctxt.LinkMode == LinkInternal && !l.AttrCgoExportDynamic(s) {
+ // Dynamic cgo exports appear
+ // in the exported symbol table.
+ ctxt.dynexp = append(ctxt.dynexp, s)
+ }
+ l.SetAttrCgoExportDynamic(s, true)
+ }
+
+ continue
+
+ case "cgo_dynamic_linker":
+ if len(f) != 2 {
+ break
+ }
+
+ if *flagInterpreter == "" {
+ if interpreter != "" && interpreter != f[1] {
+ fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
+ nerrors++
+ return
+ }
+
+ interpreter = f[1]
+ }
+ continue
+
+ case "cgo_ldflag":
+ if len(f) != 2 {
+ break
+ }
+ ldflag = append(ldflag, f[1])
+ continue
+ }
+
+ fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
+ nerrors++
+ }
+ return
+}
+
+// openbsdTrimLibVersion indicates whether a shared library is
+// versioned and if it is, returns the unversioned name. The
+// OpenBSD library naming scheme is lib<name>.so.<major>.<minor>
+func openbsdTrimLibVersion(lib string) (string, bool) {
+ parts := strings.Split(lib, ".")
+ if len(parts) != 4 {
+ return "", false
+ }
+ if parts[1] != "so" {
+ return "", false
+ }
+ if _, err := strconv.Atoi(parts[2]); err != nil {
+ return "", false
+ }
+ if _, err := strconv.Atoi(parts[3]); err != nil {
+ return "", false
+ }
+ return fmt.Sprintf("%s.%s", parts[0], parts[1]), true
+}
+
+// dedupLibrariesOpenBSD dedups a list of shared libraries, treating versioned
+// and unversioned libraries as equivalents. Versioned libraries are preferred
+// and retained over unversioned libraries. This avoids the situation where
+// the use of cgo results in a DT_NEEDED for a versioned library (for example,
+// libc.so.96.1), while a dynamic import specifies an unversioned library (for
+// example, libc.so) - this would otherwise result in two DT_NEEDED entries
+// for the same library, resulting in a failure when ld.so attempts to load
+// the Go binary.
+func dedupLibrariesOpenBSD(ctxt *Link, libs []string) []string {
+ libraries := make(map[string]string)
+ for _, lib := range libs {
+ if name, ok := openbsdTrimLibVersion(lib); ok {
+ // Record unversioned name as seen.
+ seenlib[name] = true
+ libraries[name] = lib
+ } else if _, ok := libraries[lib]; !ok {
+ libraries[lib] = lib
+ }
+ }
+
+ libs = nil
+ for _, lib := range libraries {
+ libs = append(libs, lib)
+ }
+ sort.Strings(libs)
+
+ return libs
+}
+
+func dedupLibraries(ctxt *Link, libs []string) []string {
+ if ctxt.Target.IsOpenbsd() {
+ return dedupLibrariesOpenBSD(ctxt, libs)
+ }
+ return libs
+}
+
+var seenlib = make(map[string]bool)
+
+func adddynlib(ctxt *Link, lib string) {
+ if seenlib[lib] || ctxt.LinkMode == LinkExternal {
+ return
+ }
+ seenlib[lib] = true
+
+ if ctxt.IsELF {
+ dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr)
+ if dsu.Size() == 0 {
+ dsu.Addstring("")
+ }
+ du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic)
+ Elfwritedynent(ctxt.Arch, du, elf.DT_NEEDED, uint64(dsu.Addstring(lib)))
+ } else {
+ Errorf(nil, "adddynlib: unsupported binary format")
+ }
+}
+
+func Adddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
+ if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal {
+ return
+ }
+
+ if target.IsELF {
+ elfadddynsym(ldr, target, syms, s)
+ } else if target.HeadType == objabi.Hdarwin {
+ ldr.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s))
+ } else if target.HeadType == objabi.Hwindows {
+ // already taken care of
+ } else {
+ ldr.Errorf(s, "adddynsym: unsupported binary format")
+ }
+}
+
+func fieldtrack(arch *sys.Arch, l *loader.Loader) {
+ var buf strings.Builder
+ for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ {
+ if name := l.SymName(i); strings.HasPrefix(name, "go:track.") {
+ if l.AttrReachable(i) {
+ l.SetAttrSpecial(i, true)
+ l.SetAttrNotInSymbolTable(i, true)
+ buf.WriteString(name[9:])
+ for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] {
+ buf.WriteString("\t")
+ buf.WriteString(l.SymName(p))
+ }
+ buf.WriteString("\n")
+ }
+ }
+ }
+ l.Reachparent = nil // we are done with it
+ if *flagFieldTrack == "" {
+ return
+ }
+ s := l.Lookup(*flagFieldTrack, 0)
+ if s == 0 || !l.AttrReachable(s) {
+ return
+ }
+ bld := l.MakeSymbolUpdater(s)
+ bld.SetType(sym.SDATA)
+ addstrdata(arch, l, *flagFieldTrack, buf.String())
+}
+
+func (ctxt *Link) addexport() {
+ // Track undefined external symbols during external link.
+ if ctxt.LinkMode == LinkExternal {
+ for _, s := range ctxt.Textp {
+ if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) {
+ continue
+ }
+ relocs := ctxt.loader.Relocs(s)
+ for i := 0; i < relocs.Count(); i++ {
+ if rs := relocs.At(i).Sym(); rs != 0 {
+ if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) {
+ // sanity check
+ if len(ctxt.loader.Data(rs)) != 0 {
+ panic("expected no data on undef symbol")
+ }
+ su := ctxt.loader.MakeSymbolUpdater(rs)
+ su.SetType(sym.SUNDEFEXT)
+ }
+ }
+ }
+ }
+ }
+
+ // TODO(aix)
+ if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
+ return
+ }
+
+ // Add dynamic symbols.
+ for _, s := range ctxt.dynexp {
+ // Consistency check.
+ if !ctxt.loader.AttrReachable(s) {
+ panic("dynexp entry not reachable")
+ }
+
+ Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, s)
+ }
+
+ for _, lib := range dedupLibraries(ctxt, dynlib) {
+ adddynlib(ctxt, lib)
+ }
+}
+
+type Pkg struct {
+ mark bool
+ checked bool
+ path string
+ impby []*Pkg
+}
+
+var pkgall []*Pkg
+
+func (p *Pkg) cycle() *Pkg {
+ if p.checked {
+ return nil
+ }
+
+ if p.mark {
+ nerrors++
+ fmt.Printf("import cycle:\n")
+ fmt.Printf("\t%s\n", p.path)
+ return p
+ }
+
+ p.mark = true
+ for _, q := range p.impby {
+ if bad := q.cycle(); bad != nil {
+ p.mark = false
+ p.checked = true
+ fmt.Printf("\timports %s\n", p.path)
+ if bad == p {
+ return nil
+ }
+ return bad
+ }
+ }
+
+ p.checked = true
+ p.mark = false
+ return nil
+}
+
+func importcycles() {
+ for _, p := range pkgall {
+ p.cycle()
+ }
+}
diff --git a/src/cmd/link/internal/ld/go_test.go b/src/cmd/link/internal/ld/go_test.go
new file mode 100644
index 0000000..836731a
--- /dev/null
+++ b/src/cmd/link/internal/ld/go_test.go
@@ -0,0 +1,116 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+ "internal/testenv"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "testing"
+
+ "cmd/internal/objabi"
+)
+
+func TestDedupLibraries(t *testing.T) {
+ ctxt := &Link{}
+ ctxt.Target.HeadType = objabi.Hlinux
+
+ libs := []string{"libc.so", "libc.so.6"}
+
+ got := dedupLibraries(ctxt, libs)
+ if !reflect.DeepEqual(got, libs) {
+ t.Errorf("dedupLibraries(%v) = %v, want %v", libs, got, libs)
+ }
+}
+
+func TestDedupLibrariesOpenBSD(t *testing.T) {
+ ctxt := &Link{}
+ ctxt.Target.HeadType = objabi.Hopenbsd
+
+ tests := []struct {
+ libs []string
+ want []string
+ }{
+ {
+ libs: []string{"libc.so"},
+ want: []string{"libc.so"},
+ },
+ {
+ libs: []string{"libc.so", "libc.so.96.1"},
+ want: []string{"libc.so.96.1"},
+ },
+ {
+ libs: []string{"libc.so.96.1", "libc.so"},
+ want: []string{"libc.so.96.1"},
+ },
+ {
+ libs: []string{"libc.a", "libc.so.96.1"},
+ want: []string{"libc.a", "libc.so.96.1"},
+ },
+ {
+ libs: []string{"libpthread.so", "libc.so"},
+ want: []string{"libc.so", "libpthread.so"},
+ },
+ {
+ libs: []string{"libpthread.so.26.1", "libpthread.so", "libc.so.96.1", "libc.so"},
+ want: []string{"libc.so.96.1", "libpthread.so.26.1"},
+ },
+ {
+ libs: []string{"libpthread.so.26.1", "libpthread.so", "libc.so.96.1", "libc.so", "libfoo.so"},
+ want: []string{"libc.so.96.1", "libfoo.so", "libpthread.so.26.1"},
+ },
+ }
+
+ for _, test := range tests {
+ t.Run("dedup", func(t *testing.T) {
+ got := dedupLibraries(ctxt, test.libs)
+ if !reflect.DeepEqual(got, test.want) {
+ t.Errorf("dedupLibraries(%v) = %v, want %v", test.libs, got, test.want)
+ }
+ })
+ }
+}
+
+func TestDedupLibrariesOpenBSDLink(t *testing.T) {
+ // The behavior we're checking for is of interest only on OpenBSD.
+ if runtime.GOOS != "openbsd" {
+ t.Skip("test only useful on openbsd")
+ }
+
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ // cgo_import_dynamic both the unversioned libraries and pull in the
+ // net package to get a cgo package with a versioned library.
+ srcFile := filepath.Join(dir, "x.go")
+ src := `package main
+
+import (
+ _ "net"
+)
+
+//go:cgo_import_dynamic _ _ "libc.so"
+
+func main() {}`
+ if err := os.WriteFile(srcFile, []byte(src), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ exe := filepath.Join(dir, "deduped.exe")
+ out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, srcFile).CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failure: %s\n%s\n", err, string(out))
+ }
+
+ // Result should be runnable.
+ if _, err = testenv.Command(t, exe).CombinedOutput(); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/heap.go b/src/cmd/link/internal/ld/heap.go
new file mode 100644
index 0000000..ea2d772
--- /dev/null
+++ b/src/cmd/link/internal/ld/heap.go
@@ -0,0 +1,54 @@
+// Copyright 2020 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.
+
+package ld
+
+import "cmd/link/internal/loader"
+
+// Min-heap implementation, for the deadcode pass.
+// Specialized for loader.Sym elements.
+
+type heap []loader.Sym
+
+func (h *heap) push(s loader.Sym) {
+ *h = append(*h, s)
+ // sift up
+ n := len(*h) - 1
+ for n > 0 {
+ p := (n - 1) / 2 // parent
+ if (*h)[p] <= (*h)[n] {
+ break
+ }
+ (*h)[n], (*h)[p] = (*h)[p], (*h)[n]
+ n = p
+ }
+}
+
+func (h *heap) pop() loader.Sym {
+ r := (*h)[0]
+ n := len(*h) - 1
+ (*h)[0] = (*h)[n]
+ *h = (*h)[:n]
+
+ // sift down
+ i := 0
+ for {
+ c := 2*i + 1 // left child
+ if c >= n {
+ break
+ }
+ if c1 := c + 1; c1 < n && (*h)[c1] < (*h)[c] {
+ c = c1 // right child
+ }
+ if (*h)[i] <= (*h)[c] {
+ break
+ }
+ (*h)[i], (*h)[c] = (*h)[c], (*h)[i]
+ i = c
+ }
+
+ return r
+}
+
+func (h *heap) empty() bool { return len(*h) == 0 }
diff --git a/src/cmd/link/internal/ld/heap_test.go b/src/cmd/link/internal/ld/heap_test.go
new file mode 100644
index 0000000..08c9030
--- /dev/null
+++ b/src/cmd/link/internal/ld/heap_test.go
@@ -0,0 +1,90 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+ "cmd/link/internal/loader"
+ "testing"
+)
+
+func TestHeap(t *testing.T) {
+ tests := [][]loader.Sym{
+ {10, 20, 30, 40, 50, 60, 70, 80, 90, 100},
+ {100, 90, 80, 70, 60, 50, 40, 30, 20, 10},
+ {30, 50, 80, 20, 60, 70, 10, 100, 90, 40},
+ }
+ for _, s := range tests {
+ h := heap{}
+ for _, i := range s {
+ h.push(i)
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ }
+ for j := 0; j < len(s); j++ {
+ x := h.pop()
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ // pop should return elements in ascending order.
+ if want := loader.Sym((j + 1) * 10); x != want {
+ t.Errorf("pop returns wrong element: want %d, got %d", want, x)
+ }
+ }
+ if !h.empty() {
+ t.Errorf("heap is not empty after all pops")
+ }
+ }
+
+ // Also check that mixed pushes and pops work correctly.
+ for _, s := range tests {
+ h := heap{}
+ for i := 0; i < len(s)/2; i++ {
+ // two pushes, one pop
+ h.push(s[2*i])
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ h.push(s[2*i+1])
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ h.pop()
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ }
+ for !h.empty() { // pop remaining elements
+ h.pop()
+ if !verify(&h, 0) {
+ t.Errorf("heap invariant violated: %v", h)
+ }
+ }
+ }
+}
+
+// recursively verify heap-ness, starting at element i.
+func verify(h *heap, i int) bool {
+ n := len(*h)
+ c1 := 2*i + 1 // left child
+ c2 := 2*i + 2 // right child
+ if c1 < n {
+ if (*h)[c1] < (*h)[i] {
+ return false
+ }
+ if !verify(h, c1) {
+ return false
+ }
+ }
+ if c2 < n {
+ if (*h)[c2] < (*h)[i] {
+ return false
+ }
+ if !verify(h, c2) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/cmd/link/internal/ld/issue33808_test.go b/src/cmd/link/internal/ld/issue33808_test.go
new file mode 100644
index 0000000..43f4540
--- /dev/null
+++ b/src/cmd/link/internal/ld/issue33808_test.go
@@ -0,0 +1,49 @@
+// Copyright 2019 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.
+
+package ld
+
+import (
+ "internal/testenv"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+const prog = `
+package main
+
+import "log"
+
+func main() {
+ log.Fatalf("HERE")
+}
+`
+
+func TestIssue33808(t *testing.T) {
+ if runtime.GOOS != "darwin" {
+ return
+ }
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+
+ dir := t.TempDir()
+
+ f := gobuild(t, dir, prog, "-ldflags=-linkmode=external")
+ f.Close()
+
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Fatalf("Error reading symbols: %v", err)
+ }
+
+ name := "log.Fatalf"
+ for _, sym := range syms {
+ if strings.Contains(sym.Name, name) {
+ return
+ }
+ }
+ t.Fatalf("Didn't find %v", name)
+}
diff --git a/src/cmd/link/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go
new file mode 100644
index 0000000..d416571
--- /dev/null
+++ b/src/cmd/link/internal/ld/ld.go
@@ -0,0 +1,256 @@
+// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "cmd/internal/goobj"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+)
+
+func (ctxt *Link) readImportCfg(file string) {
+ ctxt.PackageFile = make(map[string]string)
+ ctxt.PackageShlib = make(map[string]string)
+ data, err := os.ReadFile(file)
+ if err != nil {
+ log.Fatalf("-importcfg: %v", err)
+ }
+
+ for lineNum, line := range strings.Split(string(data), "\n") {
+ lineNum++ // 1-based
+ line = strings.TrimSpace(line)
+ if line == "" {
+ continue
+ }
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+
+ var verb, args string
+ if i := strings.Index(line, " "); i < 0 {
+ verb = line
+ } else {
+ verb, args = line[:i], strings.TrimSpace(line[i+1:])
+ }
+ var before, after string
+ if i := strings.Index(args, "="); i >= 0 {
+ before, after = args[:i], args[i+1:]
+ }
+ switch verb {
+ default:
+ log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
+ case "packagefile":
+ if before == "" || after == "" {
+ log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
+ }
+ ctxt.PackageFile[before] = after
+ case "packageshlib":
+ if before == "" || after == "" {
+ log.Fatalf(`%s:%d: invalid packageshlib: syntax is "packageshlib path=filename"`, file, lineNum)
+ }
+ ctxt.PackageShlib[before] = after
+ case "modinfo":
+ s, err := strconv.Unquote(args)
+ if err != nil {
+ log.Fatalf("%s:%d: invalid modinfo: %v", file, lineNum, err)
+ }
+ addstrdata1(ctxt, "runtime.modinfo="+s)
+ }
+ }
+}
+
+func pkgname(ctxt *Link, lib string) string {
+ return path.Clean(lib)
+}
+
+func findlib(ctxt *Link, lib string) (string, bool) {
+ name := path.Clean(lib)
+
+ var pname string
+ isshlib := false
+
+ if ctxt.linkShared && ctxt.PackageShlib[name] != "" {
+ pname = ctxt.PackageShlib[name]
+ isshlib = true
+ } else if ctxt.PackageFile != nil {
+ pname = ctxt.PackageFile[name]
+ if pname == "" {
+ ctxt.Logf("cannot find package %s (using -importcfg)\n", name)
+ return "", false
+ }
+ } else {
+ pkg := pkgname(ctxt, lib)
+
+ // search -L "libdir" directories
+ for _, dir := range ctxt.Libdir {
+ if ctxt.linkShared {
+ pname = filepath.Join(dir, pkg+".shlibname")
+ if _, err := os.Stat(pname); err == nil {
+ isshlib = true
+ break
+ }
+ }
+ pname = filepath.Join(dir, name+".a")
+ if _, err := os.Stat(pname); err == nil {
+ break
+ }
+ pname = filepath.Join(dir, name+".o")
+ if _, err := os.Stat(pname); err == nil {
+ break
+ }
+ }
+ pname = filepath.Clean(pname)
+ }
+
+ return pname, isshlib
+}
+
+func addlib(ctxt *Link, src, obj, lib string, fingerprint goobj.FingerprintType) *sym.Library {
+ pkg := pkgname(ctxt, lib)
+
+ // already loaded?
+ if l := ctxt.LibraryByPkg[pkg]; l != nil && !l.Fingerprint.IsZero() {
+ // Normally, packages are loaded in dependency order, and if l != nil
+ // l is already loaded with the actual fingerprint. In shared build mode,
+ // however, packages may be added not in dependency order, and it is
+ // possible that l's fingerprint is not yet loaded -- exclude it in
+ // checking.
+ checkFingerprint(l, l.Fingerprint, src, fingerprint)
+ return l
+ }
+
+ pname, isshlib := findlib(ctxt, lib)
+
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("addlib: %s %s pulls in %s isshlib %v\n", obj, src, pname, isshlib)
+ }
+
+ if isshlib {
+ return addlibpath(ctxt, src, obj, "", pkg, pname, fingerprint)
+ }
+ return addlibpath(ctxt, src, obj, pname, pkg, "", fingerprint)
+}
+
+/*
+ * add library to library list, return added library.
+ * srcref: src file referring to package
+ * objref: object file referring to package
+ * file: object file, e.g., /home/rsc/go/pkg/container/vector.a
+ * pkg: package import path, e.g. container/vector
+ * shlib: path to shared library, or .shlibname file holding path
+ * fingerprint: if not 0, expected fingerprint for import from srcref
+ * fingerprint is 0 if the library is not imported (e.g. main)
+ */
+func addlibpath(ctxt *Link, srcref, objref, file, pkg, shlib string, fingerprint goobj.FingerprintType) *sym.Library {
+ if l := ctxt.LibraryByPkg[pkg]; l != nil {
+ return l
+ }
+
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("addlibpath: srcref: %s objref: %s file: %s pkg: %s shlib: %s fingerprint: %x\n", srcref, objref, file, pkg, shlib, fingerprint)
+ }
+
+ l := &sym.Library{}
+ ctxt.LibraryByPkg[pkg] = l
+ ctxt.Library = append(ctxt.Library, l)
+ l.Objref = objref
+ l.Srcref = srcref
+ l.File = file
+ l.Pkg = pkg
+ l.Fingerprint = fingerprint
+ if shlib != "" {
+ if strings.HasSuffix(shlib, ".shlibname") {
+ data, err := os.ReadFile(shlib)
+ if err != nil {
+ Errorf(nil, "cannot read %s: %v", shlib, err)
+ }
+ shlib = strings.TrimSpace(string(data))
+ }
+ l.Shlib = shlib
+ }
+ return l
+}
+
+func atolwhex(s string) int64 {
+ n, _ := strconv.ParseInt(s, 0, 64)
+ return n
+}
+
+// PrepareAddmoduledata returns a symbol builder that target-specific
+// code can use to build up the linker-generated go.link.addmoduledata
+// function, along with the sym for runtime.addmoduledata itself. If
+// this function is not needed (for example in cases where we're
+// linking a module that contains the runtime) the returned builder
+// will be nil.
+func PrepareAddmoduledata(ctxt *Link) (*loader.SymbolBuilder, loader.Sym) {
+ if !ctxt.DynlinkingGo() {
+ return nil, 0
+ }
+ amd := ctxt.loader.LookupOrCreateSym("runtime.addmoduledata", 0)
+ if ctxt.loader.SymType(amd) == sym.STEXT && ctxt.BuildMode != BuildModePlugin {
+ // we're linking a module containing the runtime -> no need for
+ // an init function
+ return nil, 0
+ }
+ ctxt.loader.SetAttrReachable(amd, true)
+
+ // Create a new init func text symbol. Caller will populate this
+ // sym with arch-specific content.
+ ifs := ctxt.loader.LookupOrCreateSym("go:link.addmoduledata", 0)
+ initfunc := ctxt.loader.MakeSymbolUpdater(ifs)
+ ctxt.loader.SetAttrReachable(ifs, true)
+ ctxt.loader.SetAttrLocal(ifs, true)
+ initfunc.SetType(sym.STEXT)
+
+ // Add the init func and/or addmoduledata to Textp.
+ if ctxt.BuildMode == BuildModePlugin {
+ ctxt.Textp = append(ctxt.Textp, amd)
+ }
+ ctxt.Textp = append(ctxt.Textp, initfunc.Sym())
+
+ // Create an init array entry
+ amdi := ctxt.loader.LookupOrCreateSym("go:link.addmoduledatainit", 0)
+ initarray_entry := ctxt.loader.MakeSymbolUpdater(amdi)
+ ctxt.loader.SetAttrReachable(amdi, true)
+ ctxt.loader.SetAttrLocal(amdi, true)
+ initarray_entry.SetType(sym.SINITARR)
+ initarray_entry.AddAddr(ctxt.Arch, ifs)
+
+ return initfunc, amd
+}
diff --git a/src/cmd/link/internal/ld/ld_test.go b/src/cmd/link/internal/ld/ld_test.go
new file mode 100644
index 0000000..4d32dd6
--- /dev/null
+++ b/src/cmd/link/internal/ld/ld_test.go
@@ -0,0 +1,343 @@
+// 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.
+
+package ld
+
+import (
+ "bytes"
+ "debug/pe"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestUndefinedRelocErrors(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // When external linking, symbols may be defined externally, so we allow
+ // undefined symbols and let external linker resolve. Skip the test.
+ testenv.MustInternalLink(t)
+
+ t.Parallel()
+
+ out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "./testdata/issue10978").CombinedOutput()
+ if err == nil {
+ t.Fatal("expected build to fail")
+ }
+
+ wantErrors := map[string]int{
+ // Main function has dedicated error message.
+ "function main is undeclared in the main package": 1,
+
+ // Single error reporting per each symbol.
+ // This way, duplicated messages are not reported for
+ // multiple relocations with a same name.
+ "main.defined1: relocation target main.undefined not defined": 1,
+ "main.defined2: relocation target main.undefined not defined": 1,
+ }
+ unexpectedErrors := map[string]int{}
+
+ for _, l := range strings.Split(string(out), "\n") {
+ if strings.HasPrefix(l, "#") || l == "" {
+ continue
+ }
+ matched := ""
+ for want := range wantErrors {
+ if strings.Contains(l, want) {
+ matched = want
+ break
+ }
+ }
+ if matched != "" {
+ wantErrors[matched]--
+ } else {
+ unexpectedErrors[l]++
+ }
+ }
+
+ for want, n := range wantErrors {
+ switch {
+ case n > 0:
+ t.Errorf("unmatched error: %s (x%d)", want, n)
+ case n < 0:
+ t.Errorf("extra errors: %s (x%d)", want, -n)
+ }
+ }
+ for unexpected, n := range unexpectedErrors {
+ t.Errorf("unexpected error: %s (x%d)", unexpected, n)
+ }
+}
+
+const carchiveSrcText = `
+package main
+
+//export GoFunc
+func GoFunc() {
+ println(42)
+}
+
+func main() {
+}
+`
+
+func TestArchiveBuildInvokeWithExec(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ // run this test on just a small set of platforms (no need to test it
+ // across the board given the nature of the test).
+ pair := runtime.GOOS + "-" + runtime.GOARCH
+ switch pair {
+ case "darwin-amd64", "darwin-arm64", "linux-amd64", "freebsd-amd64":
+ default:
+ t.Skip("no need for test on " + pair)
+ }
+ switch runtime.GOOS {
+ case "openbsd", "windows":
+ t.Skip("c-archive unsupported")
+ }
+ dir := t.TempDir()
+
+ srcfile := filepath.Join(dir, "test.go")
+ arfile := filepath.Join(dir, "test.a")
+ if err := os.WriteFile(srcfile, []byte(carchiveSrcText), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ ldf := fmt.Sprintf("-ldflags=-v -tmpdir=%s", dir)
+ argv := []string{"build", "-buildmode=c-archive", "-o", arfile, ldf, srcfile}
+ out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failure: %s\n%s\n", err, string(out))
+ }
+
+ found := false
+ const want = "invoking archiver with syscall.Exec"
+ for _, l := range strings.Split(string(out), "\n") {
+ if strings.HasPrefix(l, want) {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Errorf("expected '%s' in -v output, got:\n%s\n", want, string(out))
+ }
+}
+
+func TestLargeTextSectionSplitting(t *testing.T) {
+ switch runtime.GOARCH {
+ case "ppc64", "ppc64le", "arm":
+ case "arm64":
+ if runtime.GOOS == "darwin" {
+ break
+ }
+ fallthrough
+ default:
+ t.Skipf("text section splitting is not done in %s/%s", runtime.GOOS, runtime.GOARCH)
+ }
+
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+ dir := t.TempDir()
+
+ // NB: the use of -ldflags=-debugtextsize=1048576 tells the linker to
+ // split text sections at a size threshold of 1M instead of the
+ // architected limit of 67M or larger. The choice of building cmd/go
+ // is arbitrary; we just need something sufficiently large that uses
+ // external linking.
+ exe := filepath.Join(dir, "go.exe")
+ out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, "-ldflags=-linkmode=external -debugtextsize=1048576", "cmd/go").CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failure: %s\n%s\n", err, string(out))
+ }
+
+ // Check that we did split text sections.
+ out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
+ if err != nil {
+ t.Fatalf("nm failure: %s\n%s\n", err, string(out))
+ }
+ if !bytes.Contains(out, []byte("runtime.text.1")) {
+ t.Errorf("runtime.text.1 not found, text section not split?")
+ }
+
+ // Result should be runnable.
+ _, err = testenv.Command(t, exe, "version").CombinedOutput()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestWindowsBuildmodeCSharedASLR(t *testing.T) {
+ platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
+ switch platform {
+ case "windows/amd64", "windows/386":
+ default:
+ t.Skip("skipping windows amd64/386 only test")
+ }
+
+ testenv.MustHaveCGO(t)
+
+ t.Run("aslr", func(t *testing.T) {
+ testWindowsBuildmodeCSharedASLR(t, true)
+ })
+ t.Run("no-aslr", func(t *testing.T) {
+ testWindowsBuildmodeCSharedASLR(t, false)
+ })
+}
+
+func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+
+ dir := t.TempDir()
+
+ srcfile := filepath.Join(dir, "test.go")
+ objfile := filepath.Join(dir, "test.dll")
+ if err := os.WriteFile(srcfile, []byte(`package main; func main() { print("hello") }`), 0666); err != nil {
+ t.Fatal(err)
+ }
+ argv := []string{"build", "-buildmode=c-shared"}
+ if !useASLR {
+ argv = append(argv, "-ldflags", "-aslr=false")
+ }
+ argv = append(argv, "-o", objfile, srcfile)
+ out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failure: %s\n%s\n", err, string(out))
+ }
+
+ f, err := pe.Open(objfile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ var dc uint16
+ switch oh := f.OptionalHeader.(type) {
+ case *pe.OptionalHeader32:
+ dc = oh.DllCharacteristics
+ case *pe.OptionalHeader64:
+ dc = oh.DllCharacteristics
+ hasHEVA := (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) != 0
+ if useASLR && !hasHEVA {
+ t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
+ } else if !useASLR && hasHEVA {
+ t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag should not be set")
+ }
+ default:
+ t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
+ }
+ hasASLR := (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0
+ if useASLR && !hasASLR {
+ t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
+ } else if !useASLR && hasASLR {
+ t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
+ }
+}
+
+// TestMemProfileCheck tests that cmd/link sets
+// runtime.disableMemoryProfiling if the runtime.MemProfile
+// symbol is unreachable after deadcode (and not dynlinking).
+// The runtime then uses that to set the default value of
+// runtime.MemProfileRate, which this test checks.
+func TestMemProfileCheck(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ prog string
+ wantOut string
+ }{
+ {
+ "no_memprofile",
+ `
+package main
+import "runtime"
+func main() {
+ println(runtime.MemProfileRate)
+}
+`,
+ "0",
+ },
+ {
+ "with_memprofile",
+ `
+package main
+import "runtime"
+func main() {
+ runtime.MemProfile(nil, false)
+ println(runtime.MemProfileRate)
+}
+`,
+ "524288",
+ },
+ {
+ "with_memprofile_indirect",
+ `
+package main
+import "runtime"
+var f = runtime.MemProfile
+func main() {
+ if f == nil {
+ panic("no f")
+ }
+ println(runtime.MemProfileRate)
+}
+`,
+ "524288",
+ },
+ {
+ "with_memprofile_runtime_pprof",
+ `
+package main
+import "runtime"
+import "runtime/pprof"
+func main() {
+ _ = pprof.Profiles()
+ println(runtime.MemProfileRate)
+}
+`,
+ "524288",
+ },
+ {
+ "with_memprofile_http_pprof",
+ `
+package main
+import "runtime"
+import _ "net/http/pprof"
+func main() {
+ println(runtime.MemProfileRate)
+}
+`,
+ "524288",
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ tempDir := t.TempDir()
+ src := filepath.Join(tempDir, "x.go")
+ if err := os.WriteFile(src, []byte(tt.prog), 0644); err != nil {
+ t.Fatal(err)
+ }
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatal(err)
+ }
+ got := strings.TrimSpace(string(out))
+ if got != tt.wantOut {
+ t.Errorf("got %q; want %q", got, tt.wantOut)
+ }
+ })
+ }
+}
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
new file mode 100644
index 0000000..6eae900
--- /dev/null
+++ b/src/cmd/link/internal/ld/lib.go
@@ -0,0 +1,2696 @@
+// Inferno utils/8l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "bytes"
+ "debug/elf"
+ "debug/macho"
+ "encoding/base64"
+ "encoding/binary"
+ "fmt"
+ "internal/buildcfg"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync"
+
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/notsha256"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loadelf"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/loadmacho"
+ "cmd/link/internal/loadpe"
+ "cmd/link/internal/loadxcoff"
+ "cmd/link/internal/sym"
+)
+
+// Data layout and relocation.
+
+// Derived from Inferno utils/6l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// ArchSyms holds a number of architecture specific symbols used during
+// relocation. Rather than allowing them universal access to all symbols,
+// we keep a subset for relocation application.
+type ArchSyms struct {
+ Rel loader.Sym
+ Rela loader.Sym
+ RelPLT loader.Sym
+ RelaPLT loader.Sym
+
+ LinkEditGOT loader.Sym
+ LinkEditPLT loader.Sym
+
+ TOC loader.Sym
+ DotTOC []loader.Sym // for each version
+
+ GOT loader.Sym
+ PLT loader.Sym
+ GOTPLT loader.Sym
+
+ Tlsg loader.Sym
+ Tlsoffset int
+
+ Dynamic loader.Sym
+ DynSym loader.Sym
+ DynStr loader.Sym
+
+ unreachableMethod loader.Sym
+}
+
+// mkArchSym is a helper for setArchSyms, to set up a special symbol.
+func (ctxt *Link) mkArchSym(name string, ver int, ls *loader.Sym) {
+ *ls = ctxt.loader.LookupOrCreateSym(name, ver)
+ ctxt.loader.SetAttrReachable(*ls, true)
+}
+
+// mkArchVecSym is similar to setArchSyms, but operates on elements within
+// a slice, where each element corresponds to some symbol version.
+func (ctxt *Link) mkArchSymVec(name string, ver int, ls []loader.Sym) {
+ ls[ver] = ctxt.loader.LookupOrCreateSym(name, ver)
+ ctxt.loader.SetAttrReachable(ls[ver], true)
+}
+
+// setArchSyms sets up the ArchSyms structure, and must be called before
+// relocations are applied.
+func (ctxt *Link) setArchSyms() {
+ ctxt.mkArchSym(".got", 0, &ctxt.GOT)
+ ctxt.mkArchSym(".plt", 0, &ctxt.PLT)
+ ctxt.mkArchSym(".got.plt", 0, &ctxt.GOTPLT)
+ ctxt.mkArchSym(".dynamic", 0, &ctxt.Dynamic)
+ ctxt.mkArchSym(".dynsym", 0, &ctxt.DynSym)
+ ctxt.mkArchSym(".dynstr", 0, &ctxt.DynStr)
+ ctxt.mkArchSym("runtime.unreachableMethod", abiInternalVer, &ctxt.unreachableMethod)
+
+ if ctxt.IsPPC64() {
+ ctxt.mkArchSym("TOC", 0, &ctxt.TOC)
+
+ ctxt.DotTOC = make([]loader.Sym, ctxt.MaxVersion()+1)
+ for i := 0; i <= ctxt.MaxVersion(); i++ {
+ if i >= sym.SymVerABICount && i < sym.SymVerStatic { // these versions are not used currently
+ continue
+ }
+ ctxt.mkArchSymVec(".TOC.", i, ctxt.DotTOC)
+ }
+ }
+ if ctxt.IsElf() {
+ ctxt.mkArchSym(".rel", 0, &ctxt.Rel)
+ ctxt.mkArchSym(".rela", 0, &ctxt.Rela)
+ ctxt.mkArchSym(".rel.plt", 0, &ctxt.RelPLT)
+ ctxt.mkArchSym(".rela.plt", 0, &ctxt.RelaPLT)
+ }
+ if ctxt.IsDarwin() {
+ ctxt.mkArchSym(".linkedit.got", 0, &ctxt.LinkEditGOT)
+ ctxt.mkArchSym(".linkedit.plt", 0, &ctxt.LinkEditPLT)
+ }
+}
+
+type Arch struct {
+ Funcalign int
+ Maxalign int
+ Minalign int
+ Dwarfregsp int
+ Dwarfreglr int
+
+ // Threshold of total text size, used for trampoline insertion. If the total
+ // text size is smaller than TrampLimit, we won't need to insert trampolines.
+ // It is pretty close to the offset range of a direct CALL machine instruction.
+ // We leave some room for extra stuff like PLT stubs.
+ TrampLimit uint64
+
+ Androiddynld string
+ Linuxdynld string
+ LinuxdynldMusl string
+ Freebsddynld string
+ Netbsddynld string
+ Openbsddynld string
+ Dragonflydynld string
+ Solarisdynld string
+
+ // Empty spaces between codeblocks will be padded with this value.
+ // For example an architecture might want to pad with a trap instruction to
+ // catch wayward programs. Architectures that do not define a padding value
+ // are padded with zeros.
+ CodePad []byte
+
+ // Plan 9 variables.
+ Plan9Magic uint32
+ Plan9_64Bit bool
+
+ Adddynrel func(*Target, *loader.Loader, *ArchSyms, loader.Sym, loader.Reloc, int) bool
+ Archinit func(*Link)
+ // Archreloc is an arch-specific hook that assists in relocation processing
+ // (invoked by 'relocsym'); it handles target-specific relocation tasks.
+ // Here "rel" is the current relocation being examined, "sym" is the symbol
+ // containing the chunk of data to which the relocation applies, and "off"
+ // is the contents of the to-be-relocated data item (from sym.P). Return
+ // value is the appropriately relocated value (to be written back to the
+ // same spot in sym.P), number of external _host_ relocations needed (i.e.
+ // ELF/Mach-O/etc. relocations, not Go relocations, this must match Elfreloc1,
+ // etc.), and a boolean indicating success/failure (a failing value indicates
+ // a fatal error).
+ Archreloc func(*Target, *loader.Loader, *ArchSyms, loader.Reloc, loader.Sym,
+ int64) (relocatedOffset int64, nExtReloc int, ok bool)
+ // Archrelocvariant is a second arch-specific hook used for
+ // relocation processing; it handles relocations where r.Type is
+ // insufficient to describe the relocation (r.Variant !=
+ // sym.RV_NONE). Here "rel" is the relocation being applied, "sym"
+ // is the symbol containing the chunk of data to which the
+ // relocation applies, and "off" is the contents of the
+ // to-be-relocated data item (from sym.P). Return is an updated
+ // offset value.
+ Archrelocvariant func(target *Target, ldr *loader.Loader, rel loader.Reloc,
+ rv sym.RelocVariant, sym loader.Sym, offset int64, data []byte) (relocatedOffset int64)
+
+ // Generate a trampoline for a call from s to rs if necessary. ri is
+ // index of the relocation.
+ Trampoline func(ctxt *Link, ldr *loader.Loader, ri int, rs, s loader.Sym)
+
+ // Assembling the binary breaks into two phases, writing the code/data/
+ // dwarf information (which is rather generic), and some more architecture
+ // specific work like setting up the elf headers/dynamic relocations, etc.
+ // The phases are called "Asmb" and "Asmb2". Asmb2 needs to be defined for
+ // every architecture, but only if architecture has an Asmb function will
+ // it be used for assembly. Otherwise a generic assembly Asmb function is
+ // used.
+ Asmb func(*Link, *loader.Loader)
+ Asmb2 func(*Link, *loader.Loader)
+
+ // Extreloc is an arch-specific hook that converts a Go relocation to an
+ // external relocation. Return the external relocation and whether it is
+ // needed.
+ Extreloc func(*Target, *loader.Loader, loader.Reloc, loader.Sym) (loader.ExtReloc, bool)
+
+ Elfreloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int, int64) bool
+ ElfrelocSize uint32 // size of an ELF relocation record, must match Elfreloc1.
+ Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
+ Gentext func(*Link, *loader.Loader) // Generate text before addressing has been performed.
+ Machoreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
+ MachorelocSize uint32 // size of an Mach-O relocation record, must match Machoreloc1.
+ PEreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
+ Xcoffreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
+
+ // Generate additional symbols for the native symbol table just prior to
+ // code generation.
+ GenSymsLate func(*Link, *loader.Loader)
+
+ // TLSIEtoLE converts a TLS Initial Executable relocation to
+ // a TLS Local Executable relocation.
+ //
+ // This is possible when a TLS IE relocation refers to a local
+ // symbol in an executable, which is typical when internally
+ // linking PIE binaries.
+ TLSIEtoLE func(P []byte, off, size int)
+
+ // optional override for assignAddress
+ AssignAddress func(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64)
+}
+
+var (
+ thearch Arch
+ lcSize int32
+ rpath Rpath
+ spSize int32
+ symSize int32
+)
+
+const (
+ MINFUNC = 16 // minimum size for a function
+)
+
+// Symbol version of ABIInternal symbols. It is sym.SymVerABIInternal if ABI wrappers
+// are used, 0 otherwise.
+var abiInternalVer = sym.SymVerABIInternal
+
+// DynlinkingGo reports whether we are producing Go code that can live
+// in separate shared libraries linked together at runtime.
+func (ctxt *Link) DynlinkingGo() bool {
+ if !ctxt.Loaded {
+ panic("DynlinkingGo called before all symbols loaded")
+ }
+ return ctxt.BuildMode == BuildModeShared || ctxt.linkShared || ctxt.BuildMode == BuildModePlugin || ctxt.canUsePlugins
+}
+
+// CanUsePlugins reports whether a plugins can be used
+func (ctxt *Link) CanUsePlugins() bool {
+ if !ctxt.Loaded {
+ panic("CanUsePlugins called before all symbols loaded")
+ }
+ return ctxt.canUsePlugins
+}
+
+// NeedCodeSign reports whether we need to code-sign the output binary.
+func (ctxt *Link) NeedCodeSign() bool {
+ return ctxt.IsDarwin() && ctxt.IsARM64()
+}
+
+var (
+ dynlib []string
+ ldflag []string
+ havedynamic int
+ Funcalign int
+ iscgo bool
+ elfglobalsymndx int
+ interpreter string
+
+ debug_s bool // backup old value of debug['s']
+ HEADR int32
+
+ nerrors int
+ liveness int64 // size of liveness data (funcdata), printed if -v
+
+ // See -strictdups command line flag.
+ checkStrictDups int // 0=off 1=warning 2=error
+ strictDupMsgCount int
+)
+
+var (
+ Segtext sym.Segment
+ Segrodata sym.Segment
+ Segrelrodata sym.Segment
+ Segdata sym.Segment
+ Segdwarf sym.Segment
+
+ Segments = []*sym.Segment{&Segtext, &Segrodata, &Segrelrodata, &Segdata, &Segdwarf}
+)
+
+const pkgdef = "__.PKGDEF"
+
+var (
+ // externalobj is set to true if we see an object compiled by
+ // the host compiler that is not from a package that is known
+ // to support internal linking mode.
+ externalobj = false
+
+ // dynimportfail is a list of packages for which generating
+ // the dynimport file, _cgo_import.go, failed. If there are
+ // any of these objects, we must link externally. Issue 52863.
+ dynimportfail []string
+
+ // preferlinkext is a list of packages for which the Go command
+ // noticed use of peculiar C flags. If we see any of these,
+ // default to linking externally unless overridden by the
+ // user. See issues #58619, #58620, and #58848.
+ preferlinkext []string
+
+ // unknownObjFormat is set to true if we see an object whose
+ // format we don't recognize.
+ unknownObjFormat = false
+
+ theline string
+)
+
+func Lflag(ctxt *Link, arg string) {
+ ctxt.Libdir = append(ctxt.Libdir, arg)
+}
+
+/*
+ * Unix doesn't like it when we write to a running (or, sometimes,
+ * recently run) binary, so remove the output file before writing it.
+ * On Windows 7, remove() can force a subsequent create() to fail.
+ * S_ISREG() does not exist on Plan 9.
+ */
+func mayberemoveoutfile() {
+ if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() {
+ return
+ }
+ os.Remove(*flagOutfile)
+}
+
+func libinit(ctxt *Link) {
+ Funcalign = thearch.Funcalign
+
+ // add goroot to the end of the libdir list.
+ suffix := ""
+
+ suffixsep := ""
+ if *flagInstallSuffix != "" {
+ suffixsep = "_"
+ suffix = *flagInstallSuffix
+ } else if *flagRace {
+ suffixsep = "_"
+ suffix = "race"
+ } else if *flagMsan {
+ suffixsep = "_"
+ suffix = "msan"
+ } else if *flagAsan {
+ suffixsep = "_"
+ suffix = "asan"
+ }
+
+ if buildcfg.GOROOT != "" {
+ Lflag(ctxt, filepath.Join(buildcfg.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", buildcfg.GOOS, buildcfg.GOARCH, suffixsep, suffix)))
+ }
+
+ mayberemoveoutfile()
+
+ if err := ctxt.Out.Open(*flagOutfile); err != nil {
+ Exitf("cannot create %s: %v", *flagOutfile, err)
+ }
+
+ if *flagEntrySymbol == "" {
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModeCArchive:
+ *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", buildcfg.GOARCH, buildcfg.GOOS)
+ case BuildModeExe, BuildModePIE:
+ *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", buildcfg.GOARCH, buildcfg.GOOS)
+ case BuildModeShared, BuildModePlugin:
+ // No *flagEntrySymbol for -buildmode=shared and plugin
+ default:
+ Errorf(nil, "unknown *flagEntrySymbol for buildmode %v", ctxt.BuildMode)
+ }
+ }
+}
+
+func exitIfErrors() {
+ if nerrors != 0 || checkStrictDups > 1 && strictDupMsgCount > 0 {
+ mayberemoveoutfile()
+ Exit(2)
+ }
+
+}
+
+func errorexit() {
+ exitIfErrors()
+ Exit(0)
+}
+
+func loadinternal(ctxt *Link, name string) *sym.Library {
+ zerofp := goobj.FingerprintType{}
+ if ctxt.linkShared && ctxt.PackageShlib != nil {
+ if shlib := ctxt.PackageShlib[name]; shlib != "" {
+ return addlibpath(ctxt, "internal", "internal", "", name, shlib, zerofp)
+ }
+ }
+ if ctxt.PackageFile != nil {
+ if pname := ctxt.PackageFile[name]; pname != "" {
+ return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp)
+ }
+ ctxt.Logf("loadinternal: cannot find %s\n", name)
+ return nil
+ }
+
+ for _, libdir := range ctxt.Libdir {
+ if ctxt.linkShared {
+ shlibname := filepath.Join(libdir, name+".shlibname")
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("searching for %s.a in %s\n", name, shlibname)
+ }
+ if _, err := os.Stat(shlibname); err == nil {
+ return addlibpath(ctxt, "internal", "internal", "", name, shlibname, zerofp)
+ }
+ }
+ pname := filepath.Join(libdir, name+".a")
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("searching for %s.a in %s\n", name, pname)
+ }
+ if _, err := os.Stat(pname); err == nil {
+ return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp)
+ }
+ }
+
+ if name == "runtime" {
+ Exitf("error: unable to find runtime.a")
+ }
+ ctxt.Logf("warning: unable to find %s.a\n", name)
+ return nil
+}
+
+// extld returns the current external linker.
+func (ctxt *Link) extld() []string {
+ if len(flagExtld) == 0 {
+ // Return the default external linker for the platform.
+ // This only matters when link tool is called directly without explicit -extld,
+ // go tool already passes the correct linker in other cases.
+ switch buildcfg.GOOS {
+ case "darwin", "freebsd", "openbsd":
+ flagExtld = []string{"clang"}
+ default:
+ flagExtld = []string{"gcc"}
+ }
+ }
+ return flagExtld
+}
+
+// findLibPathCmd uses cmd command to find gcc library libname.
+// It returns library full path if found, or "none" if not found.
+func (ctxt *Link) findLibPathCmd(cmd, libname string) string {
+ extld := ctxt.extld()
+ name, args := extld[0], extld[1:]
+ args = append(args, hostlinkArchArgs(ctxt.Arch)...)
+ args = append(args, cmd)
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("%s %v\n", extld, args)
+ }
+ out, err := exec.Command(name, args...).Output()
+ if err != nil {
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("not using a %s file because compiler failed\n%v\n%s\n", libname, err, out)
+ }
+ return "none"
+ }
+ return strings.TrimSpace(string(out))
+}
+
+// findLibPath searches for library libname.
+// It returns library full path if found, or "none" if not found.
+func (ctxt *Link) findLibPath(libname string) string {
+ return ctxt.findLibPathCmd("--print-file-name="+libname, libname)
+}
+
+func (ctxt *Link) loadlib() {
+ var flags uint32
+ switch *FlagStrictDups {
+ case 0:
+ // nothing to do
+ case 1, 2:
+ flags |= loader.FlagStrictDups
+ default:
+ log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
+ }
+ elfsetstring1 := func(str string, off int) { elfsetstring(ctxt, 0, str, off) }
+ ctxt.loader = loader.NewLoader(flags, elfsetstring1, &ctxt.ErrorReporter.ErrorReporter)
+ ctxt.ErrorReporter.SymName = func(s loader.Sym) string {
+ return ctxt.loader.SymName(s)
+ }
+
+ // ctxt.Library grows during the loop, so not a range loop.
+ i := 0
+ for ; i < len(ctxt.Library); i++ {
+ lib := ctxt.Library[i]
+ if lib.Shlib == "" {
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref)
+ }
+ loadobjfile(ctxt, lib)
+ }
+ }
+
+ // load internal packages, if not already
+ if *flagRace {
+ loadinternal(ctxt, "runtime/race")
+ }
+ if *flagMsan {
+ loadinternal(ctxt, "runtime/msan")
+ }
+ if *flagAsan {
+ loadinternal(ctxt, "runtime/asan")
+ }
+ loadinternal(ctxt, "runtime")
+ for ; i < len(ctxt.Library); i++ {
+ lib := ctxt.Library[i]
+ if lib.Shlib == "" {
+ loadobjfile(ctxt, lib)
+ }
+ }
+ // At this point, the Go objects are "preloaded". Not all the symbols are
+ // added to the symbol table (only defined package symbols are). Looking
+ // up symbol by name may not get expected result.
+
+ iscgo = ctxt.LibraryByPkg["runtime/cgo"] != nil
+
+ // Plugins a require cgo support to function. Similarly, plugins may require additional
+ // internal linker support on some platforms which may not be implemented.
+ ctxt.canUsePlugins = ctxt.LibraryByPkg["plugin"] != nil && iscgo
+
+ // We now have enough information to determine the link mode.
+ determineLinkMode(ctxt)
+
+ if ctxt.LinkMode == LinkExternal && !iscgo && !(buildcfg.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && ctxt.Arch.Family == sys.AMD64) {
+ // This indicates a user requested -linkmode=external.
+ // The startup code uses an import of runtime/cgo to decide
+ // whether to initialize the TLS. So give it one. This could
+ // be handled differently but it's an unusual case.
+ if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil && lib.Shlib == "" {
+ if ctxt.BuildMode == BuildModeShared || ctxt.linkShared {
+ Exitf("cannot implicitly include runtime/cgo in a shared library")
+ }
+ for ; i < len(ctxt.Library); i++ {
+ lib := ctxt.Library[i]
+ if lib.Shlib == "" {
+ loadobjfile(ctxt, lib)
+ }
+ }
+ }
+ }
+
+ // Add non-package symbols and references of externally defined symbols.
+ ctxt.loader.LoadSyms(ctxt.Arch)
+
+ // Load symbols from shared libraries, after all Go object symbols are loaded.
+ for _, lib := range ctxt.Library {
+ if lib.Shlib != "" {
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref)
+ }
+ ldshlibsyms(ctxt, lib.Shlib)
+ }
+ }
+
+ // Process cgo directives (has to be done before host object loading).
+ ctxt.loadcgodirectives()
+
+ // Conditionally load host objects, or setup for external linking.
+ hostobjs(ctxt)
+ hostlinksetup(ctxt)
+
+ if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
+ // If we have any undefined symbols in external
+ // objects, try to read them from the libgcc file.
+ any := false
+ undefs, froms := ctxt.loader.UndefinedRelocTargets(1)
+ if len(undefs) > 0 {
+ any = true
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("loadlib: first unresolved is %s [%d] from %s [%d]\n",
+ ctxt.loader.SymName(undefs[0]), undefs[0],
+ ctxt.loader.SymName(froms[0]), froms[0])
+ }
+ }
+ if any {
+ if *flagLibGCC == "" {
+ *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc")
+ }
+ if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" {
+ // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a".
+ // In this case we fail to load libgcc.a and can encounter link
+ // errors - see if we can find libcompiler_rt.a instead.
+ *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
+ }
+ if ctxt.HeadType == objabi.Hwindows {
+ loadWindowsHostArchives(ctxt)
+ }
+ if *flagLibGCC != "none" {
+ hostArchive(ctxt, *flagLibGCC)
+ }
+ }
+ }
+
+ // We've loaded all the code now.
+ ctxt.Loaded = true
+
+ importcycles()
+
+ 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 *flagRace {
+ if p := ctxt.findLibPath("libsynchronization.a"); p != "none" {
+ hostArchive(ctxt, 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, froms := ctxt.loader.UndefinedRelocTargets(1)
+ if len(undefs) > 0 {
+ any = true
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("loadWindowsHostArchives: remaining unresolved is %s [%d] from %s [%d]\n",
+ ctxt.loader.SymName(undefs[0]), undefs[0],
+ ctxt.loader.SymName(froms[0]), froms[0])
+ }
+ }
+ }
+ // 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)
+ }
+ }
+
+ // Fix up references to DLL import symbols now that we're done
+ // pulling in new objects.
+ if err := loadpe.PostProcessImports(); err != nil {
+ Errorf(nil, "%v", err)
+ }
+
+ // 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() {
+ l := ctxt.loader
+ hostObjSyms := make(map[loader.Sym]struct{})
+ for _, d := range ctxt.cgodata {
+ setCgoAttr(ctxt, d.file, d.pkg, d.directives, hostObjSyms)
+ }
+ ctxt.cgodata = nil
+
+ if ctxt.LinkMode == LinkInternal {
+ // Drop all the cgo_import_static declarations.
+ // Turns out we won't be needing them.
+ for symIdx := range hostObjSyms {
+ if l.SymType(symIdx) == sym.SHOSTOBJ {
+ // If a symbol was marked both
+ // cgo_import_static and cgo_import_dynamic,
+ // then we want to make it cgo_import_dynamic
+ // now.
+ su := l.MakeSymbolUpdater(symIdx)
+ if l.SymExtname(symIdx) != "" && l.SymDynimplib(symIdx) != "" && !(l.AttrCgoExportStatic(symIdx) || l.AttrCgoExportDynamic(symIdx)) {
+ su.SetType(sym.SDYNIMPORT)
+ } else {
+ su.SetType(0)
+ }
+ }
+ }
+ }
+}
+
+// Set up flags and special symbols depending on the platform build mode.
+// This version works with loader.Loader.
+func (ctxt *Link) linksetup() {
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModePlugin:
+ symIdx := ctxt.loader.LookupOrCreateSym("runtime.islibrary", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(symIdx)
+ sb.SetType(sym.SNOPTRDATA)
+ sb.AddUint8(1)
+ case BuildModeCArchive:
+ symIdx := ctxt.loader.LookupOrCreateSym("runtime.isarchive", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(symIdx)
+ sb.SetType(sym.SNOPTRDATA)
+ sb.AddUint8(1)
+ }
+
+ // Recalculate pe parameters now that we have ctxt.LinkMode set.
+ if ctxt.HeadType == objabi.Hwindows {
+ Peinit(ctxt)
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ // When external linking, we are creating an object file. The
+ // absolute address is irrelevant.
+ *FlagTextAddr = 0
+ }
+
+ // If there are no dynamic libraries needed, gcc disables dynamic linking.
+ // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
+ // assumes that a dynamic binary always refers to at least one dynamic library.
+ // Rather than be a source of test cases for glibc, disable dynamic linking
+ // the same way that gcc would.
+ //
+ // Exception: on OS X, programs such as Shark only work with dynamic
+ // binaries, so leave it enabled on OS X (Mach-O) binaries.
+ // Also leave it enabled on Solaris which doesn't support
+ // statically linked binaries.
+ if ctxt.BuildMode == BuildModeExe {
+ if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris {
+ *FlagD = true
+ }
+ }
+
+ if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && buildcfg.GOOS != "aix" {
+ toc := ctxt.loader.LookupOrCreateSym(".TOC.", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(toc)
+ sb.SetType(sym.SDYNIMPORT)
+ }
+
+ // The Android Q linker started to complain about underalignment of the our TLS
+ // section. We don't actually use the section on android, so don't
+ // generate it.
+ if buildcfg.GOOS != "android" {
+ tlsg := ctxt.loader.LookupOrCreateSym("runtime.tlsg", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(tlsg)
+
+ // runtime.tlsg is used for external linking on platforms that do not define
+ // a variable to hold g in assembly (currently only intel).
+ if sb.Type() == 0 {
+ sb.SetType(sym.STLSBSS)
+ sb.SetSize(int64(ctxt.Arch.PtrSize))
+ } else if sb.Type() != sym.SDYNIMPORT {
+ Errorf(nil, "runtime declared tlsg variable %v", sb.Type())
+ }
+ ctxt.loader.SetAttrReachable(tlsg, true)
+ ctxt.Tlsg = tlsg
+ }
+
+ var moduledata loader.Sym
+ var mdsb *loader.SymbolBuilder
+ if ctxt.BuildMode == BuildModePlugin {
+ moduledata = ctxt.loader.LookupOrCreateSym("local.pluginmoduledata", 0)
+ mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+ ctxt.loader.SetAttrLocal(moduledata, true)
+ } else {
+ moduledata = ctxt.loader.LookupOrCreateSym("runtime.firstmoduledata", 0)
+ mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+ }
+ if mdsb.Type() != 0 && mdsb.Type() != sym.SDYNIMPORT {
+ // If the module (toolchain-speak for "executable or shared
+ // library") we are linking contains the runtime package, it
+ // will define the runtime.firstmoduledata symbol and we
+ // truncate it back to 0 bytes so we can define its entire
+ // contents in symtab.go:symtab().
+ mdsb.SetSize(0)
+
+ // In addition, on ARM, the runtime depends on the linker
+ // recording the value of GOARM.
+ if ctxt.Arch.Family == sys.ARM {
+ goarm := ctxt.loader.LookupOrCreateSym("runtime.goarm", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(goarm)
+ sb.SetType(sym.SDATA)
+ sb.SetSize(0)
+ sb.AddUint8(uint8(buildcfg.GOARM))
+ }
+
+ // Set runtime.disableMemoryProfiling bool if
+ // runtime.MemProfile is not retained in the binary after
+ // deadcode (and we're not dynamically linking).
+ memProfile := ctxt.loader.Lookup("runtime.MemProfile", abiInternalVer)
+ if memProfile != 0 && !ctxt.loader.AttrReachable(memProfile) && !ctxt.DynlinkingGo() {
+ memProfSym := ctxt.loader.LookupOrCreateSym("runtime.disableMemoryProfiling", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(memProfSym)
+ sb.SetType(sym.SDATA)
+ sb.SetSize(0)
+ sb.AddUint8(1) // true bool
+ }
+ } else {
+ // If OTOH the module does not contain the runtime package,
+ // create a local symbol for the moduledata.
+ moduledata = ctxt.loader.LookupOrCreateSym("local.moduledata", 0)
+ mdsb = ctxt.loader.MakeSymbolUpdater(moduledata)
+ ctxt.loader.SetAttrLocal(moduledata, true)
+ }
+ // In all cases way we mark the moduledata as noptrdata to hide it from
+ // the GC.
+ mdsb.SetType(sym.SNOPTRDATA)
+ ctxt.loader.SetAttrReachable(moduledata, true)
+ ctxt.Moduledata = moduledata
+
+ if ctxt.Arch == sys.Arch386 && ctxt.HeadType != objabi.Hwindows {
+ if (ctxt.BuildMode == BuildModeCArchive && ctxt.IsELF) || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE || ctxt.DynlinkingGo() {
+ got := ctxt.loader.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(got)
+ sb.SetType(sym.SDYNIMPORT)
+ ctxt.loader.SetAttrReachable(got, true)
+ }
+ }
+
+ // DWARF-gen and other phases require that the unit Textp slices
+ // be populated, so that it can walk the functions in each unit.
+ // Call into the loader to do this (requires that we collect the
+ // set of internal libraries first). NB: might be simpler if we
+ // moved isRuntimeDepPkg to cmd/internal and then did the test in
+ // loader.AssignTextSymbolOrder.
+ ctxt.Library = postorder(ctxt.Library)
+ intlibs := []bool{}
+ for _, lib := range ctxt.Library {
+ intlibs = append(intlibs, isRuntimeDepPkg(lib.Pkg))
+ }
+ ctxt.Textp = ctxt.loader.AssignTextSymbolOrder(ctxt.Library, intlibs, ctxt.Textp)
+}
+
+// mangleTypeSym shortens the names of symbols that represent Go types
+// if they are visible in the symbol table.
+//
+// As the names of these symbols are derived from the string of
+// the type, they can run to many kilobytes long. So we shorten
+// them using a SHA-1 when the name appears in the final binary.
+// This also removes characters that upset external linkers.
+//
+// These are the symbols that begin with the prefix 'type.' and
+// contain run-time type information used by the runtime and reflect
+// packages. All Go binaries contain these symbols, but only
+// those programs loaded dynamically in multiple parts need these
+// symbols to have entries in the symbol table.
+func (ctxt *Link) mangleTypeSym() {
+ if ctxt.BuildMode != BuildModeShared && !ctxt.linkShared && ctxt.BuildMode != BuildModePlugin && !ctxt.CanUsePlugins() {
+ return
+ }
+
+ ldr := ctxt.loader
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) && !ctxt.linkShared {
+ // If -linkshared, the GCProg generation code may need to reach
+ // out to the shared library for the type descriptor's data, even
+ // the type descriptor itself is not actually needed at run time
+ // (therefore not reachable). We still need to mangle its name,
+ // so it is consistent with the one stored in the shared library.
+ continue
+ }
+ name := ldr.SymName(s)
+ newName := typeSymbolMangle(name)
+ if newName != name {
+ ldr.SetSymExtname(s, newName)
+
+ // When linking against a shared library, the Go object file may
+ // have reference to the original symbol name whereas the shared
+ // library provides a symbol with the mangled name. We need to
+ // copy the payload of mangled to original.
+ // XXX maybe there is a better way to do this.
+ dup := ldr.Lookup(newName, ldr.SymVersion(s))
+ if dup != 0 {
+ st := ldr.SymType(s)
+ dt := ldr.SymType(dup)
+ if st == sym.Sxxx && dt != sym.Sxxx {
+ ldr.CopySym(dup, s)
+ }
+ }
+ }
+ }
+}
+
+// typeSymbolMangle mangles the given symbol name into something shorter.
+//
+// Keep the type:. prefix, which parts of the linker (like the
+// DWARF generator) know means the symbol is not decodable.
+// Leave type:runtime. symbols alone, because other parts of
+// the linker manipulates them.
+func typeSymbolMangle(name string) string {
+ if !strings.HasPrefix(name, "type:") {
+ return name
+ }
+ if strings.HasPrefix(name, "type:runtime.") {
+ return name
+ }
+ if len(name) <= 14 && !strings.Contains(name, "@") { // Issue 19529
+ return name
+ }
+ hash := notsha256.Sum256([]byte(name))
+ prefix := "type:"
+ if name[5] == '.' {
+ prefix = "type:."
+ }
+ return prefix + base64.StdEncoding.EncodeToString(hash[:6])
+}
+
+/*
+ * look for the next file in an archive.
+ * adapted from libmach.
+ */
+func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 {
+ if off&1 != 0 {
+ off++
+ }
+ bp.MustSeek(off, 0)
+ var buf [SAR_HDR]byte
+ if n, err := io.ReadFull(bp, buf[:]); err != nil {
+ if n == 0 && err != io.EOF {
+ return -1
+ }
+ return 0
+ }
+
+ a.name = artrim(buf[0:16])
+ a.date = artrim(buf[16:28])
+ a.uid = artrim(buf[28:34])
+ a.gid = artrim(buf[34:40])
+ a.mode = artrim(buf[40:48])
+ a.size = artrim(buf[48:58])
+ a.fmag = artrim(buf[58:60])
+
+ arsize := atolwhex(a.size)
+ if arsize&1 != 0 {
+ arsize++
+ }
+ return arsize + SAR_HDR
+}
+
+func loadobjfile(ctxt *Link, lib *sym.Library) {
+ pkg := objabi.PathToPrefix(lib.Pkg)
+
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("ldobj: %s (%s)\n", lib.File, pkg)
+ }
+ f, err := bio.Open(lib.File)
+ if err != nil {
+ Exitf("cannot open file %s: %v", lib.File, err)
+ }
+ defer f.Close()
+ defer func() {
+ if pkg == "main" && !lib.Main {
+ Exitf("%s: not package main", lib.File)
+ }
+ }()
+
+ for i := 0; i < len(ARMAG); i++ {
+ if c, err := f.ReadByte(); err == nil && c == ARMAG[i] {
+ continue
+ }
+
+ /* load it as a regular file */
+ l := f.MustSeek(0, 2)
+ f.MustSeek(0, 0)
+ ldobj(ctxt, f, lib, l, lib.File, lib.File)
+ return
+ }
+
+ /*
+ * load all the object files from the archive now.
+ * this gives us sequential file access and keeps us
+ * from needing to come back later to pick up more
+ * objects. it breaks the usual C archive model, but
+ * this is Go, not C. the common case in Go is that
+ * we need to load all the objects, and then we throw away
+ * the individual symbols that are unused.
+ *
+ * loading every object will also make it possible to
+ * load foreign objects not referenced by __.PKGDEF.
+ */
+ var arhdr ArHdr
+ off := f.Offset()
+ for {
+ l := nextar(f, off, &arhdr)
+ if l == 0 {
+ break
+ }
+ if l < 0 {
+ Exitf("%s: malformed archive", lib.File)
+ }
+ off += l
+
+ // __.PKGDEF isn't a real Go object file, and it's
+ // absent in -linkobj builds anyway. Skipping it
+ // ensures consistency between -linkobj and normal
+ // build modes.
+ if arhdr.name == pkgdef {
+ continue
+ }
+
+ if arhdr.name == "dynimportfail" {
+ dynimportfail = append(dynimportfail, lib.Pkg)
+ }
+ if arhdr.name == "preferlinkext" {
+ // Ignore this directive if -linkmode has been
+ // set explicitly.
+ if ctxt.LinkMode == LinkAuto {
+ preferlinkext = append(preferlinkext, lib.Pkg)
+ }
+ }
+
+ // Skip other special (non-object-file) sections that
+ // build tools may have added. Such sections must have
+ // short names so that the suffix is not truncated.
+ if len(arhdr.name) < 16 {
+ if ext := filepath.Ext(arhdr.name); ext != ".o" && ext != ".syso" {
+ continue
+ }
+ }
+
+ pname := fmt.Sprintf("%s(%s)", lib.File, arhdr.name)
+ l = atolwhex(arhdr.size)
+ ldobj(ctxt, f, lib, l, pname, lib.File)
+ }
+}
+
+type Hostobj struct {
+ ld func(*Link, *bio.Reader, string, int64, string)
+ pkg string
+ pn string
+ file string
+ off int64
+ length int64
+}
+
+var hostobj []Hostobj
+
+// These packages can use internal linking mode.
+// Others trigger external mode.
+var internalpkg = []string{
+ "crypto/internal/boring",
+ "crypto/internal/boring/syso",
+ "crypto/x509",
+ "net",
+ "os/user",
+ "runtime/cgo",
+ "runtime/race",
+ "runtime/race/internal/amd64v1",
+ "runtime/race/internal/amd64v3",
+ "runtime/msan",
+ "runtime/asan",
+}
+
+func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType objabi.HeadType, f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj {
+ isinternal := false
+ for _, intpkg := range internalpkg {
+ if pkg == intpkg {
+ isinternal = true
+ break
+ }
+ }
+
+ // DragonFly declares errno with __thread, which results in a symbol
+ // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not
+ // currently know how to handle TLS relocations, hence we have to
+ // force external linking for any libraries that link in code that
+ // uses errno. This can be removed if the Go linker ever supports
+ // these relocation types.
+ if headType == objabi.Hdragonfly {
+ if pkg == "net" || pkg == "os/user" {
+ isinternal = false
+ }
+ }
+
+ if !isinternal {
+ externalobj = true
+ }
+
+ hostobj = append(hostobj, Hostobj{})
+ h := &hostobj[len(hostobj)-1]
+ h.ld = ld
+ h.pkg = pkg
+ h.pn = pn
+ h.file = file
+ h.off = f.Offset()
+ h.length = length
+ return h
+}
+
+func hostobjs(ctxt *Link) {
+ if ctxt.LinkMode != LinkInternal {
+ return
+ }
+ var h *Hostobj
+
+ for i := 0; i < len(hostobj); i++ {
+ h = &hostobj[i]
+ f, err := bio.Open(h.file)
+ if err != nil {
+ Exitf("cannot reopen %s: %v", h.pn, err)
+ }
+ f.MustSeek(h.off, 0)
+ if h.ld == nil {
+ Errorf(nil, "%s: unrecognized object file format", h.pn)
+ continue
+ }
+ h.ld(ctxt, f, h.pkg, h.length, h.pn)
+ if *flagCaptureHostObjs != "" {
+ captureHostObj(h)
+ }
+ f.Close()
+ }
+}
+
+func hostlinksetup(ctxt *Link) {
+ if ctxt.LinkMode != LinkExternal {
+ return
+ }
+
+ // For external link, record that we need to tell the external linker -s,
+ // and turn off -s internally: the external linker needs the symbol
+ // information for its final link.
+ debug_s = *FlagS
+ *FlagS = false
+
+ // create temporary directory and arrange cleanup
+ if *flagTmpdir == "" {
+ dir, err := os.MkdirTemp("", "go-link-")
+ if err != nil {
+ log.Fatal(err)
+ }
+ *flagTmpdir = dir
+ ownTmpDir = true
+ AtExit(func() {
+ os.RemoveAll(*flagTmpdir)
+ })
+ }
+
+ // change our output to temporary object file
+ if err := ctxt.Out.Close(); err != nil {
+ Exitf("error closing output file")
+ }
+ mayberemoveoutfile()
+
+ p := filepath.Join(*flagTmpdir, "go.o")
+ if err := ctxt.Out.Open(p); err != nil {
+ Exitf("cannot create %s: %v", p, err)
+ }
+}
+
+// hostobjCopy creates a copy of the object files in hostobj in a
+// temporary directory.
+func hostobjCopy() (paths []string) {
+ var wg sync.WaitGroup
+ sema := make(chan struct{}, runtime.NumCPU()) // limit open file descriptors
+ for i, h := range hostobj {
+ h := h
+ dst := filepath.Join(*flagTmpdir, fmt.Sprintf("%06d.o", i))
+ paths = append(paths, dst)
+
+ wg.Add(1)
+ go func() {
+ sema <- struct{}{}
+ defer func() {
+ <-sema
+ wg.Done()
+ }()
+ f, err := os.Open(h.file)
+ if err != nil {
+ Exitf("cannot reopen %s: %v", h.pn, err)
+ }
+ defer f.Close()
+ if _, err := f.Seek(h.off, 0); err != nil {
+ Exitf("cannot seek %s: %v", h.pn, err)
+ }
+
+ w, err := os.Create(dst)
+ if err != nil {
+ Exitf("cannot create %s: %v", dst, err)
+ }
+ if _, err := io.CopyN(w, f, h.length); err != nil {
+ Exitf("cannot write %s: %v", dst, err)
+ }
+ if err := w.Close(); err != nil {
+ Exitf("cannot close %s: %v", dst, err)
+ }
+ }()
+ }
+ wg.Wait()
+ return paths
+}
+
+// writeGDBLinkerScript creates gcc linker script file in temp
+// directory. writeGDBLinkerScript returns created file path.
+// The script is used to work around gcc bug
+// (see https://golang.org/issue/20183 for details).
+func writeGDBLinkerScript() string {
+ name := "fix_debug_gdb_scripts.ld"
+ path := filepath.Join(*flagTmpdir, name)
+ src := `SECTIONS
+{
+ .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) :
+ {
+ *(.debug_gdb_scripts)
+ }
+}
+INSERT AFTER .debug_types;
+`
+ err := os.WriteFile(path, []byte(src), 0666)
+ if err != nil {
+ Errorf(nil, "WriteFile %s failed: %v", name, err)
+ }
+ return path
+}
+
+// archive builds a .a archive from the hostobj object files.
+func (ctxt *Link) archive() {
+ if ctxt.BuildMode != BuildModeCArchive {
+ return
+ }
+
+ exitIfErrors()
+
+ if *flagExtar == "" {
+ *flagExtar = "ar"
+ }
+
+ mayberemoveoutfile()
+
+ // Force the buffer to flush here so that external
+ // tools will see a complete file.
+ if err := ctxt.Out.Close(); err != nil {
+ Exitf("error closing %v", *flagOutfile)
+ }
+
+ argv := []string{*flagExtar, "-q", "-c", "-s"}
+ if ctxt.HeadType == objabi.Haix {
+ argv = append(argv, "-X64")
+ }
+ argv = append(argv, *flagOutfile)
+ argv = append(argv, filepath.Join(*flagTmpdir, "go.o"))
+ argv = append(argv, hostobjCopy()...)
+
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("archive: %s\n", strings.Join(argv, " "))
+ }
+
+ // If supported, use syscall.Exec() to invoke the archive command,
+ // which should be the final remaining step needed for the link.
+ // This will reduce peak RSS for the link (and speed up linking of
+ // large applications), since when the archive command runs we
+ // won't be holding onto all of the linker's live memory.
+ if syscallExecSupported && !ownTmpDir {
+ runAtExitFuncs()
+ ctxt.execArchive(argv)
+ panic("should not get here")
+ }
+
+ // Otherwise invoke 'ar' in the usual way (fork + exec).
+ if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
+ Exitf("running %s failed: %v\n%s", argv[0], err, out)
+ }
+}
+
+func (ctxt *Link) hostlink() {
+ if ctxt.LinkMode != LinkExternal || nerrors > 0 {
+ return
+ }
+ if ctxt.BuildMode == BuildModeCArchive {
+ return
+ }
+
+ var argv []string
+ argv = append(argv, ctxt.extld()...)
+ argv = append(argv, hostlinkArchArgs(ctxt.Arch)...)
+
+ if *FlagS || debug_s {
+ if ctxt.HeadType == objabi.Hdarwin {
+ // Recent versions of macOS print
+ // ld: warning: option -s is obsolete and being ignored
+ // so do not pass any arguments.
+ } else {
+ argv = append(argv, "-s")
+ }
+ }
+
+ // On darwin, whether to combine DWARF into executable.
+ // Only macOS supports unmapped segments such as our __DWARF segment.
+ combineDwarf := ctxt.IsDarwin() && !*FlagS && !*FlagW && !debug_s && machoPlatform == PLATFORM_MACOS
+
+ switch ctxt.HeadType {
+ case objabi.Hdarwin:
+ if linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,-ld_classic") {
+ // Force old linker to work around bugs in Apple's new linker.
+ argv = append(argv, "-Wl,-ld_classic")
+ }
+ if combineDwarf {
+ // Leave room for DWARF combining.
+ // -headerpad is incompatible with -fembed-bitcode.
+ argv = append(argv, "-Wl,-headerpad,1144")
+ }
+ if ctxt.DynlinkingGo() && buildcfg.GOOS != "ios" {
+ // -flat_namespace is deprecated on iOS.
+ // It is useful for supporting plugins. We don't support plugins on iOS.
+ // -flat_namespace may cause the dynamic linker to hang at forkExec when
+ // resolving a lazy binding. See issue 38824.
+ // Force eager resolution to work around.
+ argv = append(argv, "-Wl,-flat_namespace", "-Wl,-bind_at_load")
+ }
+ if !combineDwarf {
+ argv = append(argv, "-Wl,-S") // suppress STAB (symbolic debugging) symbols
+ }
+ case objabi.Hopenbsd:
+ argv = append(argv, "-Wl,-nopie")
+ argv = append(argv, "-pthread")
+ case objabi.Hwindows:
+ if windowsgui {
+ argv = append(argv, "-mwindows")
+ } else {
+ argv = append(argv, "-mconsole")
+ }
+ // Mark as having awareness of terminal services, to avoid
+ // ancient compatibility hacks.
+ argv = append(argv, "-Wl,--tsaware")
+
+ // Enable DEP
+ argv = append(argv, "-Wl,--nxcompat")
+
+ argv = append(argv, fmt.Sprintf("-Wl,--major-os-version=%d", PeMinimumTargetMajorVersion))
+ argv = append(argv, fmt.Sprintf("-Wl,--minor-os-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:
+ argv = append(argv, "-pthread")
+ // prevent ld to reorder .text functions to keep the same
+ // first/last functions for moduledata.
+ argv = append(argv, "-Wl,-bnoobjreorder")
+ // mcmodel=large is needed for every gcc generated files, but
+ // ld still need -bbigtoc in order to allow larger TOC.
+ argv = append(argv, "-mcmodel=large")
+ argv = append(argv, "-Wl,-bbigtoc")
+ }
+
+ // 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 && heopt != "" {
+ argv = append(argv, "-Wl,"+heopt)
+ }
+ return argv
+ }
+
+ switch ctxt.BuildMode {
+ case BuildModeExe:
+ if ctxt.HeadType == objabi.Hdarwin {
+ if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() {
+ argv = append(argv, "-Wl,-no_pie")
+ }
+ }
+ if *flagRace && ctxt.HeadType == objabi.Hwindows {
+ // Current windows/amd64 race detector tsan support
+ // library can't handle PIE mode (see #53539 for more details).
+ // For now, explicitly disable PIE (since some compilers
+ // default to it) if -race is in effect.
+ argv = addASLRargs(argv, false)
+ }
+ case BuildModePIE:
+ switch ctxt.HeadType {
+ case objabi.Hdarwin, objabi.Haix:
+ case objabi.Hwindows:
+ if *flagAslr && *flagRace {
+ // Current windows/amd64 race detector tsan support
+ // library can't handle PIE mode (see #53539 for more details).
+ // Disable alsr if -race in effect.
+ *flagAslr = false
+ }
+ argv = addASLRargs(argv, *flagAslr)
+ default:
+ // ELF.
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-pie")
+ }
+ case BuildModeCShared:
+ if ctxt.HeadType == objabi.Hdarwin {
+ argv = append(argv, "-dynamiclib")
+ } else {
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-shared")
+ if ctxt.HeadType == objabi.Hwindows {
+ argv = addASLRargs(argv, *flagAslr)
+ } else {
+ // Pass -z nodelete to mark the shared library as
+ // non-closeable: a dlclose will do nothing.
+ argv = append(argv, "-Wl,-z,nodelete")
+ // Only pass Bsymbolic on non-Windows.
+ argv = append(argv, "-Wl,-Bsymbolic")
+ }
+ }
+ case BuildModeShared:
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-shared")
+ case BuildModePlugin:
+ if ctxt.HeadType == objabi.Hdarwin {
+ argv = append(argv, "-dynamiclib")
+ } else {
+ if ctxt.UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
+ argv = append(argv, "-shared")
+ }
+ }
+
+ var altLinker string
+ if ctxt.IsELF && ctxt.DynlinkingGo() {
+ // We force all symbol resolution to be done at program startup
+ // because lazy PLT resolution can use large amounts of stack at
+ // times we cannot allow it to do so.
+ argv = append(argv, "-Wl,-z,now")
+
+ // Do not let the host linker generate COPY relocations. These
+ // can move symbols out of sections that rely on stable offsets
+ // from the beginning of the section (like sym.STYPE).
+ argv = append(argv, "-Wl,-z,nocopyreloc")
+
+ if buildcfg.GOOS == "android" {
+ // Use lld to avoid errors from default linker (issue #38838)
+ altLinker = "lld"
+ }
+
+ if ctxt.Arch.InFamily(sys.ARM, sys.ARM64) && buildcfg.GOOS == "linux" {
+ // On ARM, the GNU linker will generate COPY relocations
+ // even with -znocopyreloc set.
+ // https://sourceware.org/bugzilla/show_bug.cgi?id=19962
+ //
+ // On ARM64, the GNU linker will fail instead of
+ // generating COPY relocations.
+ //
+ // In both cases, switch to gold.
+ altLinker = "gold"
+
+ // If gold is not installed, gcc will silently switch
+ // back to ld.bfd. So we parse the version information
+ // and provide a useful error if gold is missing.
+ name, args := flagExtld[0], flagExtld[1:]
+ args = append(args, "-fuse-ld=gold", "-Wl,--version")
+ cmd := exec.Command(name, args...)
+ if out, err := cmd.CombinedOutput(); err == nil {
+ if !bytes.Contains(out, []byte("GNU gold")) {
+ log.Fatalf("ARM external linker must be gold (issue #15696), but is not: %s", out)
+ }
+ }
+ }
+ }
+ if ctxt.Arch.Family == sys.ARM64 && buildcfg.GOOS == "freebsd" {
+ // Switch to ld.bfd on freebsd/arm64.
+ altLinker = "bfd"
+
+ // Provide a useful error if ld.bfd is missing.
+ name, args := flagExtld[0], flagExtld[1:]
+ args = append(args, "-fuse-ld=bfd", "-Wl,--version")
+ cmd := exec.Command(name, args...)
+ if out, err := cmd.CombinedOutput(); err == nil {
+ if !bytes.Contains(out, []byte("GNU ld")) {
+ log.Fatalf("ARM64 external linker must be ld.bfd (issue #35197), please install devel/binutils")
+ }
+ }
+ }
+ if altLinker != "" {
+ argv = append(argv, "-fuse-ld="+altLinker)
+ }
+
+ if ctxt.IsELF && len(buildinfo) > 0 {
+ argv = append(argv, fmt.Sprintf("-Wl,--build-id=0x%x", buildinfo))
+ }
+
+ // On Windows, given -o foo, GCC will append ".exe" to produce
+ // "foo.exe". We have decided that we want to honor the -o
+ // option. To make this work, we append a '.' so that GCC
+ // will decide that the file already has an extension. We
+ // only want to do this when producing a Windows output file
+ // on a Windows host.
+ outopt := *flagOutfile
+ if buildcfg.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" {
+ outopt += "."
+ }
+ argv = append(argv, "-o")
+ argv = append(argv, outopt)
+
+ if rpath.val != "" {
+ argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val))
+ }
+
+ if *flagInterpreter != "" {
+ // Many linkers support both -I and the --dynamic-linker flags
+ // to set the ELF interpreter, but lld only supports
+ // --dynamic-linker so prefer that (ld on very old Solaris only
+ // supports -I but that seems less important).
+ argv = append(argv, fmt.Sprintf("-Wl,--dynamic-linker,%s", *flagInterpreter))
+ }
+
+ // Force global symbols to be exported for dlopen, etc.
+ if ctxt.IsELF {
+ argv = append(argv, "-rdynamic")
+ }
+ if ctxt.HeadType == objabi.Haix {
+ fileName := xcoffCreateExportFile(ctxt)
+ argv = append(argv, "-Wl,-bE:"+fileName)
+ }
+
+ const unusedArguments = "-Qunused-arguments"
+ if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, unusedArguments) {
+ argv = append(argv, unusedArguments)
+ }
+
+ if ctxt.IsWindows() {
+ // Suppress generation of the PE file header timestamp,
+ // so as to avoid spurious build ID differences between
+ // linked binaries that are otherwise identical other than
+ // the date/time they were linked.
+ const noTimeStamp = "-Wl,--no-insert-timestamp"
+ if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, noTimeStamp) {
+ argv = append(argv, noTimeStamp)
+ }
+ }
+
+ const compressDWARF = "-Wl,--compress-debug-sections=zlib"
+ if ctxt.compressDWARF && linkerFlagSupported(ctxt.Arch, argv[0], altLinker, compressDWARF) {
+ argv = append(argv, compressDWARF)
+ }
+
+ argv = append(argv, filepath.Join(*flagTmpdir, "go.o"))
+ argv = append(argv, hostobjCopy()...)
+ if ctxt.HeadType == objabi.Haix {
+ // We want to have C files after Go files to remove
+ // trampolines csects made by ld.
+ argv = append(argv, "-nostartfiles")
+ argv = append(argv, "/lib/crt0_64.o")
+
+ extld := ctxt.extld()
+ name, args := extld[0], extld[1:]
+ // Get starting files.
+ getPathFile := func(file string) string {
+ args := append(args, "-maix64", "--print-file-name="+file)
+ out, err := exec.Command(name, args...).CombinedOutput()
+ if err != nil {
+ log.Fatalf("running %s failed: %v\n%s", extld, err, out)
+ }
+ return strings.Trim(string(out), "\n")
+ }
+ // Since GCC version 11, the 64-bit version of GCC starting files
+ // are now suffixed by "_64". Even under "-maix64" multilib directory
+ // "crtcxa.o" is 32-bit.
+ crtcxa := getPathFile("crtcxa_64.o")
+ if !filepath.IsAbs(crtcxa) {
+ crtcxa = getPathFile("crtcxa.o")
+ }
+ crtdbase := getPathFile("crtdbase_64.o")
+ if !filepath.IsAbs(crtdbase) {
+ crtdbase = getPathFile("crtdbase.o")
+ }
+ argv = append(argv, crtcxa)
+ argv = append(argv, crtdbase)
+ }
+
+ if ctxt.linkShared {
+ seenDirs := make(map[string]bool)
+ seenLibs := make(map[string]bool)
+ addshlib := func(path string) {
+ dir, base := filepath.Split(path)
+ if !seenDirs[dir] {
+ argv = append(argv, "-L"+dir)
+ if !rpath.set {
+ argv = append(argv, "-Wl,-rpath="+dir)
+ }
+ seenDirs[dir] = true
+ }
+ base = strings.TrimSuffix(base, ".so")
+ base = strings.TrimPrefix(base, "lib")
+ if !seenLibs[base] {
+ argv = append(argv, "-l"+base)
+ seenLibs[base] = true
+ }
+ }
+ for _, shlib := range ctxt.Shlibs {
+ addshlib(shlib.Path)
+ for _, dep := range shlib.Deps {
+ if dep == "" {
+ continue
+ }
+ libpath := findshlib(ctxt, dep)
+ if libpath != "" {
+ addshlib(libpath)
+ }
+ }
+ }
+ }
+
+ // clang, unlike GCC, passes -rdynamic to the linker
+ // even when linking with -static, causing a linker
+ // error when using GNU ld. So take out -rdynamic if
+ // we added it. We do it in this order, rather than
+ // only adding -rdynamic later, so that -extldflags
+ // can override -rdynamic without using -static.
+ // Similarly for -Wl,--dynamic-linker.
+ checkStatic := func(arg string) {
+ if ctxt.IsELF && arg == "-static" {
+ for i := range argv {
+ if argv[i] == "-rdynamic" || strings.HasPrefix(argv[i], "-Wl,--dynamic-linker,") {
+ argv[i] = "-static"
+ }
+ }
+ }
+ }
+
+ for _, p := range ldflag {
+ argv = append(argv, p)
+ checkStatic(p)
+ }
+
+ // When building a program with the default -buildmode=exe the
+ // gc compiler generates code requires DT_TEXTREL in a
+ // position independent executable (PIE). On systems where the
+ // toolchain creates PIEs by default, and where DT_TEXTREL
+ // does not work, the resulting programs will not run. See
+ // issue #17847. To avoid this problem pass -no-pie to the
+ // toolchain if it is supported.
+ if ctxt.BuildMode == BuildModeExe && !ctxt.linkShared && !(ctxt.IsDarwin() && ctxt.IsARM64()) {
+ // GCC uses -no-pie, clang uses -nopie.
+ for _, nopie := range []string{"-no-pie", "-nopie"} {
+ if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, nopie) {
+ argv = append(argv, nopie)
+ break
+ }
+ }
+ }
+
+ for _, p := range flagExtldflags {
+ argv = append(argv, p)
+ checkStatic(p)
+ }
+ if ctxt.HeadType == objabi.Hwindows {
+ // Determine which linker we're using. Add in the extldflags in
+ // case used has specified "-fuse-ld=...".
+ extld := ctxt.extld()
+ name, args := extld[0], extld[1:]
+ args = append(args, flagExtldflags...)
+ args = append(args, "-Wl,--version")
+ cmd := exec.Command(name, args...)
+ usingLLD := false
+ if out, err := cmd.CombinedOutput(); err == nil {
+ if bytes.Contains(out, []byte("LLD ")) {
+ usingLLD = true
+ }
+ }
+
+ // use gcc linker script to work around gcc bug
+ // (see https://golang.org/issue/20183 for details).
+ if !usingLLD {
+ p := writeGDBLinkerScript()
+ argv = append(argv, "-Wl,-T,"+p)
+ }
+ if *flagRace {
+ if p := ctxt.findLibPath("libsynchronization.a"); p != "libsynchronization.a" {
+ argv = append(argv, "-lsynchronization")
+ }
+ }
+ // libmingw32 and libmingwex have some inter-dependencies,
+ // so must use linker groups.
+ argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group")
+ argv = append(argv, peimporteddlls()...)
+ }
+
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("host link:")
+ for _, v := range argv {
+ ctxt.Logf(" %q", v)
+ }
+ ctxt.Logf("\n")
+ }
+
+ out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput()
+ if err != nil {
+ Exitf("running %s failed: %v\n%s", argv[0], err, out)
+ }
+
+ // Filter out useless linker warnings caused by bugs outside Go.
+ // See also cmd/go/internal/work/exec.go's gccld method.
+ var save [][]byte
+ var skipLines int
+ for _, line := range bytes.SplitAfter(out, []byte("\n")) {
+ // golang.org/issue/26073 - Apple Xcode bug
+ if bytes.Contains(line, []byte("ld: warning: text-based stub file")) {
+ continue
+ }
+
+ if skipLines > 0 {
+ skipLines--
+ continue
+ }
+
+ // Remove TOC overflow warning on AIX.
+ if bytes.Contains(line, []byte("ld: 0711-783")) {
+ skipLines = 2
+ continue
+ }
+
+ save = append(save, line)
+ }
+ out = bytes.Join(save, nil)
+
+ if len(out) > 0 {
+ // always print external output even if the command is successful, so that we don't
+ // swallow linker warnings (see https://golang.org/issue/17935).
+ if ctxt.IsDarwin() && ctxt.IsAMD64() {
+ const noPieWarning = "ld: warning: -no_pie is deprecated when targeting new OS versions\n"
+ if i := bytes.Index(out, []byte(noPieWarning)); i >= 0 {
+ // swallow -no_pie deprecation warning, issue 54482
+ out = append(out[:i], out[i+len(noPieWarning):]...)
+ }
+ }
+ if ctxt.IsDarwin() {
+ const bindAtLoadWarning = "ld: warning: -bind_at_load is deprecated on macOS\n"
+ if i := bytes.Index(out, []byte(bindAtLoadWarning)); i >= 0 {
+ // -bind_at_load is deprecated with ld-prime, but needed for
+ // correctness with older versions of ld64. Swallow the warning.
+ // TODO: maybe pass -bind_at_load conditionally based on C
+ // linker version.
+ out = append(out[:i], out[i+len(bindAtLoadWarning):]...)
+ }
+ }
+ ctxt.Logf("%s", out)
+ }
+
+ if combineDwarf {
+ // Find "dsymutils" and "strip" tools using CC --print-prog-name.
+ var cc []string
+ cc = append(cc, ctxt.extld()...)
+ cc = append(cc, hostlinkArchArgs(ctxt.Arch)...)
+ cc = append(cc, "--print-prog-name", "dsymutil")
+ out, err := exec.Command(cc[0], cc[1:]...).CombinedOutput()
+ if err != nil {
+ Exitf("%s: finding dsymutil failed: %v\n%s", os.Args[0], err, out)
+ }
+ dsymutilCmd := strings.TrimSuffix(string(out), "\n")
+
+ cc[len(cc)-1] = "strip"
+ out, err = exec.Command(cc[0], cc[1:]...).CombinedOutput()
+ if err != nil {
+ Exitf("%s: finding strip failed: %v\n%s", os.Args[0], err, out)
+ }
+ stripCmd := strings.TrimSuffix(string(out), "\n")
+
+ dsym := filepath.Join(*flagTmpdir, "go.dwarf")
+ if out, err := exec.Command(dsymutilCmd, "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil {
+ Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out)
+ }
+ // Remove STAB (symbolic debugging) symbols after we are done with them (by dsymutil).
+ // They contain temporary file paths and make the build not reproducible.
+ if out, err := exec.Command(stripCmd, "-S", *flagOutfile).CombinedOutput(); err != nil {
+ Exitf("%s: running strip failed: %v\n%s", os.Args[0], err, out)
+ }
+ // Skip combining if `dsymutil` didn't generate a file. See #11994.
+ if _, err := os.Stat(dsym); os.IsNotExist(err) {
+ return
+ }
+ // For os.Rename to work reliably, must be in same directory as outfile.
+ combinedOutput := *flagOutfile + "~"
+ exef, err := os.Open(*flagOutfile)
+ if err != nil {
+ Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
+ }
+ defer exef.Close()
+ exem, err := macho.NewFile(exef)
+ if err != nil {
+ Exitf("%s: parsing Mach-O header failed: %v", os.Args[0], err)
+ }
+ if err := machoCombineDwarf(ctxt, exef, exem, dsym, combinedOutput); err != nil {
+ Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
+ }
+ os.Remove(*flagOutfile)
+ if err := os.Rename(combinedOutput, *flagOutfile); err != nil {
+ Exitf("%s: %v", os.Args[0], err)
+ }
+ }
+ if ctxt.NeedCodeSign() {
+ err := machoCodeSign(ctxt, *flagOutfile)
+ if err != nil {
+ Exitf("%s: code signing failed: %v", os.Args[0], err)
+ }
+ }
+}
+
+var createTrivialCOnce sync.Once
+
+func linkerFlagSupported(arch *sys.Arch, linker, altLinker, flag string) bool {
+ createTrivialCOnce.Do(func() {
+ src := filepath.Join(*flagTmpdir, "trivial.c")
+ if err := os.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil {
+ Errorf(nil, "WriteFile trivial.c failed: %v", err)
+ }
+ })
+
+ flagsWithNextArgSkip := []string{
+ "-F",
+ "-l",
+ "-L",
+ "-framework",
+ "-Wl,-framework",
+ "-Wl,-rpath",
+ "-Wl,-undefined",
+ }
+ flagsWithNextArgKeep := []string{
+ "-arch",
+ "-isysroot",
+ "--sysroot",
+ "-target",
+ }
+ prefixesToKeep := []string{
+ "-f",
+ "-m",
+ "-p",
+ "-Wl,",
+ "-arch",
+ "-isysroot",
+ "--sysroot",
+ "-target",
+ }
+
+ flags := hostlinkArchArgs(arch)
+ keep := false
+ skip := false
+ for _, f := range append(flagExtldflags, ldflag...) {
+ if keep {
+ flags = append(flags, f)
+ keep = false
+ } else if skip {
+ skip = false
+ } else if f == "" || f[0] != '-' {
+ } else if contains(flagsWithNextArgSkip, f) {
+ skip = true
+ } else if contains(flagsWithNextArgKeep, f) {
+ flags = append(flags, f)
+ keep = true
+ } else {
+ for _, p := range prefixesToKeep {
+ if strings.HasPrefix(f, p) {
+ flags = append(flags, f)
+ break
+ }
+ }
+ }
+ }
+
+ if altLinker != "" {
+ flags = append(flags, "-fuse-ld="+altLinker)
+ }
+ flags = append(flags, flag, "trivial.c")
+
+ cmd := exec.Command(linker, flags...)
+ cmd.Dir = *flagTmpdir
+ cmd.Env = append([]string{"LC_ALL=C"}, os.Environ()...)
+ out, err := cmd.CombinedOutput()
+ // GCC says "unrecognized command line option ‘-no-pie’"
+ // clang says "unknown argument: '-no-pie'"
+ return err == nil && !bytes.Contains(out, []byte("unrecognized")) && !bytes.Contains(out, []byte("unknown"))
+}
+
+// hostlinkArchArgs returns arguments to pass to the external linker
+// based on the architecture.
+func hostlinkArchArgs(arch *sys.Arch) []string {
+ switch arch.Family {
+ case sys.I386:
+ return []string{"-m32"}
+ case sys.AMD64:
+ if buildcfg.GOOS == "darwin" {
+ return []string{"-arch", "x86_64", "-m64"}
+ }
+ return []string{"-m64"}
+ case sys.S390X:
+ return []string{"-m64"}
+ case sys.ARM:
+ return []string{"-marm"}
+ case sys.ARM64:
+ if buildcfg.GOOS == "darwin" {
+ return []string{"-arch", "arm64"}
+ }
+ case sys.Loong64:
+ return []string{"-mabi=lp64d"}
+ case sys.MIPS64:
+ return []string{"-mabi=64"}
+ case sys.MIPS:
+ return []string{"-mabi=32"}
+ case sys.PPC64:
+ if buildcfg.GOOS == "aix" {
+ return []string{"-maix64"}
+ } else {
+ return []string{"-m64"}
+ }
+
+ }
+ return nil
+}
+
+var wantHdr = objabi.HeaderString()
+
+// ldobj loads an input object. If it is a host object (an object
+// compiled by a non-Go compiler) it returns the Hostobj pointer. If
+// it is a Go object, it returns nil.
+func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, file string) *Hostobj {
+ pkg := objabi.PathToPrefix(lib.Pkg)
+
+ eof := f.Offset() + length
+ start := f.Offset()
+ c1 := bgetc(f)
+ c2 := bgetc(f)
+ c3 := bgetc(f)
+ c4 := bgetc(f)
+ f.MustSeek(start, 0)
+
+ unit := &sym.CompilationUnit{Lib: lib}
+ lib.Units = append(lib.Units, unit)
+
+ magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4)
+ if magic == 0x7f454c46 { // \x7F E L F
+ ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn, ehdr.Flags)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ehdr.Flags = flags
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
+ if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe {
+ ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
+ switch c1<<8 | c2 {
+ case 0x4c01, // 386
+ 0x6486, // amd64
+ 0xc401, // arm
+ 0x64aa: // arm64
+ ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ if len(rsrc) != 0 {
+ setpersrc(ctxt, rsrc)
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
+ if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) {
+ ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
+ if c1 != 'g' || c2 != 'o' || c3 != ' ' || c4 != 'o' {
+ // An unrecognized object is just passed to the external linker.
+ // If we try to read symbols from this object, we will
+ // report an error at that time.
+ unknownObjFormat = true
+ return ldhostobj(nil, ctxt.HeadType, f, pkg, length, pn, file)
+ }
+
+ /* check the header */
+ line, err := f.ReadString('\n')
+ if err != nil {
+ Errorf(nil, "truncated object file: %s: %v", pn, err)
+ return nil
+ }
+
+ if !strings.HasPrefix(line, "go object ") {
+ if strings.HasSuffix(pn, ".go") {
+ Exitf("%s: uncompiled .go source file", pn)
+ return nil
+ }
+
+ if line == ctxt.Arch.Name {
+ // old header format: just $GOOS
+ Errorf(nil, "%s: stale object file", pn)
+ return nil
+ }
+
+ Errorf(nil, "%s: not an object file: @%d %q", pn, start, line)
+ return nil
+ }
+
+ // First, check that the basic GOOS, GOARCH, and Version match.
+ if line != wantHdr {
+ Errorf(nil, "%s: linked object header mismatch:\nhave %q\nwant %q\n", pn, line, wantHdr)
+ }
+
+ // Skip over exports and other info -- ends with \n!\n.
+ //
+ // Note: It's possible for "\n!\n" to appear within the binary
+ // package export data format. To avoid truncating the package
+ // definition prematurely (issue 21703), we keep track of
+ // how many "$$" delimiters we've seen.
+
+ import0 := f.Offset()
+
+ c1 = '\n' // the last line ended in \n
+ c2 = bgetc(f)
+ c3 = bgetc(f)
+ markers := 0
+ for {
+ if c1 == '\n' {
+ if markers%2 == 0 && c2 == '!' && c3 == '\n' {
+ break
+ }
+ if c2 == '$' && c3 == '$' {
+ markers++
+ }
+ }
+
+ c1 = c2
+ c2 = c3
+ c3 = bgetc(f)
+ if c3 == -1 {
+ Errorf(nil, "truncated object file: %s", pn)
+ return nil
+ }
+ }
+
+ import1 := f.Offset()
+
+ f.MustSeek(import0, 0)
+ ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n
+ f.MustSeek(import1, 0)
+
+ fingerprint := ctxt.loader.Preload(ctxt.IncVersion(), f, lib, unit, eof-f.Offset())
+ if !fingerprint.IsZero() { // Assembly objects don't have fingerprints. Ignore them.
+ // Check fingerprint, to ensure the importing and imported packages
+ // have consistent view of symbol indices.
+ // Normally the go command should ensure this. But in case something
+ // goes wrong, it could lead to obscure bugs like run-time crash.
+ // Check it here to be sure.
+ if lib.Fingerprint.IsZero() { // Not yet imported. Update its fingerprint.
+ lib.Fingerprint = fingerprint
+ }
+ checkFingerprint(lib, fingerprint, lib.Srcref, lib.Fingerprint)
+ }
+
+ addImports(ctxt, lib, pn)
+ 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)
+ }
+ h.file = path
+ h.length = f.MustSeek(0, 2)
+ f.MustSeek(h.off, 0)
+ h.ld(ctxt, f, h.pkg, h.length, h.pn)
+ if *flagCaptureHostObjs != "" {
+ captureHostObj(h)
+ }
+}
+
+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)
+ }
+}
+
+func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte {
+ data := make([]byte, sym.Size)
+ sect := f.Sections[sym.Section]
+ if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE {
+ Errorf(nil, "reading %s from non-data section", sym.Name)
+ }
+ n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr))
+ if uint64(n) != sym.Size {
+ Errorf(nil, "reading contents of %s: %v", sym.Name, err)
+ }
+ return data
+}
+
+func readwithpad(r io.Reader, sz int32) ([]byte, error) {
+ data := make([]byte, Rnd(int64(sz), 4))
+ _, err := io.ReadFull(r, data)
+ if err != nil {
+ return nil, err
+ }
+ data = data[:sz]
+ return data, nil
+}
+
+func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) {
+ for _, sect := range f.Sections {
+ if sect.Type != elf.SHT_NOTE {
+ continue
+ }
+ r := sect.Open()
+ for {
+ var namesize, descsize, noteType int32
+ err := binary.Read(r, f.ByteOrder, &namesize)
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, fmt.Errorf("read namesize failed: %v", err)
+ }
+ err = binary.Read(r, f.ByteOrder, &descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read descsize failed: %v", err)
+ }
+ err = binary.Read(r, f.ByteOrder, &noteType)
+ if err != nil {
+ return nil, fmt.Errorf("read type failed: %v", err)
+ }
+ noteName, err := readwithpad(r, namesize)
+ if err != nil {
+ return nil, fmt.Errorf("read name failed: %v", err)
+ }
+ desc, err := readwithpad(r, descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read desc failed: %v", err)
+ }
+ if string(name) == string(noteName) && typ == noteType {
+ return desc, nil
+ }
+ }
+ }
+ return nil, nil
+}
+
+func findshlib(ctxt *Link, shlib string) string {
+ if filepath.IsAbs(shlib) {
+ return shlib
+ }
+ for _, libdir := range ctxt.Libdir {
+ libpath := filepath.Join(libdir, shlib)
+ if _, err := os.Stat(libpath); err == nil {
+ return libpath
+ }
+ }
+ Errorf(nil, "cannot find shared library: %s", shlib)
+ return ""
+}
+
+func ldshlibsyms(ctxt *Link, shlib string) {
+ var libpath string
+ if filepath.IsAbs(shlib) {
+ libpath = shlib
+ shlib = filepath.Base(shlib)
+ } else {
+ libpath = findshlib(ctxt, shlib)
+ if libpath == "" {
+ return
+ }
+ }
+ for _, processedlib := range ctxt.Shlibs {
+ if processedlib.Path == libpath {
+ return
+ }
+ }
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("ldshlibsyms: found library with name %s at %s\n", shlib, libpath)
+ }
+
+ f, err := elf.Open(libpath)
+ if err != nil {
+ Errorf(nil, "cannot open shared library: %s", libpath)
+ return
+ }
+ // Keep the file open as decodetypeGcprog needs to read from it.
+ // TODO: fix. Maybe mmap the file.
+ //defer f.Close()
+
+ hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG)
+ if err != nil {
+ Errorf(nil, "cannot read ABI hash from shared library %s: %v", libpath, err)
+ return
+ }
+
+ depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG)
+ if err != nil {
+ Errorf(nil, "cannot read dep list from shared library %s: %v", libpath, err)
+ return
+ }
+ var deps []string
+ for _, dep := range strings.Split(string(depsbytes), "\n") {
+ if dep == "" {
+ continue
+ }
+ if !filepath.IsAbs(dep) {
+ // If the dep can be interpreted as a path relative to the shlib
+ // in which it was found, do that. Otherwise, we will leave it
+ // to be resolved by libdir lookup.
+ abs := filepath.Join(filepath.Dir(libpath), dep)
+ if _, err := os.Stat(abs); err == nil {
+ dep = abs
+ }
+ }
+ deps = append(deps, dep)
+ }
+
+ syms, err := f.DynamicSymbols()
+ if err != nil {
+ Errorf(nil, "cannot read symbols from shared library: %s", libpath)
+ return
+ }
+
+ for _, elfsym := range syms {
+ if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION {
+ continue
+ }
+
+ // Symbols whose names start with "type:" are compiler generated,
+ // so make functions with that prefix internal.
+ ver := 0
+ symname := elfsym.Name // (unmangled) symbol name
+ if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type:") {
+ ver = abiInternalVer
+ } else if buildcfg.Experiment.RegabiWrappers && elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC {
+ // Demangle the ABI name. Keep in sync with symtab.go:mangleABIName.
+ if strings.HasSuffix(elfsym.Name, ".abiinternal") {
+ ver = sym.SymVerABIInternal
+ symname = strings.TrimSuffix(elfsym.Name, ".abiinternal")
+ } else if strings.HasSuffix(elfsym.Name, ".abi0") {
+ ver = 0
+ symname = strings.TrimSuffix(elfsym.Name, ".abi0")
+ }
+ }
+
+ l := ctxt.loader
+ s := l.LookupOrCreateSym(symname, ver)
+
+ // Because loadlib above loads all .a files before loading
+ // any shared libraries, any non-dynimport symbols we find
+ // that duplicate symbols already loaded should be ignored
+ // (the symbols from the .a files "win").
+ if l.SymType(s) != 0 && l.SymType(s) != sym.SDYNIMPORT {
+ continue
+ }
+ su := l.MakeSymbolUpdater(s)
+ su.SetType(sym.SDYNIMPORT)
+ l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info))
+ su.SetSize(int64(elfsym.Size))
+ if elfsym.Section != elf.SHN_UNDEF {
+ // Set .File for the library that actually defines the symbol.
+ l.SetSymPkg(s, libpath)
+
+ // The decodetype_* functions in decodetype.go need access to
+ // the type data.
+ sname := l.SymName(s)
+ if strings.HasPrefix(sname, "type:") && !strings.HasPrefix(sname, "type:.") {
+ su.SetData(readelfsymboldata(ctxt, f, &elfsym))
+ }
+ }
+
+ if symname != elfsym.Name {
+ l.SetSymExtname(s, elfsym.Name)
+ }
+ }
+ ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
+}
+
+func addsection(ldr *loader.Loader, arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section {
+ sect := ldr.NewSection()
+ sect.Rwx = uint8(rwx)
+ sect.Name = name
+ sect.Seg = seg
+ sect.Align = int32(arch.PtrSize) // everything is at least pointer-aligned
+ seg.Sections = append(seg.Sections, sect)
+ return sect
+}
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n")
+ objabi.Flagprint(os.Stderr)
+ Exit(2)
+}
+
+type SymbolType int8 // TODO: after genasmsym is gone, maybe rename to plan9typeChar or something
+
+const (
+ // see also https://9p.io/magic/man2html/1/nm
+ TextSym SymbolType = 'T'
+ DataSym SymbolType = 'D'
+ BSSSym SymbolType = 'B'
+ UndefinedSym SymbolType = 'U'
+ TLSSym SymbolType = 't'
+ FrameSym SymbolType = 'm'
+ ParamSym SymbolType = 'p'
+ AutoSym SymbolType = 'a'
+
+ // Deleted auto (not a real sym, just placeholder for type)
+ DeletedAutoSym = 'x'
+)
+
+// defineInternal defines a symbol used internally by the go runtime.
+func (ctxt *Link) defineInternal(p string, t sym.SymKind) loader.Sym {
+ s := ctxt.loader.CreateSymForUpdate(p, 0)
+ s.SetType(t)
+ s.SetSpecial(true)
+ s.SetLocal(true)
+ return s.Sym()
+}
+
+func (ctxt *Link) xdefine(p string, t sym.SymKind, v int64) loader.Sym {
+ s := ctxt.defineInternal(p, t)
+ ctxt.loader.SetSymValue(s, v)
+ return s
+}
+
+func datoff(ldr *loader.Loader, s loader.Sym, addr int64) int64 {
+ if uint64(addr) >= Segdata.Vaddr {
+ return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff)
+ }
+ if uint64(addr) >= Segtext.Vaddr {
+ return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff)
+ }
+ ldr.Errorf(s, "invalid datoff %#x", addr)
+ return 0
+}
+
+func Entryvalue(ctxt *Link) int64 {
+ a := *flagEntrySymbol
+ if a[0] >= '0' && a[0] <= '9' {
+ return atolwhex(a)
+ }
+ ldr := ctxt.loader
+ s := ldr.Lookup(a, 0)
+ if s == 0 {
+ Errorf(nil, "missing entry symbol %q", a)
+ return 0
+ }
+ st := ldr.SymType(s)
+ if st == 0 {
+ return *FlagTextAddr
+ }
+ if !ctxt.IsAIX() && st != sym.STEXT {
+ ldr.Errorf(s, "entry not text")
+ }
+ return ldr.SymValue(s)
+}
+
+func (ctxt *Link) callgraph() {
+ if !*FlagC {
+ return
+ }
+
+ ldr := ctxt.loader
+ for _, s := range ctxt.Textp {
+ relocs := ldr.Relocs(s)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At(i)
+ rs := r.Sym()
+ if rs == 0 {
+ continue
+ }
+ if r.Type().IsDirectCall() && ldr.SymType(rs) == sym.STEXT {
+ ctxt.Logf("%s calls %s\n", ldr.SymName(s), ldr.SymName(rs))
+ }
+ }
+ }
+}
+
+func Rnd(v int64, r int64) int64 {
+ if r <= 0 {
+ return v
+ }
+ v += r - 1
+ c := v % r
+ if c < 0 {
+ c += r
+ }
+ v -= c
+ return v
+}
+
+func bgetc(r *bio.Reader) int {
+ c, err := r.ReadByte()
+ if err != nil {
+ if err != io.EOF {
+ log.Fatalf("reading input: %v", err)
+ }
+ return -1
+ }
+ return int(c)
+}
+
+type markKind uint8 // for postorder traversal
+const (
+ _ markKind = iota
+ visiting
+ visited
+)
+
+func postorder(libs []*sym.Library) []*sym.Library {
+ order := make([]*sym.Library, 0, len(libs)) // hold the result
+ mark := make(map[*sym.Library]markKind, len(libs))
+ for _, lib := range libs {
+ dfs(lib, mark, &order)
+ }
+ return order
+}
+
+func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library) {
+ if mark[lib] == visited {
+ return
+ }
+ if mark[lib] == visiting {
+ panic("found import cycle while visiting " + lib.Pkg)
+ }
+ mark[lib] = visiting
+ for _, i := range lib.Imports {
+ dfs(i, mark, order)
+ }
+ mark[lib] = visited
+ *order = append(*order, lib)
+}
+
+func ElfSymForReloc(ctxt *Link, s loader.Sym) int32 {
+ // If putelfsym created a local version of this symbol, use that in all
+ // relocations.
+ les := ctxt.loader.SymLocalElfSym(s)
+ if les != 0 {
+ return les
+ } else {
+ return ctxt.loader.SymElfSym(s)
+ }
+}
+
+func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, elfRelocTyp uint32) {
+ if ldr.SymGot(s) >= 0 {
+ return
+ }
+
+ Adddynsym(ldr, target, syms, s)
+ got := ldr.MakeSymbolUpdater(syms.GOT)
+ ldr.SetGot(s, int32(got.Size()))
+ got.AddUint(target.Arch, 0)
+
+ if target.IsElf() {
+ if target.Arch.PtrSize == 8 {
+ rela := ldr.MakeSymbolUpdater(syms.Rela)
+ rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp))
+ rela.AddUint64(target.Arch, 0)
+ } else {
+ rel := ldr.MakeSymbolUpdater(syms.Rel)
+ rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), elfRelocTyp))
+ }
+ } else if target.IsDarwin() {
+ leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT)
+ leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s)))
+ if target.IsPIE() && target.IsInternal() {
+ // Mach-O relocations are a royal pain to lay out.
+ // They use a compact stateful bytecode representation.
+ // Here we record what are needed and encode them later.
+ MachoAddBind(int64(ldr.SymGot(s)), s)
+ }
+ } else {
+ ldr.Errorf(s, "addgotsym: unsupported binary format")
+ }
+}
+
+var hostobjcounter int
+
+// captureHostObj writes out the content of a host object (pulled from
+// an archive or loaded from a *.o file directly) to a directory
+// specified via the linker's "-capturehostobjs" debugging flag. This
+// is intended to make it easier for a developer to inspect the actual
+// object feeding into "CGO internal" link step.
+func captureHostObj(h *Hostobj) {
+ // Form paths for info file and obj file.
+ ofile := fmt.Sprintf("captured-obj-%d.o", hostobjcounter)
+ ifile := fmt.Sprintf("captured-obj-%d.txt", hostobjcounter)
+ hostobjcounter++
+ opath := filepath.Join(*flagCaptureHostObjs, ofile)
+ ipath := filepath.Join(*flagCaptureHostObjs, ifile)
+
+ // Write the info file.
+ info := fmt.Sprintf("pkg: %s\npn: %s\nfile: %s\noff: %d\nlen: %d\n",
+ h.pkg, h.pn, h.file, h.off, h.length)
+ if err := os.WriteFile(ipath, []byte(info), 0666); err != nil {
+ log.Fatalf("error writing captured host obj info %s: %v", ipath, err)
+ }
+
+ readObjData := func() []byte {
+ inf, err := os.Open(h.file)
+ if err != nil {
+ log.Fatalf("capturing host obj: open failed on %s: %v", h.pn, err)
+ }
+ res := make([]byte, h.length)
+ if n, err := inf.ReadAt(res, h.off); err != nil || n != int(h.length) {
+ log.Fatalf("capturing host obj: readat failed on %s: %v", h.pn, err)
+ }
+ return res
+ }
+
+ // Write the object file.
+ if err := os.WriteFile(opath, readObjData(), 0666); err != nil {
+ log.Fatalf("error writing captured host object %s: %v", opath, err)
+ }
+
+ fmt.Fprintf(os.Stderr, "link: info: captured host object %s to %s\n",
+ h.file, opath)
+}
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
new file mode 100644
index 0000000..34221df
--- /dev/null
+++ b/src/cmd/link/internal/ld/link.go
@@ -0,0 +1,161 @@
+// Derived from Inferno utils/6l/l.h and related files.
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "bufio"
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+)
+
+type Shlib struct {
+ Path string
+ Hash []byte
+ Deps []string
+ File *elf.File
+}
+
+// Link holds the context for writing object code from a compiler
+// or for reading that input into the linker.
+type Link struct {
+ Target
+ ErrorReporter
+ ArchSyms
+
+ outSem chan int // limits the number of output writers
+ Out *OutBuf
+
+ version int // current version number for static/file-local symbols
+
+ Debugvlog int
+ Bso *bufio.Writer
+
+ Loaded bool // set after all inputs have been loaded as symbols
+
+ compressDWARF bool
+
+ Libdir []string
+ Library []*sym.Library
+ LibraryByPkg map[string]*sym.Library
+ Shlibs []Shlib
+ Textp []loader.Sym
+ Moduledata loader.Sym
+
+ PackageFile map[string]string
+ PackageShlib map[string]string
+
+ tramps []loader.Sym // trampolines
+
+ compUnits []*sym.CompilationUnit // DWARF compilation units
+ runtimeCU *sym.CompilationUnit // One of the runtime CUs, the last one seen.
+
+ loader *loader.Loader
+ cgodata []cgodata // cgo directives to load, three strings are args for loadcgo
+
+ datap []loader.Sym
+ dynexp []loader.Sym
+
+ // Elf symtab variables.
+ numelfsym int // starts at 0, 1 is reserved
+
+ // These are symbols that created and written by the linker.
+ // Rather than creating a symbol, and writing all its data into the heap,
+ // you can create a symbol, and just a generation function will be called
+ // after the symbol's been created in the output mmap.
+ generatorSyms map[loader.Sym]generatorFunc
+}
+
+type cgodata struct {
+ file string
+ pkg string
+ directives [][]string
+}
+
+func (ctxt *Link) Logf(format string, args ...interface{}) {
+ fmt.Fprintf(ctxt.Bso, format, args...)
+ ctxt.Bso.Flush()
+}
+
+func addImports(ctxt *Link, l *sym.Library, pn string) {
+ pkg := objabi.PathToPrefix(l.Pkg)
+ for _, imp := range l.Autolib {
+ lib := addlib(ctxt, pkg, pn, imp.Pkg, imp.Fingerprint)
+ if lib != nil {
+ l.Imports = append(l.Imports, lib)
+ }
+ }
+ l.Autolib = nil
+}
+
+// Allocate a new version (i.e. symbol namespace).
+func (ctxt *Link) IncVersion() int {
+ ctxt.version++
+ return ctxt.version - 1
+}
+
+// returns the maximum version number
+func (ctxt *Link) MaxVersion() int {
+ return ctxt.version
+}
+
+// generatorFunc is a convenience type.
+// Some linker-created Symbols are large and shouldn't really live in the heap.
+// Such Symbols can define a generator function. Their bytes can be generated
+// directly in the output mmap.
+//
+// Relocations are applied prior to emitting generator Symbol contents.
+// Generator Symbols that require relocations can be written in two passes.
+// The first pass, at Symbol creation time, adds only relocations.
+// The second pass, at content generation time, adds the rest.
+// See generateFunctab for an example.
+//
+// Generator functions shouldn't grow the Symbol size.
+// Generator functions must be safe for concurrent use.
+//
+// Generator Symbols have their Data set to the mmapped area when the
+// generator is called.
+type generatorFunc func(*Link, loader.Sym)
+
+// createGeneratorSymbol is a convenience method for creating a generator
+// symbol.
+func (ctxt *Link) createGeneratorSymbol(name string, version int, t sym.SymKind, size int64, gen generatorFunc) loader.Sym {
+ ldr := ctxt.loader
+ s := ldr.LookupOrCreateSym(name, version)
+ ldr.SetIsGeneratedSym(s, true)
+ sb := ldr.MakeSymbolUpdater(s)
+ sb.SetType(t)
+ sb.SetSize(size)
+ ctxt.generatorSyms[s] = gen
+ return s
+}
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
new file mode 100644
index 0000000..d6c28e4
--- /dev/null
+++ b/src/cmd/link/internal/ld/macho.go
@@ -0,0 +1,1568 @@
+// Copyright 2009 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.
+
+package ld
+
+import (
+ "bytes"
+ "cmd/internal/codesign"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/macho"
+ "encoding/binary"
+ "fmt"
+ "internal/buildcfg"
+ "io"
+ "os"
+ "sort"
+ "strings"
+ "unsafe"
+)
+
+type MachoHdr struct {
+ cpu uint32
+ subcpu uint32
+}
+
+type MachoSect struct {
+ name string
+ segname string
+ addr uint64
+ size uint64
+ off uint32
+ align uint32
+ reloc uint32
+ nreloc uint32
+ flag uint32
+ res1 uint32
+ res2 uint32
+}
+
+type MachoSeg struct {
+ name string
+ vsize uint64
+ vaddr uint64
+ fileoffset uint64
+ filesize uint64
+ prot1 uint32
+ prot2 uint32
+ nsect uint32
+ msect uint32
+ sect []MachoSect
+ flag uint32
+}
+
+// MachoPlatformLoad represents a LC_VERSION_MIN_* or
+// LC_BUILD_VERSION load command.
+type MachoPlatformLoad struct {
+ platform MachoPlatform // One of PLATFORM_* constants.
+ cmd MachoLoad
+}
+
+type MachoLoad struct {
+ type_ uint32
+ data []uint32
+}
+
+type MachoPlatform int
+
+/*
+ * Total amount of space to reserve at the start of the file
+ * for Header, PHeaders, and SHeaders.
+ * May waste some.
+ */
+const (
+ INITIAL_MACHO_HEADR = 4 * 1024
+)
+
+const (
+ MACHO_CPU_AMD64 = 1<<24 | 7
+ MACHO_CPU_386 = 7
+ MACHO_SUBCPU_X86 = 3
+ MACHO_CPU_ARM = 12
+ MACHO_SUBCPU_ARM = 0
+ MACHO_SUBCPU_ARMV7 = 9
+ MACHO_CPU_ARM64 = 1<<24 | 12
+ MACHO_SUBCPU_ARM64_ALL = 0
+ MACHO_SUBCPU_ARM64_V8 = 1
+ MACHO_SUBCPU_ARM64E = 2
+ MACHO32SYMSIZE = 12
+ MACHO64SYMSIZE = 16
+ MACHO_X86_64_RELOC_UNSIGNED = 0
+ MACHO_X86_64_RELOC_SIGNED = 1
+ MACHO_X86_64_RELOC_BRANCH = 2
+ MACHO_X86_64_RELOC_GOT_LOAD = 3
+ MACHO_X86_64_RELOC_GOT = 4
+ MACHO_X86_64_RELOC_SUBTRACTOR = 5
+ MACHO_X86_64_RELOC_SIGNED_1 = 6
+ MACHO_X86_64_RELOC_SIGNED_2 = 7
+ MACHO_X86_64_RELOC_SIGNED_4 = 8
+ MACHO_ARM_RELOC_VANILLA = 0
+ MACHO_ARM_RELOC_PAIR = 1
+ MACHO_ARM_RELOC_SECTDIFF = 2
+ MACHO_ARM_RELOC_BR24 = 5
+ MACHO_ARM64_RELOC_UNSIGNED = 0
+ MACHO_ARM64_RELOC_BRANCH26 = 2
+ MACHO_ARM64_RELOC_PAGE21 = 3
+ MACHO_ARM64_RELOC_PAGEOFF12 = 4
+ MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5
+ MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6
+ MACHO_ARM64_RELOC_ADDEND = 10
+ MACHO_GENERIC_RELOC_VANILLA = 0
+ MACHO_FAKE_GOTPCREL = 100
+)
+
+const (
+ MH_MAGIC = 0xfeedface
+ MH_MAGIC_64 = 0xfeedfacf
+
+ MH_OBJECT = 0x1
+ MH_EXECUTE = 0x2
+
+ MH_NOUNDEFS = 0x1
+ MH_DYLDLINK = 0x4
+ MH_PIE = 0x200000
+)
+
+const (
+ LC_SEGMENT = 0x1
+ LC_SYMTAB = 0x2
+ LC_SYMSEG = 0x3
+ LC_THREAD = 0x4
+ LC_UNIXTHREAD = 0x5
+ LC_LOADFVMLIB = 0x6
+ LC_IDFVMLIB = 0x7
+ LC_IDENT = 0x8
+ LC_FVMFILE = 0x9
+ LC_PREPAGE = 0xa
+ LC_DYSYMTAB = 0xb
+ LC_LOAD_DYLIB = 0xc
+ LC_ID_DYLIB = 0xd
+ LC_LOAD_DYLINKER = 0xe
+ LC_ID_DYLINKER = 0xf
+ LC_PREBOUND_DYLIB = 0x10
+ LC_ROUTINES = 0x11
+ LC_SUB_FRAMEWORK = 0x12
+ LC_SUB_UMBRELLA = 0x13
+ LC_SUB_CLIENT = 0x14
+ LC_SUB_LIBRARY = 0x15
+ LC_TWOLEVEL_HINTS = 0x16
+ LC_PREBIND_CKSUM = 0x17
+ LC_LOAD_WEAK_DYLIB = 0x80000018
+ LC_SEGMENT_64 = 0x19
+ LC_ROUTINES_64 = 0x1a
+ LC_UUID = 0x1b
+ LC_RPATH = 0x8000001c
+ LC_CODE_SIGNATURE = 0x1d
+ LC_SEGMENT_SPLIT_INFO = 0x1e
+ LC_REEXPORT_DYLIB = 0x8000001f
+ LC_LAZY_LOAD_DYLIB = 0x20
+ LC_ENCRYPTION_INFO = 0x21
+ LC_DYLD_INFO = 0x22
+ LC_DYLD_INFO_ONLY = 0x80000022
+ LC_LOAD_UPWARD_DYLIB = 0x80000023
+ LC_VERSION_MIN_MACOSX = 0x24
+ LC_VERSION_MIN_IPHONEOS = 0x25
+ LC_FUNCTION_STARTS = 0x26
+ LC_DYLD_ENVIRONMENT = 0x27
+ LC_MAIN = 0x80000028
+ LC_DATA_IN_CODE = 0x29
+ LC_SOURCE_VERSION = 0x2A
+ LC_DYLIB_CODE_SIGN_DRS = 0x2B
+ LC_ENCRYPTION_INFO_64 = 0x2C
+ LC_LINKER_OPTION = 0x2D
+ LC_LINKER_OPTIMIZATION_HINT = 0x2E
+ LC_VERSION_MIN_TVOS = 0x2F
+ LC_VERSION_MIN_WATCHOS = 0x30
+ LC_VERSION_NOTE = 0x31
+ LC_BUILD_VERSION = 0x32
+ LC_DYLD_EXPORTS_TRIE = 0x80000033
+ LC_DYLD_CHAINED_FIXUPS = 0x80000034
+)
+
+const (
+ S_REGULAR = 0x0
+ S_ZEROFILL = 0x1
+ S_NON_LAZY_SYMBOL_POINTERS = 0x6
+ S_SYMBOL_STUBS = 0x8
+ S_MOD_INIT_FUNC_POINTERS = 0x9
+ S_ATTR_PURE_INSTRUCTIONS = 0x80000000
+ S_ATTR_DEBUG = 0x02000000
+ S_ATTR_SOME_INSTRUCTIONS = 0x00000400
+)
+
+const (
+ PLATFORM_MACOS MachoPlatform = 1
+ PLATFORM_IOS MachoPlatform = 2
+ PLATFORM_TVOS MachoPlatform = 3
+ PLATFORM_WATCHOS MachoPlatform = 4
+ PLATFORM_BRIDGEOS MachoPlatform = 5
+)
+
+// rebase table opcode
+const (
+ REBASE_TYPE_POINTER = 1
+ REBASE_TYPE_TEXT_ABSOLUTE32 = 2
+ REBASE_TYPE_TEXT_PCREL32 = 3
+
+ REBASE_OPCODE_MASK = 0xF0
+ REBASE_IMMEDIATE_MASK = 0x0F
+ REBASE_OPCODE_DONE = 0x00
+ REBASE_OPCODE_SET_TYPE_IMM = 0x10
+ REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20
+ REBASE_OPCODE_ADD_ADDR_ULEB = 0x30
+ REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40
+ REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50
+ REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60
+ REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70
+ REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80
+)
+
+// bind table opcode
+const (
+ BIND_TYPE_POINTER = 1
+ BIND_TYPE_TEXT_ABSOLUTE32 = 2
+ BIND_TYPE_TEXT_PCREL32 = 3
+
+ BIND_SPECIAL_DYLIB_SELF = 0
+ BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1
+ BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2
+ BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3
+
+ BIND_OPCODE_MASK = 0xF0
+ BIND_IMMEDIATE_MASK = 0x0F
+ BIND_OPCODE_DONE = 0x00
+ BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10
+ BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20
+ BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30
+ BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40
+ BIND_OPCODE_SET_TYPE_IMM = 0x50
+ BIND_OPCODE_SET_ADDEND_SLEB = 0x60
+ BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70
+ BIND_OPCODE_ADD_ADDR_ULEB = 0x80
+ BIND_OPCODE_DO_BIND = 0x90
+ BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0
+ BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0
+ BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0
+ BIND_OPCODE_THREADED = 0xD0
+ BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00
+ BIND_SUBOPCODE_THREADED_APPLY = 0x01
+)
+
+const machoHeaderSize64 = 8 * 4 // size of 64-bit Mach-O header
+
+// Mach-O file writing
+// https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
+
+var machohdr MachoHdr
+
+var load []MachoLoad
+
+var machoPlatform MachoPlatform
+
+var seg [16]MachoSeg
+
+var nseg int
+
+var ndebug int
+
+var nsect int
+
+const (
+ SymKindLocal = 0 + iota
+ SymKindExtdef
+ SymKindUndef
+ NumSymKind
+)
+
+var nkind [NumSymKind]int
+
+var sortsym []loader.Sym
+
+var nsortsym int
+
+// Amount of space left for adding load commands
+// that refer to dynamic libraries. Because these have
+// to go in the Mach-O header, we can't just pick a
+// "big enough" header size. The initial header is
+// one page, the non-dynamic library stuff takes
+// up about 1300 bytes; we overestimate that as 2k.
+var loadBudget = INITIAL_MACHO_HEADR - 2*1024
+
+func getMachoHdr() *MachoHdr {
+ return &machohdr
+}
+
+func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad {
+ if arch.PtrSize == 8 && (ndata&1 != 0) {
+ ndata++
+ }
+
+ load = append(load, MachoLoad{})
+ l := &load[len(load)-1]
+ l.type_ = type_
+ l.data = make([]uint32, ndata)
+ return l
+}
+
+func newMachoSeg(name string, msect int) *MachoSeg {
+ if nseg >= len(seg) {
+ Exitf("too many segs")
+ }
+
+ s := &seg[nseg]
+ nseg++
+ s.name = name
+ s.msect = uint32(msect)
+ s.sect = make([]MachoSect, msect)
+ return s
+}
+
+func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect {
+ if seg.nsect >= seg.msect {
+ Exitf("too many sects in segment %s", seg.name)
+ }
+
+ s := &seg.sect[seg.nsect]
+ seg.nsect++
+ s.name = name
+ s.segname = segname
+ nsect++
+ return s
+}
+
+// Generic linking code.
+
+var dylib []string
+
+var linkoff int64
+
+func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int {
+ o1 := out.Offset()
+
+ loadsize := 4 * 4 * ndebug
+ for i := range load {
+ loadsize += 4 * (len(load[i].data) + 2)
+ }
+ if arch.PtrSize == 8 {
+ loadsize += 18 * 4 * nseg
+ loadsize += 20 * 4 * nsect
+ } else {
+ loadsize += 14 * 4 * nseg
+ loadsize += 17 * 4 * nsect
+ }
+
+ if arch.PtrSize == 8 {
+ out.Write32(MH_MAGIC_64)
+ } else {
+ out.Write32(MH_MAGIC)
+ }
+ out.Write32(machohdr.cpu)
+ out.Write32(machohdr.subcpu)
+ if linkmode == LinkExternal {
+ out.Write32(MH_OBJECT) /* file type - mach object */
+ } else {
+ out.Write32(MH_EXECUTE) /* file type - mach executable */
+ }
+ out.Write32(uint32(len(load)) + uint32(nseg) + uint32(ndebug))
+ out.Write32(uint32(loadsize))
+ flags := uint32(0)
+ if nkind[SymKindUndef] == 0 {
+ flags |= MH_NOUNDEFS
+ }
+ if ctxt.IsPIE() && linkmode == LinkInternal {
+ flags |= MH_PIE | MH_DYLDLINK
+ }
+ out.Write32(flags) /* flags */
+ if arch.PtrSize == 8 {
+ out.Write32(0) /* reserved */
+ }
+
+ for i := 0; i < nseg; i++ {
+ s := &seg[i]
+ if arch.PtrSize == 8 {
+ out.Write32(LC_SEGMENT_64)
+ out.Write32(72 + 80*s.nsect)
+ out.WriteStringN(s.name, 16)
+ out.Write64(s.vaddr)
+ out.Write64(s.vsize)
+ out.Write64(s.fileoffset)
+ out.Write64(s.filesize)
+ out.Write32(s.prot1)
+ out.Write32(s.prot2)
+ out.Write32(s.nsect)
+ out.Write32(s.flag)
+ } else {
+ out.Write32(LC_SEGMENT)
+ out.Write32(56 + 68*s.nsect)
+ out.WriteStringN(s.name, 16)
+ out.Write32(uint32(s.vaddr))
+ out.Write32(uint32(s.vsize))
+ out.Write32(uint32(s.fileoffset))
+ out.Write32(uint32(s.filesize))
+ out.Write32(s.prot1)
+ out.Write32(s.prot2)
+ out.Write32(s.nsect)
+ out.Write32(s.flag)
+ }
+
+ for j := uint32(0); j < s.nsect; j++ {
+ t := &s.sect[j]
+ if arch.PtrSize == 8 {
+ out.WriteStringN(t.name, 16)
+ out.WriteStringN(t.segname, 16)
+ out.Write64(t.addr)
+ out.Write64(t.size)
+ out.Write32(t.off)
+ out.Write32(t.align)
+ out.Write32(t.reloc)
+ out.Write32(t.nreloc)
+ out.Write32(t.flag)
+ out.Write32(t.res1) /* reserved */
+ out.Write32(t.res2) /* reserved */
+ out.Write32(0) /* reserved */
+ } else {
+ out.WriteStringN(t.name, 16)
+ out.WriteStringN(t.segname, 16)
+ out.Write32(uint32(t.addr))
+ out.Write32(uint32(t.size))
+ out.Write32(t.off)
+ out.Write32(t.align)
+ out.Write32(t.reloc)
+ out.Write32(t.nreloc)
+ out.Write32(t.flag)
+ out.Write32(t.res1) /* reserved */
+ out.Write32(t.res2) /* reserved */
+ }
+ }
+ }
+
+ for i := range load {
+ l := &load[i]
+ out.Write32(l.type_)
+ out.Write32(4 * (uint32(len(l.data)) + 2))
+ for j := 0; j < len(l.data); j++ {
+ out.Write32(l.data[j])
+ }
+ }
+
+ return int(out.Offset() - o1)
+}
+
+func (ctxt *Link) domacho() {
+ if *FlagD {
+ return
+ }
+
+ // Copy platform load command.
+ for _, h := range hostobj {
+ load, err := hostobjMachoPlatform(&h)
+ if err != nil {
+ Exitf("%v", err)
+ }
+ if load != nil {
+ machoPlatform = load.platform
+ ml := newMachoLoad(ctxt.Arch, load.cmd.type_, uint32(len(load.cmd.data)))
+ copy(ml.data, load.cmd.data)
+ break
+ }
+ }
+ if machoPlatform == 0 {
+ machoPlatform = PLATFORM_MACOS
+ if buildcfg.GOOS == "ios" {
+ machoPlatform = PLATFORM_IOS
+ }
+ if ctxt.LinkMode == LinkInternal && machoPlatform == PLATFORM_MACOS {
+ var version uint32
+ switch ctxt.Arch.Family {
+ case sys.AMD64:
+ // This must be fairly recent for Apple signing (go.dev/issue/30488).
+ // Having too old a version here was also implicated in some problems
+ // calling into macOS libraries (go.dev/issue/56784).
+ // In general this can be the most recent supported macOS version.
+ version = 10<<16 | 13<<8 | 0<<0 // 10.13.0
+ case sys.ARM64:
+ version = 11<<16 | 0<<8 | 0<<0 // 11.0.0
+ }
+ ml := newMachoLoad(ctxt.Arch, LC_BUILD_VERSION, 4)
+ ml.data[0] = uint32(machoPlatform)
+ ml.data[1] = version // OS version
+ ml.data[2] = version // SDK version
+ ml.data[3] = 0 // ntools
+ }
+ }
+
+ // empirically, string table must begin with " \x00".
+ s := ctxt.loader.LookupOrCreateSym(".machosymstr", 0)
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+
+ sb.SetType(sym.SMACHOSYMSTR)
+ sb.SetReachable(true)
+ sb.AddUint8(' ')
+ sb.AddUint8('\x00')
+
+ s = ctxt.loader.LookupOrCreateSym(".machosymtab", 0)
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOSYMTAB)
+ sb.SetReachable(true)
+
+ if ctxt.IsInternal() {
+ s = ctxt.loader.LookupOrCreateSym(".plt", 0) // will be __symbol_stub
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOPLT)
+ sb.SetReachable(true)
+
+ s = ctxt.loader.LookupOrCreateSym(".got", 0) // will be __nl_symbol_ptr
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOGOT)
+ sb.SetReachable(true)
+ sb.SetAlign(4)
+
+ s = ctxt.loader.LookupOrCreateSym(".linkedit.plt", 0) // indirect table for .plt
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOINDIRECTPLT)
+ sb.SetReachable(true)
+
+ s = ctxt.loader.LookupOrCreateSym(".linkedit.got", 0) // indirect table for .got
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHOINDIRECTGOT)
+ sb.SetReachable(true)
+ }
+
+ // Add a dummy symbol that will become the __asm marker section.
+ if ctxt.IsExternal() {
+ s = ctxt.loader.LookupOrCreateSym(".llvmasm", 0)
+ sb = ctxt.loader.MakeSymbolUpdater(s)
+ sb.SetType(sym.SMACHO)
+ sb.SetReachable(true)
+ sb.AddUint8(0)
+ }
+
+ // Un-export runtime symbols from plugins. Since the runtime
+ // is included in both the main binary and each plugin, these
+ // symbols appear in both images. If we leave them exported in
+ // the plugin, then the dynamic linker will resolve
+ // relocations to these functions in the plugin's functab to
+ // point to the main image, causing the runtime to think the
+ // plugin's functab is corrupted. By unexporting them, these
+ // become static references, which are resolved to the
+ // plugin's text.
+ //
+ // It would be better to omit the runtime from plugins. (Using
+ // relative PCs in the functab instead of relocations would
+ // also address this.)
+ //
+ // See issue #18190.
+ if ctxt.BuildMode == BuildModePlugin {
+ for _, name := range []string{"_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2"} {
+ // Most of these are data symbols or C
+ // symbols, so they have symbol version 0.
+ ver := 0
+ // _cgo_panic is a Go function, so it uses ABIInternal.
+ if name == "_cgo_panic" {
+ ver = abiInternalVer
+ }
+ s := ctxt.loader.Lookup(name, ver)
+ if s != 0 {
+ ctxt.loader.SetAttrCgoExportDynamic(s, false)
+ }
+ }
+ }
+}
+
+func machoadddynlib(lib string, linkmode LinkMode) {
+ if seenlib[lib] || linkmode == LinkExternal {
+ return
+ }
+ seenlib[lib] = true
+
+ // Will need to store the library name rounded up
+ // and 24 bytes of header metadata. If not enough
+ // space, grab another page of initial space at the
+ // beginning of the output file.
+ loadBudget -= (len(lib)+7)/8*8 + 24
+
+ if loadBudget < 0 {
+ HEADR += 4096
+ *FlagTextAddr += 4096
+ loadBudget += 4096
+ }
+
+ dylib = append(dylib, lib)
+}
+
+func machoshbits(ctxt *Link, mseg *MachoSeg, sect *sym.Section, segname string) {
+ buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1)
+
+ msect := newMachoSect(mseg, buf, segname)
+
+ if sect.Rellen > 0 {
+ msect.reloc = uint32(sect.Reloff)
+ msect.nreloc = uint32(sect.Rellen / 8)
+ }
+
+ for 1<<msect.align < sect.Align {
+ msect.align++
+ }
+ msect.addr = sect.Vaddr
+ msect.size = sect.Length
+
+ if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
+ // data in file
+ if sect.Length > sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr {
+ Errorf(nil, "macho cannot represent section %s crossing data and bss", sect.Name)
+ }
+ msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr)
+ } else {
+ msect.off = 0
+ msect.flag |= S_ZEROFILL
+ }
+
+ if sect.Rwx&1 != 0 {
+ msect.flag |= S_ATTR_SOME_INSTRUCTIONS
+ }
+
+ if sect.Name == ".text" {
+ msect.flag |= S_ATTR_PURE_INSTRUCTIONS
+ }
+
+ if sect.Name == ".plt" {
+ msect.name = "__symbol_stub1"
+ msect.flag = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS | S_SYMBOL_STUBS
+ msect.res1 = 0 //nkind[SymKindLocal];
+ msect.res2 = 6
+ }
+
+ if sect.Name == ".got" {
+ msect.name = "__nl_symbol_ptr"
+ msect.flag = S_NON_LAZY_SYMBOL_POINTERS
+ msect.res1 = uint32(ctxt.loader.SymSize(ctxt.ArchSyms.LinkEditPLT) / 4) /* offset into indirect symbol table */
+ }
+
+ if sect.Name == ".init_array" {
+ msect.name = "__mod_init_func"
+ msect.flag = S_MOD_INIT_FUNC_POINTERS
+ }
+
+ // Some platforms such as watchOS and tvOS require binaries with
+ // bitcode enabled. The Go toolchain can't output bitcode, so use
+ // a marker section in the __LLVM segment, "__asm", to tell the Apple
+ // toolchain that the Go text came from assembler and thus has no
+ // bitcode. This is not true, but Kotlin/Native, Rust and Flutter
+ // are also using this trick.
+ if sect.Name == ".llvmasm" {
+ msect.name = "__asm"
+ msect.segname = "__LLVM"
+ }
+
+ if segname == "__DWARF" {
+ msect.flag |= S_ATTR_DEBUG
+ }
+}
+
+func asmbMacho(ctxt *Link) {
+ machlink := doMachoLink(ctxt)
+ if !*FlagS && ctxt.IsExternal() {
+ symo := int64(Segdwarf.Fileoff + uint64(Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))) + uint64(machlink))
+ ctxt.Out.SeekSet(symo)
+ machoEmitReloc(ctxt)
+ }
+ ctxt.Out.SeekSet(0)
+
+ ldr := ctxt.loader
+
+ /* apple MACH */
+ va := *FlagTextAddr - int64(HEADR)
+
+ mh := getMachoHdr()
+ switch ctxt.Arch.Family {
+ default:
+ Exitf("unknown macho architecture: %v", ctxt.Arch.Family)
+
+ case sys.AMD64:
+ mh.cpu = MACHO_CPU_AMD64
+ mh.subcpu = MACHO_SUBCPU_X86
+
+ case sys.ARM64:
+ mh.cpu = MACHO_CPU_ARM64
+ mh.subcpu = MACHO_SUBCPU_ARM64_ALL
+ }
+
+ var ms *MachoSeg
+ if ctxt.LinkMode == LinkExternal {
+ /* segment for entire file */
+ ms = newMachoSeg("", 40)
+
+ ms.fileoffset = Segtext.Fileoff
+ ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff
+ ms.vsize = Segdwarf.Vaddr + Segdwarf.Length - Segtext.Vaddr
+ }
+
+ /* segment for zero page */
+ if ctxt.LinkMode != LinkExternal {
+ ms = newMachoSeg("__PAGEZERO", 0)
+ ms.vsize = uint64(va)
+ }
+
+ /* text */
+ v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound))
+
+ if ctxt.LinkMode != LinkExternal {
+ ms = newMachoSeg("__TEXT", 20)
+ ms.vaddr = uint64(va)
+ ms.vsize = uint64(v)
+ ms.fileoffset = 0
+ ms.filesize = uint64(v)
+ ms.prot1 = 7
+ ms.prot2 = 5
+ }
+
+ for _, sect := range Segtext.Sections {
+ machoshbits(ctxt, ms, sect, "__TEXT")
+ }
+
+ /* rodata */
+ if ctxt.LinkMode != LinkExternal && Segrelrodata.Length > 0 {
+ ms = newMachoSeg("__DATA_CONST", 20)
+ ms.vaddr = Segrelrodata.Vaddr
+ ms.vsize = Segrelrodata.Length
+ ms.fileoffset = Segrelrodata.Fileoff
+ ms.filesize = Segrelrodata.Filelen
+ ms.prot1 = 3
+ ms.prot2 = 3
+ ms.flag = 0x10 // SG_READ_ONLY
+ }
+
+ for _, sect := range Segrelrodata.Sections {
+ machoshbits(ctxt, ms, sect, "__DATA_CONST")
+ }
+
+ /* data */
+ if ctxt.LinkMode != LinkExternal {
+ ms = newMachoSeg("__DATA", 20)
+ ms.vaddr = Segdata.Vaddr
+ ms.vsize = Segdata.Length
+ ms.fileoffset = Segdata.Fileoff
+ ms.filesize = Segdata.Filelen
+ ms.prot1 = 3
+ ms.prot2 = 3
+ }
+
+ for _, sect := range Segdata.Sections {
+ machoshbits(ctxt, ms, sect, "__DATA")
+ }
+
+ /* dwarf */
+ if !*FlagW {
+ if ctxt.LinkMode != LinkExternal {
+ ms = newMachoSeg("__DWARF", 20)
+ ms.vaddr = Segdwarf.Vaddr
+ ms.vsize = 0
+ ms.fileoffset = Segdwarf.Fileoff
+ ms.filesize = Segdwarf.Filelen
+ }
+ for _, sect := range Segdwarf.Sections {
+ machoshbits(ctxt, ms, sect, "__DWARF")
+ }
+ }
+
+ if ctxt.LinkMode != LinkExternal {
+ switch ctxt.Arch.Family {
+ default:
+ Exitf("unknown macho architecture: %v", ctxt.Arch.Family)
+
+ case sys.AMD64:
+ ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 42+2)
+ ml.data[0] = 4 /* thread type */
+ ml.data[1] = 42 /* word count */
+ ml.data[2+32] = uint32(Entryvalue(ctxt)) /* start pc */
+ ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32)
+
+ case sys.ARM64:
+ ml := newMachoLoad(ctxt.Arch, LC_MAIN, 4)
+ ml.data[0] = uint32(uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR)))
+ ml.data[1] = uint32((uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) >> 32)
+ }
+ }
+
+ var codesigOff int64
+ if !*FlagD {
+ // must match doMachoLink below
+ s1 := ldr.SymSize(ldr.Lookup(".machorebase", 0))
+ s2 := ldr.SymSize(ldr.Lookup(".machobind", 0))
+ s3 := ldr.SymSize(ldr.Lookup(".machosymtab", 0))
+ s4 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT)
+ s5 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT)
+ s6 := ldr.SymSize(ldr.Lookup(".machosymstr", 0))
+ s7 := ldr.SymSize(ldr.Lookup(".machocodesig", 0))
+
+ if ctxt.LinkMode != LinkExternal {
+ ms := newMachoSeg("__LINKEDIT", 0)
+ ms.vaddr = uint64(Rnd(int64(Segdata.Vaddr+Segdata.Length), int64(*FlagRound)))
+ ms.vsize = uint64(s1 + s2 + s3 + s4 + s5 + s6 + s7)
+ ms.fileoffset = uint64(linkoff)
+ ms.filesize = ms.vsize
+ ms.prot1 = 1
+ ms.prot2 = 1
+
+ codesigOff = linkoff + s1 + s2 + s3 + s4 + s5 + s6
+ }
+
+ if ctxt.LinkMode != LinkExternal && ctxt.IsPIE() {
+ ml := newMachoLoad(ctxt.Arch, LC_DYLD_INFO_ONLY, 10)
+ ml.data[0] = uint32(linkoff) // rebase off
+ ml.data[1] = uint32(s1) // rebase size
+ ml.data[2] = uint32(linkoff + s1) // bind off
+ ml.data[3] = uint32(s2) // bind size
+ ml.data[4] = 0 // weak bind off
+ ml.data[5] = 0 // weak bind size
+ ml.data[6] = 0 // lazy bind off
+ ml.data[7] = 0 // lazy bind size
+ ml.data[8] = 0 // export
+ ml.data[9] = 0 // export size
+ }
+
+ ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4)
+ ml.data[0] = uint32(linkoff + s1 + s2) /* symoff */
+ ml.data[1] = uint32(nsortsym) /* nsyms */
+ ml.data[2] = uint32(linkoff + s1 + s2 + s3 + s4 + s5) /* stroff */
+ ml.data[3] = uint32(s6) /* strsize */
+
+ machodysymtab(ctxt, linkoff+s1+s2)
+
+ if ctxt.LinkMode != LinkExternal {
+ ml := newMachoLoad(ctxt.Arch, LC_LOAD_DYLINKER, 6)
+ ml.data[0] = 12 /* offset to string */
+ stringtouint32(ml.data[1:], "/usr/lib/dyld")
+
+ for _, lib := range dylib {
+ ml = newMachoLoad(ctxt.Arch, LC_LOAD_DYLIB, 4+(uint32(len(lib))+1+7)/8*2)
+ ml.data[0] = 24 /* offset of string from beginning of load */
+ ml.data[1] = 0 /* time stamp */
+ ml.data[2] = 0 /* version */
+ ml.data[3] = 0 /* compatibility version */
+ stringtouint32(ml.data[4:], lib)
+ }
+ }
+
+ if ctxt.IsInternal() && ctxt.NeedCodeSign() {
+ ml := newMachoLoad(ctxt.Arch, LC_CODE_SIGNATURE, 2)
+ ml.data[0] = uint32(codesigOff)
+ ml.data[1] = uint32(s7)
+ }
+ }
+
+ a := machowrite(ctxt, ctxt.Arch, ctxt.Out, ctxt.LinkMode)
+ if int32(a) > HEADR {
+ Exitf("HEADR too small: %d > %d", a, HEADR)
+ }
+
+ // Now we have written everything. Compute the code signature (which
+ // is a hash of the file content, so it must be done at last.)
+ if ctxt.IsInternal() && ctxt.NeedCodeSign() {
+ cs := ldr.Lookup(".machocodesig", 0)
+ data := ctxt.Out.Data()
+ if int64(len(data)) != codesigOff {
+ panic("wrong size")
+ }
+ codesign.Sign(ldr.Data(cs), bytes.NewReader(data), "a.out", codesigOff, int64(Segtext.Fileoff), int64(Segtext.Filelen), ctxt.IsExe() || ctxt.IsPIE())
+ ctxt.Out.SeekSet(codesigOff)
+ ctxt.Out.Write(ldr.Data(cs))
+ }
+}
+
+func symkind(ldr *loader.Loader, s loader.Sym) int {
+ if ldr.SymType(s) == sym.SDYNIMPORT {
+ return SymKindUndef
+ }
+ if ldr.AttrCgoExport(s) {
+ return SymKindExtdef
+ }
+ return SymKindLocal
+}
+
+func collectmachosyms(ctxt *Link) {
+ ldr := ctxt.loader
+
+ addsym := func(s loader.Sym) {
+ sortsym = append(sortsym, s)
+ nkind[symkind(ldr, s)]++
+ }
+
+ // Add special runtime.text and runtime.etext symbols.
+ // We've already included this symbol in Textp on darwin if ctxt.DynlinkingGo().
+ // See data.go:/textaddress
+ if !ctxt.DynlinkingGo() {
+ s := ldr.Lookup("runtime.text", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ addsym(s)
+ }
+ for n := range Segtext.Sections[1:] {
+ s := ldr.Lookup(fmt.Sprintf("runtime.text.%d", n+1), 0)
+ if s != 0 {
+ addsym(s)
+ } else {
+ break
+ }
+ }
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ addsym(s)
+ }
+ }
+
+ // Add text symbols.
+ for _, s := range ctxt.Textp {
+ addsym(s)
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym) bool {
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ name := ldr.SymName(s) // TODO: try not to read the name
+ if name == "" || name[0] == '.' {
+ return false
+ }
+ return true
+ }
+
+ // Add data symbols and external references.
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ t := ldr.SymType(s)
+ if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata
+ if t == sym.STLSBSS {
+ // TLSBSS is not used on darwin. See data.go:allocateDataSections
+ continue
+ }
+ if !shouldBeInSymbolTable(s) {
+ continue
+ }
+ addsym(s)
+ }
+
+ switch t {
+ case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT:
+ addsym(s)
+ }
+
+ // Some 64-bit functions have a "$INODE64" or "$INODE64$UNIX2003" suffix.
+ if t == sym.SDYNIMPORT && ldr.SymDynimplib(s) == "/usr/lib/libSystem.B.dylib" {
+ // But only on macOS.
+ if machoPlatform == PLATFORM_MACOS {
+ switch n := ldr.SymExtname(s); n {
+ case "fdopendir":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ ldr.SetSymExtname(s, n+"$INODE64")
+ }
+ case "readdir_r", "getfsstat":
+ switch buildcfg.GOARCH {
+ case "amd64":
+ ldr.SetSymExtname(s, n+"$INODE64")
+ }
+ }
+ }
+ }
+ }
+
+ nsortsym = len(sortsym)
+}
+
+func machosymorder(ctxt *Link) {
+ ldr := ctxt.loader
+
+ // On Mac OS X Mountain Lion, we must sort exported symbols
+ // So we sort them here and pre-allocate dynid for them
+ // See https://golang.org/issue/4029
+ for _, s := range ctxt.dynexp {
+ if !ldr.AttrReachable(s) {
+ panic("dynexp symbol is not reachable")
+ }
+ }
+ collectmachosyms(ctxt)
+ sort.Slice(sortsym[:nsortsym], func(i, j int) bool {
+ s1 := sortsym[i]
+ s2 := sortsym[j]
+ k1 := symkind(ldr, s1)
+ k2 := symkind(ldr, s2)
+ if k1 != k2 {
+ return k1 < k2
+ }
+ return ldr.SymExtname(s1) < ldr.SymExtname(s2) // Note: unnamed symbols are not added in collectmachosyms
+ })
+ for i, s := range sortsym {
+ ldr.SetSymDynid(s, int32(i))
+ }
+}
+
+// AddMachoSym adds s to Mach-O symbol table, used in GenSymLate.
+// Currently only used on ARM64 when external linking.
+func AddMachoSym(ldr *loader.Loader, s loader.Sym) {
+ ldr.SetSymDynid(s, int32(nsortsym))
+ sortsym = append(sortsym, s)
+ nsortsym++
+ nkind[symkind(ldr, s)]++
+}
+
+// machoShouldExport reports whether a symbol needs to be exported.
+//
+// When dynamically linking, all non-local variables and plugin-exported
+// symbols need to be exported.
+func machoShouldExport(ctxt *Link, ldr *loader.Loader, s loader.Sym) bool {
+ if !ctxt.DynlinkingGo() || ldr.AttrLocal(s) {
+ return false
+ }
+ if ctxt.BuildMode == BuildModePlugin && strings.HasPrefix(ldr.SymExtname(s), objabi.PathToPrefix(*flagPluginPath)) {
+ return true
+ }
+ name := ldr.SymName(s)
+ if strings.HasPrefix(name, "go:itab.") {
+ return true
+ }
+ if strings.HasPrefix(name, "type:") && !strings.HasPrefix(name, "type:.") {
+ // reduce runtime typemap pressure, but do not
+ // export alg functions (type:.*), as these
+ // appear in pclntable.
+ return true
+ }
+ if strings.HasPrefix(name, "go:link.pkghash") {
+ return true
+ }
+ return ldr.SymType(s) >= sym.SFirstWritable // only writable sections
+}
+
+func machosymtab(ctxt *Link) {
+ ldr := ctxt.loader
+ symtab := ldr.CreateSymForUpdate(".machosymtab", 0)
+ symstr := ldr.CreateSymForUpdate(".machosymstr", 0)
+
+ for _, s := range sortsym[:nsortsym] {
+ symtab.AddUint32(ctxt.Arch, uint32(symstr.Size()))
+
+ export := machoShouldExport(ctxt, ldr, s)
+
+ // Prefix symbol names with "_" to match the system toolchain.
+ // (We used to only prefix C symbols, which is all required for the build.
+ // But some tools don't recognize Go symbols as symbols, so we prefix them
+ // as well.)
+ symstr.AddUint8('_')
+
+ // replace "·" as ".", because DTrace cannot handle it.
+ name := strings.Replace(ldr.SymExtname(s), "·", ".", -1)
+
+ name = mangleABIName(ctxt, ldr, s, name)
+ symstr.Addstring(name)
+
+ if t := ldr.SymType(s); t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT {
+ symtab.AddUint8(0x01) // type N_EXT, external symbol
+ symtab.AddUint8(0) // no section
+ symtab.AddUint16(ctxt.Arch, 0) // desc
+ symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value
+ } else {
+ if export || ldr.AttrCgoExportDynamic(s) {
+ symtab.AddUint8(0x0f) // N_SECT | N_EXT
+ } else if ldr.AttrCgoExportStatic(s) {
+ // Only export statically, not dynamically. (N_PEXT is like hidden visibility)
+ symtab.AddUint8(0x1f) // N_SECT | N_EXT | N_PEXT
+ } else {
+ symtab.AddUint8(0x0e) // N_SECT
+ }
+ o := s
+ if outer := ldr.OuterSym(o); outer != 0 {
+ o = outer
+ }
+ if ldr.SymSect(o) == nil {
+ ldr.Errorf(s, "missing section for symbol")
+ symtab.AddUint8(0)
+ } else {
+ symtab.AddUint8(uint8(ldr.SymSect(o).Extnum))
+ }
+ symtab.AddUint16(ctxt.Arch, 0) // desc
+ symtab.AddUintXX(ctxt.Arch, uint64(ldr.SymAddr(s)), ctxt.Arch.PtrSize)
+ }
+ }
+}
+
+func machodysymtab(ctxt *Link, base int64) {
+ ml := newMachoLoad(ctxt.Arch, LC_DYSYMTAB, 18)
+
+ n := 0
+ ml.data[0] = uint32(n) /* ilocalsym */
+ ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */
+ n += nkind[SymKindLocal]
+
+ ml.data[2] = uint32(n) /* iextdefsym */
+ ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */
+ n += nkind[SymKindExtdef]
+
+ ml.data[4] = uint32(n) /* iundefsym */
+ ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */
+
+ ml.data[6] = 0 /* tocoffset */
+ ml.data[7] = 0 /* ntoc */
+ ml.data[8] = 0 /* modtaboff */
+ ml.data[9] = 0 /* nmodtab */
+ ml.data[10] = 0 /* extrefsymoff */
+ ml.data[11] = 0 /* nextrefsyms */
+
+ ldr := ctxt.loader
+
+ // must match domacholink below
+ s1 := ldr.SymSize(ldr.Lookup(".machosymtab", 0))
+ s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT)
+ s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT)
+ ml.data[12] = uint32(base + s1) /* indirectsymoff */
+ ml.data[13] = uint32((s2 + s3) / 4) /* nindirectsyms */
+
+ ml.data[14] = 0 /* extreloff */
+ ml.data[15] = 0 /* nextrel */
+ ml.data[16] = 0 /* locreloff */
+ ml.data[17] = 0 /* nlocrel */
+}
+
+func doMachoLink(ctxt *Link) int64 {
+ machosymtab(ctxt)
+ machoDyldInfo(ctxt)
+
+ ldr := ctxt.loader
+
+ // write data that will be linkedit section
+ s1 := ldr.Lookup(".machorebase", 0)
+ s2 := ldr.Lookup(".machobind", 0)
+ s3 := ldr.Lookup(".machosymtab", 0)
+ s4 := ctxt.ArchSyms.LinkEditPLT
+ s5 := ctxt.ArchSyms.LinkEditGOT
+ s6 := ldr.Lookup(".machosymstr", 0)
+
+ size := ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4) + ldr.SymSize(s5) + ldr.SymSize(s6)
+
+ // Force the linkedit section to end on a 16-byte
+ // boundary. This allows pure (non-cgo) Go binaries
+ // to be code signed correctly.
+ //
+ // Apple's codesign_allocate (a helper utility for
+ // the codesign utility) can do this fine itself if
+ // it is run on a dynamic Mach-O binary. However,
+ // when it is run on a pure (non-cgo) Go binary, where
+ // the linkedit section is mostly empty, it fails to
+ // account for the extra padding that it itself adds
+ // when adding the LC_CODE_SIGNATURE load command
+ // (which must be aligned on a 16-byte boundary).
+ //
+ // By forcing the linkedit section to end on a 16-byte
+ // boundary, codesign_allocate will not need to apply
+ // any alignment padding itself, working around the
+ // issue.
+ if size%16 != 0 {
+ n := 16 - size%16
+ s6b := ldr.MakeSymbolUpdater(s6)
+ s6b.Grow(s6b.Size() + n)
+ s6b.SetSize(s6b.Size() + n)
+ size += n
+ }
+
+ if size > 0 {
+ linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segrelrodata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))
+ ctxt.Out.SeekSet(linkoff)
+
+ ctxt.Out.Write(ldr.Data(s1))
+ ctxt.Out.Write(ldr.Data(s2))
+ ctxt.Out.Write(ldr.Data(s3))
+ ctxt.Out.Write(ldr.Data(s4))
+ ctxt.Out.Write(ldr.Data(s5))
+ ctxt.Out.Write(ldr.Data(s6))
+
+ // Add code signature if necessary. This must be the last.
+ s7 := machoCodeSigSym(ctxt, linkoff+size)
+ size += ldr.SymSize(s7)
+ }
+
+ return Rnd(size, int64(*FlagRound))
+}
+
+func machorelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) {
+ // If main section has no bits, nothing to relocate.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return
+ }
+ ldr := ctxt.loader
+
+ for i, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if uint64(ldr.SymValue(s)) >= sect.Vaddr {
+ syms = syms[i:]
+ break
+ }
+ }
+
+ eaddr := sect.Vaddr + sect.Length
+ for _, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymValue(s) >= int64(eaddr) {
+ break
+ }
+
+ // Compute external relocations on the go, and pass to Machoreloc1
+ // to stream out.
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rr, ok := extreloc(ctxt, ldr, s, r)
+ if !ok {
+ continue
+ }
+ if rr.Xsym == 0 {
+ ldr.Errorf(s, "missing xsym in relocation")
+ continue
+ }
+ if !ldr.AttrReachable(rr.Xsym) {
+ ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym))
+ }
+ if !thearch.Machoreloc1(ctxt.Arch, out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) {
+ ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym()))
+ }
+ }
+ }
+
+ // sanity check
+ if uint64(out.Offset()) != sect.Reloff+sect.Rellen {
+ panic("machorelocsect: size mismatch")
+ }
+}
+
+func machoEmitReloc(ctxt *Link) {
+ for ctxt.Out.Offset()&7 != 0 {
+ ctxt.Out.Write8(0)
+ }
+
+ sizeExtRelocs(ctxt, thearch.MachorelocSize)
+ relocSect, wg := relocSectFn(ctxt, machorelocsect)
+
+ relocSect(ctxt, Segtext.Sections[0], ctxt.Textp)
+ for _, sect := range Segtext.Sections[1:] {
+ if sect.Name == ".text" {
+ relocSect(ctxt, sect, ctxt.Textp)
+ } else {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ }
+ for _, sect := range Segrelrodata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for _, sect := range Segdata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
+ for i := 0; i < len(Segdwarf.Sections); i++ {
+ sect := Segdwarf.Sections[i]
+ si := dwarfp[i]
+ if si.secSym() != loader.Sym(sect.Sym) ||
+ ctxt.loader.SymSect(si.secSym()) != sect {
+ panic("inconsistency between dwarfp and Segdwarf")
+ }
+ relocSect(ctxt, sect, si.syms)
+ }
+ wg.Wait()
+}
+
+// hostobjMachoPlatform returns the first platform load command found
+// in the host object, if any.
+func hostobjMachoPlatform(h *Hostobj) (*MachoPlatformLoad, error) {
+ f, err := os.Open(h.file)
+ if err != nil {
+ return nil, fmt.Errorf("%s: failed to open host object: %v\n", h.file, err)
+ }
+ defer f.Close()
+ sr := io.NewSectionReader(f, h.off, h.length)
+ m, err := macho.NewFile(sr)
+ if err != nil {
+ // Not a valid Mach-O file.
+ return nil, nil
+ }
+ return peekMachoPlatform(m)
+}
+
+// peekMachoPlatform returns the first LC_VERSION_MIN_* or LC_BUILD_VERSION
+// load command found in the Mach-O file, if any.
+func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) {
+ for _, cmd := range m.Loads {
+ raw := cmd.Raw()
+ ml := MachoLoad{
+ type_: m.ByteOrder.Uint32(raw),
+ }
+ // Skip the type and command length.
+ data := raw[8:]
+ var p MachoPlatform
+ switch ml.type_ {
+ case LC_VERSION_MIN_IPHONEOS:
+ p = PLATFORM_IOS
+ case LC_VERSION_MIN_MACOSX:
+ p = PLATFORM_MACOS
+ case LC_VERSION_MIN_WATCHOS:
+ p = PLATFORM_WATCHOS
+ case LC_VERSION_MIN_TVOS:
+ p = PLATFORM_TVOS
+ case LC_BUILD_VERSION:
+ p = MachoPlatform(m.ByteOrder.Uint32(data))
+ default:
+ continue
+ }
+ ml.data = make([]uint32, len(data)/4)
+ r := bytes.NewReader(data)
+ if err := binary.Read(r, m.ByteOrder, &ml.data); err != nil {
+ return nil, err
+ }
+ return &MachoPlatformLoad{
+ platform: p,
+ cmd: ml,
+ }, nil
+ }
+ return nil, nil
+}
+
+// A rebase entry tells the dynamic linker the data at sym+off needs to be
+// relocated when the in-memory image moves. (This is somewhat like, say,
+// ELF R_X86_64_RELATIVE).
+// For now, the only kind of entry we support is that the data is an absolute
+// address. That seems all we need.
+// In the binary it uses a compact stateful bytecode encoding. So we record
+// entries as we go and build the table at the end.
+type machoRebaseRecord struct {
+ sym loader.Sym
+ off int64
+}
+
+var machorebase []machoRebaseRecord
+
+func MachoAddRebase(s loader.Sym, off int64) {
+ machorebase = append(machorebase, machoRebaseRecord{s, off})
+}
+
+// A bind entry tells the dynamic linker the data at GOT+off should be bound
+// to the address of the target symbol, which is a dynamic import.
+// For now, the only kind of entry we support is that the data is an absolute
+// address, and the source symbol is always the GOT. That seems all we need.
+// In the binary it uses a compact stateful bytecode encoding. So we record
+// entries as we go and build the table at the end.
+type machoBindRecord struct {
+ off int64
+ targ loader.Sym
+}
+
+var machobind []machoBindRecord
+
+func MachoAddBind(off int64, targ loader.Sym) {
+ machobind = append(machobind, machoBindRecord{off, targ})
+}
+
+// Generate data for the dynamic linker, used in LC_DYLD_INFO_ONLY load command.
+// See mach-o/loader.h, struct dyld_info_command, for the encoding.
+// e.g. https://opensource.apple.com/source/xnu/xnu-6153.81.5/EXTERNAL_HEADERS/mach-o/loader.h
+func machoDyldInfo(ctxt *Link) {
+ ldr := ctxt.loader
+ rebase := ldr.CreateSymForUpdate(".machorebase", 0)
+ bind := ldr.CreateSymForUpdate(".machobind", 0)
+
+ if !(ctxt.IsPIE() && ctxt.IsInternal()) {
+ return
+ }
+
+ segId := func(seg *sym.Segment) uint8 {
+ switch seg {
+ case &Segtext:
+ return 1
+ case &Segrelrodata:
+ return 2
+ case &Segdata:
+ if Segrelrodata.Length > 0 {
+ return 3
+ }
+ return 2
+ }
+ panic("unknown segment")
+ }
+
+ dylibId := func(s loader.Sym) int {
+ slib := ldr.SymDynimplib(s)
+ for i, lib := range dylib {
+ if lib == slib {
+ return i + 1
+ }
+ }
+ return BIND_SPECIAL_DYLIB_FLAT_LOOKUP // don't know where it is from
+ }
+
+ // Rebase table.
+ // TODO: use more compact encoding. The encoding is stateful, and
+ // we can use delta encoding.
+ rebase.AddUint8(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER)
+ for _, r := range machorebase {
+ seg := ldr.SymSect(r.sym).Seg
+ off := uint64(ldr.SymValue(r.sym)+r.off) - seg.Vaddr
+ rebase.AddUint8(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg))
+ rebase.AddUleb(off)
+
+ rebase.AddUint8(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1)
+ }
+ rebase.AddUint8(REBASE_OPCODE_DONE)
+ sz := Rnd(rebase.Size(), 8)
+ rebase.Grow(sz)
+ rebase.SetSize(sz)
+
+ // Bind table.
+ // TODO: compact encoding, as above.
+ // TODO: lazy binding?
+ got := ctxt.GOT
+ seg := ldr.SymSect(got).Seg
+ gotAddr := ldr.SymValue(got)
+ bind.AddUint8(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER)
+ for _, r := range machobind {
+ off := uint64(gotAddr+r.off) - seg.Vaddr
+ bind.AddUint8(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg))
+ bind.AddUleb(off)
+
+ d := dylibId(r.targ)
+ if d > 0 && d < 128 {
+ bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | uint8(d)&0xf)
+ } else if d >= 128 {
+ bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB)
+ bind.AddUleb(uint64(d))
+ } else { // d <= 0
+ bind.AddUint8(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | uint8(d)&0xf)
+ }
+
+ bind.AddUint8(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
+ // target symbol name as a C string, with _ prefix
+ bind.AddUint8('_')
+ bind.Addstring(ldr.SymExtname(r.targ))
+
+ bind.AddUint8(BIND_OPCODE_DO_BIND)
+ }
+ bind.AddUint8(BIND_OPCODE_DONE)
+ sz = Rnd(bind.Size(), 16) // make it 16-byte aligned, see the comment in doMachoLink
+ bind.Grow(sz)
+ bind.SetSize(sz)
+
+ // TODO: export table.
+ // The symbols names are encoded as a trie. I'm really too lazy to do that
+ // for now.
+ // Without it, the symbols are not dynamically exported, so they cannot be
+ // e.g. dlsym'd. But internal linking is not the default in that case, so
+ // it is fine.
+}
+
+// machoCodeSigSym creates and returns a symbol for code signature.
+// The symbol context is left as zeros, which will be generated at the end
+// (as it depends on the rest of the file).
+func machoCodeSigSym(ctxt *Link, codeSize int64) loader.Sym {
+ ldr := ctxt.loader
+ cs := ldr.CreateSymForUpdate(".machocodesig", 0)
+ if !ctxt.NeedCodeSign() || ctxt.IsExternal() {
+ return cs.Sym()
+ }
+ sz := codesign.Size(codeSize, "a.out")
+ cs.Grow(sz)
+ cs.SetSize(sz)
+ return cs.Sym()
+}
+
+// machoCodeSign code-signs Mach-O file fname with an ad-hoc signature.
+// This is used for updating an external linker generated binary.
+func machoCodeSign(ctxt *Link, fname string) error {
+ f, err := os.OpenFile(fname, os.O_RDWR, 0)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ mf, err := macho.NewFile(f)
+ if err != nil {
+ return err
+ }
+ if mf.Magic != macho.Magic64 {
+ Exitf("not 64-bit Mach-O file: %s", fname)
+ }
+
+ // Find existing LC_CODE_SIGNATURE and __LINKEDIT segment
+ var sigOff, sigSz, csCmdOff, linkeditOff int64
+ var linkeditSeg, textSeg *macho.Segment
+ loadOff := int64(machoHeaderSize64)
+ get32 := mf.ByteOrder.Uint32
+ for _, l := range mf.Loads {
+ data := l.Raw()
+ cmd, sz := get32(data), get32(data[4:])
+ if cmd == LC_CODE_SIGNATURE {
+ sigOff = int64(get32(data[8:]))
+ sigSz = int64(get32(data[12:]))
+ csCmdOff = loadOff
+ }
+ if seg, ok := l.(*macho.Segment); ok {
+ switch seg.Name {
+ case "__LINKEDIT":
+ linkeditSeg = seg
+ linkeditOff = loadOff
+ case "__TEXT":
+ textSeg = seg
+ }
+ }
+ loadOff += int64(sz)
+ }
+
+ if sigOff == 0 {
+ // The C linker doesn't generate a signed binary, for some reason.
+ // Skip.
+ return nil
+ }
+
+ fi, err := f.Stat()
+ if err != nil {
+ return err
+ }
+ if sigOff+sigSz != fi.Size() {
+ // We don't expect anything after the signature (this will invalidate
+ // the signature anyway.)
+ return fmt.Errorf("unexpected content after code signature")
+ }
+
+ sz := codesign.Size(sigOff, "a.out")
+ if sz != sigSz {
+ // Update the load command,
+ var tmp [8]byte
+ mf.ByteOrder.PutUint32(tmp[:4], uint32(sz))
+ _, err = f.WriteAt(tmp[:4], csCmdOff+12)
+ if err != nil {
+ return err
+ }
+
+ // Uodate the __LINKEDIT segment.
+ segSz := sigOff + sz - int64(linkeditSeg.Offset)
+ mf.ByteOrder.PutUint64(tmp[:8], uint64(segSz))
+ _, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Memsz)))
+ if err != nil {
+ return err
+ }
+ _, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Filesz)))
+ if err != nil {
+ return err
+ }
+ }
+
+ cs := make([]byte, sz)
+ codesign.Sign(cs, f, "a.out", sigOff, int64(textSeg.Offset), int64(textSeg.Filesz), ctxt.IsExe() || ctxt.IsPIE())
+ _, err = f.WriteAt(cs, sigOff)
+ if err != nil {
+ return err
+ }
+ err = f.Truncate(sigOff + sz)
+ return err
+}
diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go
new file mode 100644
index 0000000..2ab7da9
--- /dev/null
+++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go
@@ -0,0 +1,438 @@
+// Copyright 2015 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.
+
+package ld
+
+import (
+ "bytes"
+ "compress/zlib"
+ "debug/macho"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "unsafe"
+)
+
+type loadCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+}
+
+type dyldInfoCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+ RebaseOff, RebaseLen uint32
+ BindOff, BindLen uint32
+ WeakBindOff, WeakBindLen uint32
+ LazyBindOff, LazyBindLen uint32
+ ExportOff, ExportLen uint32
+}
+
+type linkEditDataCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+ DataOff, DataLen uint32
+}
+
+type encryptionInfoCmd struct {
+ Cmd macho.LoadCmd
+ Len uint32
+ CryptOff, CryptLen uint32
+ CryptId uint32
+}
+
+type loadCmdReader struct {
+ offset, next int64
+ f *os.File
+ order binary.ByteOrder
+}
+
+func (r *loadCmdReader) Next() (loadCmd, error) {
+ var cmd loadCmd
+
+ r.offset = r.next
+ if _, err := r.f.Seek(r.offset, 0); err != nil {
+ return cmd, err
+ }
+ if err := binary.Read(r.f, r.order, &cmd); err != nil {
+ return cmd, err
+ }
+ r.next = r.offset + int64(cmd.Len)
+ return cmd, nil
+}
+
+func (r loadCmdReader) ReadAt(offset int64, data interface{}) error {
+ if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+ return err
+ }
+ return binary.Read(r.f, r.order, data)
+}
+
+func (r loadCmdReader) WriteAt(offset int64, data interface{}) error {
+ if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+ return err
+ }
+ return binary.Write(r.f, r.order, data)
+}
+
+// machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable.
+//
+// With internal linking, DWARF is embedded into the executable, this lets us do the
+// same for external linking.
+// exef is the file of the executable with no DWARF. It must have enough room in the macho
+// header to add the DWARF sections. (Use ld's -headerpad option)
+// exem is the macho representation of exef.
+// dsym is the path to the macho file containing DWARF from dsymutil.
+// outexe is the path where the combined executable should be saved.
+func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe string) error {
+ dwarff, err := os.Open(dsym)
+ if err != nil {
+ return err
+ }
+ defer dwarff.Close()
+ outf, err := os.OpenFile(outexe, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
+ if err != nil {
+ return err
+ }
+ defer outf.Close()
+ dwarfm, err := macho.NewFile(dwarff)
+ if err != nil {
+ return err
+ }
+ defer dwarfm.Close()
+
+ // The string table needs to be the last thing in the file
+ // for code signing to work. So we'll need to move the
+ // linkedit section, but all the others can be copied directly.
+ linkseg := exem.Segment("__LINKEDIT")
+ if linkseg == nil {
+ return fmt.Errorf("missing __LINKEDIT segment")
+ }
+
+ if _, err := exef.Seek(0, 0); err != nil {
+ return err
+ }
+ if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil {
+ return err
+ }
+
+ realdwarf := dwarfm.Segment("__DWARF")
+ if realdwarf == nil {
+ return fmt.Errorf("missing __DWARF segment")
+ }
+
+ // Try to compress the DWARF sections. This includes some Apple
+ // proprietary sections like __apple_types.
+ compressedSects, compressedBytes, err := machoCompressSections(ctxt, dwarfm)
+ if err != nil {
+ return err
+ }
+
+ // Now copy the dwarf data into the output.
+ // Kernel requires all loaded segments to be page-aligned in the file,
+ // even though we mark this one as being 0 bytes of virtual address space.
+ dwarfstart := Rnd(int64(linkseg.Offset), int64(*FlagRound))
+ if _, err := outf.Seek(dwarfstart, 0); err != nil {
+ return err
+ }
+
+ if _, err := dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
+ return err
+ }
+
+ // Write out the compressed sections, or the originals if we gave up
+ // on compressing them.
+ var dwarfsize uint64
+ if compressedBytes != nil {
+ dwarfsize = uint64(len(compressedBytes))
+ if _, err := outf.Write(compressedBytes); err != nil {
+ return err
+ }
+ } else {
+ if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil {
+ return err
+ }
+ dwarfsize = realdwarf.Filesz
+ }
+
+ // And finally the linkedit section.
+ if _, err := exef.Seek(int64(linkseg.Offset), 0); err != nil {
+ return err
+ }
+ linkstart := Rnd(dwarfstart+int64(dwarfsize), int64(*FlagRound))
+ if _, err := outf.Seek(linkstart, 0); err != nil {
+ return err
+ }
+ if _, err := io.Copy(outf, exef); err != nil {
+ return err
+ }
+
+ // Now we need to update the headers.
+ textsect := exem.Section("__text")
+ if textsect == nil {
+ return fmt.Errorf("missing __text section")
+ }
+
+ cmdOffset := unsafe.Sizeof(exem.FileHeader)
+ if is64bit := exem.Magic == macho.Magic64; is64bit {
+ // mach_header_64 has one extra uint32.
+ cmdOffset += unsafe.Sizeof(exem.Magic)
+ }
+ dwarfCmdOffset := uint32(cmdOffset) + exem.FileHeader.Cmdsz
+ availablePadding := textsect.Offset - dwarfCmdOffset
+ if availablePadding < realdwarf.Len {
+ return fmt.Errorf("no room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding)
+ }
+ // First, copy the dwarf load command into the header. It will be
+ // updated later with new offsets and lengths as necessary.
+ if _, err := outf.Seek(int64(dwarfCmdOffset), 0); err != nil {
+ return err
+ }
+ if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil {
+ return err
+ }
+ if _, err := outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil {
+ return err
+ }
+ if err := binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil {
+ return err
+ }
+ if err := binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil {
+ return err
+ }
+
+ reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder}
+ for i := uint32(0); i < exem.Ncmd; i++ {
+ cmd, err := reader.Next()
+ if err != nil {
+ return err
+ }
+ linkoffset := uint64(linkstart) - linkseg.Offset
+ switch cmd.Cmd {
+ case macho.LoadCmdSegment64:
+ err = machoUpdateSegment(reader, linkseg, linkoffset)
+ case macho.LoadCmdSegment:
+ panic("unexpected 32-bit segment")
+ case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff")
+ case macho.LoadCmdSymtab:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.SymtabCmd{}, "Symoff", "Stroff")
+ case macho.LoadCmdDysymtab:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff")
+ case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS,
+ LC_DYLD_EXPORTS_TRIE, LC_DYLD_CHAINED_FIXUPS:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &linkEditDataCmd{}, "DataOff")
+ case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
+ err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &encryptionInfoCmd{}, "CryptOff")
+ case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread,
+ LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION,
+ LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH, LC_ID_DYLIB,
+ LC_SYMSEG, LC_LOADFVMLIB, LC_IDFVMLIB, LC_IDENT, LC_FVMFILE, LC_PREPAGE, LC_ID_DYLINKER,
+ LC_ROUTINES, LC_SUB_FRAMEWORK, LC_SUB_UMBRELLA, LC_SUB_CLIENT, LC_SUB_LIBRARY, LC_TWOLEVEL_HINTS,
+ LC_PREBIND_CKSUM, LC_ROUTINES_64, LC_LAZY_LOAD_DYLIB, LC_LOAD_UPWARD_DYLIB, LC_DYLD_ENVIRONMENT,
+ LC_LINKER_OPTION, LC_LINKER_OPTIMIZATION_HINT, LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS,
+ LC_VERSION_NOTE, LC_BUILD_VERSION:
+ // Nothing to update
+ default:
+ err = fmt.Errorf("unknown load command 0x%x (%s)", int(cmd.Cmd), cmd.Cmd)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ // Do the final update of the DWARF segment's load command.
+ return machoUpdateDwarfHeader(&reader, compressedSects, dwarfsize, dwarfstart, realdwarf)
+}
+
+// machoCompressSections tries to compress the DWARF segments in dwarfm,
+// returning the updated sections and segment contents, nils if the sections
+// weren't compressed, or an error if there was a problem reading dwarfm.
+func machoCompressSections(ctxt *Link, dwarfm *macho.File) ([]*macho.Section, []byte, error) {
+ if !ctxt.compressDWARF {
+ return nil, nil, nil
+ }
+
+ dwarfseg := dwarfm.Segment("__DWARF")
+ var sects []*macho.Section
+ var buf bytes.Buffer
+
+ for _, sect := range dwarfm.Sections {
+ if sect.Seg != "__DWARF" {
+ continue
+ }
+
+ // As of writing, there are no relocations in dsymutil's output
+ // so there's no point in worrying about them. Bail out if that
+ // changes.
+ if sect.Nreloc != 0 {
+ return nil, nil, nil
+ }
+
+ data, err := sect.Data()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ compressed, contents, err := machoCompressSection(data)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ newSec := *sect
+ newSec.Offset = uint32(dwarfseg.Offset) + uint32(buf.Len())
+ newSec.Addr = dwarfseg.Addr + uint64(buf.Len())
+ if compressed {
+ newSec.Name = "__z" + sect.Name[2:]
+ newSec.Size = uint64(len(contents))
+ }
+ sects = append(sects, &newSec)
+ buf.Write(contents)
+ }
+ return sects, buf.Bytes(), nil
+}
+
+// machoCompressSection compresses secBytes if it results in less data.
+func machoCompressSection(sectBytes []byte) (compressed bool, contents []byte, err error) {
+ var buf bytes.Buffer
+ buf.WriteString("ZLIB")
+ var sizeBytes [8]byte
+ binary.BigEndian.PutUint64(sizeBytes[:], uint64(len(sectBytes)))
+ buf.Write(sizeBytes[:])
+
+ z := zlib.NewWriter(&buf)
+ if _, err := z.Write(sectBytes); err != nil {
+ return false, nil, err
+ }
+ if err := z.Close(); err != nil {
+ return false, nil, err
+ }
+ if buf.Len() >= len(sectBytes) {
+ return false, sectBytes, nil
+ }
+ return true, buf.Bytes(), nil
+}
+
+// machoUpdateSegment updates the load command for a moved segment.
+// Only the linkedit segment should move, and it should have 0 sections.
+func machoUpdateSegment(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64) error {
+ var seg macho.Segment64
+ if err := r.ReadAt(0, &seg); err != nil {
+ return err
+ }
+
+ // Only the linkedit segment moved, anything before that is fine.
+ if seg.Offset < linkseg.Offset {
+ return nil
+ }
+ seg.Offset += linkoffset
+ if err := r.WriteAt(0, &seg); err != nil {
+ return err
+ }
+ // There shouldn't be any sections, but just to make sure...
+ return machoUpdateSections(r, &seg, linkoffset, nil)
+}
+
+func machoUpdateSections(r loadCmdReader, seg *macho.Segment64, deltaOffset uint64, compressedSects []*macho.Section) error {
+ nsect := seg.Nsect
+ if nsect == 0 {
+ return nil
+ }
+ sectOffset := int64(unsafe.Sizeof(*seg))
+
+ var sect macho.Section64
+ sectSize := int64(unsafe.Sizeof(sect))
+ for i := uint32(0); i < nsect; i++ {
+ if err := r.ReadAt(sectOffset, &sect); err != nil {
+ return err
+ }
+ if compressedSects != nil {
+ cSect := compressedSects[i]
+ copy(sect.Name[:], cSect.Name)
+ sect.Size = cSect.Size
+ if cSect.Offset != 0 {
+ sect.Offset = cSect.Offset + uint32(deltaOffset)
+ }
+ if cSect.Addr != 0 {
+ sect.Addr = cSect.Addr
+ }
+ } else {
+ if sect.Offset != 0 {
+ sect.Offset += uint32(deltaOffset)
+ }
+ if sect.Reloff != 0 {
+ sect.Reloff += uint32(deltaOffset)
+ }
+ }
+ if err := r.WriteAt(sectOffset, &sect); err != nil {
+ return err
+ }
+ sectOffset += sectSize
+ }
+ return nil
+}
+
+// machoUpdateDwarfHeader updates the DWARF segment load command.
+func machoUpdateDwarfHeader(r *loadCmdReader, compressedSects []*macho.Section, dwarfsize uint64, dwarfstart int64, realdwarf *macho.Segment) error {
+ cmd, err := r.Next()
+ if err != nil {
+ return err
+ }
+ if cmd.Cmd != macho.LoadCmdSegment64 {
+ panic("not a Segment64")
+ }
+ var seg macho.Segment64
+ if err := r.ReadAt(0, &seg); err != nil {
+ return err
+ }
+ seg.Offset = uint64(dwarfstart)
+
+ if compressedSects != nil {
+ var segSize uint64
+ for _, newSect := range compressedSects {
+ segSize += newSect.Size
+ }
+ seg.Filesz = segSize
+ } else {
+ seg.Filesz = dwarfsize
+ }
+
+ // We want the DWARF segment to be considered non-loadable, so
+ // force vmaddr and vmsize to zero. In addition, set the initial
+ // protection to zero so as to make the dynamic loader happy,
+ // since otherwise it may complain that the vm size and file
+ // size don't match for the segment. See issues 21647 and 32673
+ // for more context. Also useful to refer to the Apple dynamic
+ // loader source, specifically ImageLoaderMachO::sniffLoadCommands
+ // in ImageLoaderMachO.cpp (various versions can be found online, see
+ // https://opensource.apple.com/source/dyld/dyld-519.2.2/src/ImageLoaderMachO.cpp.auto.html
+ // as one example).
+ seg.Addr = 0
+ seg.Memsz = 0
+ seg.Prot = 0
+
+ if err := r.WriteAt(0, &seg); err != nil {
+ return err
+ }
+ return machoUpdateSections(*r, &seg, uint64(dwarfstart)-realdwarf.Offset, compressedSects)
+}
+
+func machoUpdateLoadCommand(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64, cmd interface{}, fields ...string) error {
+ if err := r.ReadAt(0, cmd); err != nil {
+ return err
+ }
+ value := reflect.Indirect(reflect.ValueOf(cmd))
+
+ for _, name := range fields {
+ field := value.FieldByName(name)
+ if fieldval := field.Uint(); fieldval >= linkseg.Offset {
+ field.SetUint(fieldval + linkoffset)
+ }
+ }
+ if err := r.WriteAt(0, cmd); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
new file mode 100644
index 0000000..34411d7
--- /dev/null
+++ b/src/cmd/link/internal/ld/main.go
@@ -0,0 +1,444 @@
+// Inferno utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "bufio"
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/quoted"
+ "cmd/internal/sys"
+ "cmd/link/internal/benchmark"
+ "flag"
+ "internal/buildcfg"
+ "log"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "strings"
+)
+
+var (
+ pkglistfornote []byte
+ windowsgui bool // writes a "GUI binary" instead of a "console binary"
+ ownTmpDir bool // set to true if tmp dir created by linker (e.g. no -tmpdir)
+)
+
+func init() {
+ flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...")
+ flag.Var(&flagExtld, "extld", "use `linker` when linking in external mode")
+ flag.Var(&flagExtldflags, "extldflags", "pass `flags` to external linker")
+}
+
+// Flags used by the linker. The exported flags are used by the architecture-specific packages.
+var (
+ flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
+
+ flagOutfile = flag.String("o", "", "write output to `file`")
+ flagPluginPath = flag.String("pluginpath", "", "full path name for plugin")
+
+ flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`")
+ flagDumpDep = flag.Bool("dumpdep", false, "dump symbol dependency graph")
+ flagRace = flag.Bool("race", false, "enable race detector")
+ flagMsan = flag.Bool("msan", false, "enable MSan interface")
+ flagAsan = flag.Bool("asan", false, "enable ASan interface")
+ flagAslr = flag.Bool("aslr", true, "enable ASLR for buildmode=c-shared on windows")
+
+ flagFieldTrack = flag.String("k", "", "set field tracking `symbol`")
+ flagLibGCC = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable")
+ flagTmpdir = flag.String("tmpdir", "", "use `directory` for temporary files")
+
+ flagExtld quoted.Flag
+ flagExtldflags quoted.Flag
+ flagExtar = flag.String("extar", "", "archive program for buildmode=c-archive")
+
+ flagCaptureHostObjs = flag.String("capturehostobjs", "", "capture host object files loaded during internal linking to specified dir")
+
+ flagA = flag.Bool("a", false, "no-op (deprecated)")
+ FlagC = flag.Bool("c", false, "dump call graph")
+ FlagD = flag.Bool("d", false, "disable dynamic executable")
+ flagF = flag.Bool("f", false, "ignore version mismatch")
+ flagG = flag.Bool("g", false, "disable go package data checks")
+ flagH = flag.Bool("h", false, "halt on error")
+ flagN = flag.Bool("n", false, "dump symbol table")
+ FlagS = flag.Bool("s", false, "disable symbol table")
+ FlagW = flag.Bool("w", false, "disable DWARF generation")
+ flag8 bool // use 64-bit addresses in symbol table
+ flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
+ FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
+ FlagDebugTextSize = flag.Int("debugtextsize", 0, "debug text section max size")
+ flagDebugNosplit = flag.Bool("debugnosplit", false, "dump nosplit call graph")
+ FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
+ FlagRound = flag.Int("R", -1, "set address rounding `quantum`")
+ FlagTextAddr = flag.Int64("T", -1, "set text segment `address`")
+ flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
+ cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
+ memprofile = flag.String("memprofile", "", "write memory profile to `file`")
+ memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
+ benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")
+ benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof")
+)
+
+// Main is the main entry point for the linker code.
+func Main(arch *sys.Arch, theArch Arch) {
+ thearch = theArch
+ ctxt := linknew(arch)
+ ctxt.Bso = bufio.NewWriter(os.Stdout)
+
+ // For testing behavior of go command when tools crash silently.
+ // Undocumented, not in standard flag parser to avoid
+ // exposing in usage message.
+ for _, arg := range os.Args {
+ if arg == "-crash_for_testing" {
+ os.Exit(2)
+ }
+ }
+
+ if final := gorootFinal(); final == "$GOROOT" {
+ // cmd/go sets GOROOT_FINAL to the dummy value "$GOROOT" when -trimpath is set,
+ // but runtime.GOROOT() should return the empty string, not a bogus value.
+ // (See https://go.dev/issue/51461.)
+ } else {
+ addstrdata1(ctxt, "runtime.defaultGOROOT="+final)
+ }
+
+ buildVersion := buildcfg.Version
+ if goexperiment := buildcfg.Experiment.String(); goexperiment != "" {
+ buildVersion += " X:" + goexperiment
+ }
+ addstrdata1(ctxt, "runtime.buildVersion="+buildVersion)
+
+ // TODO(matloob): define these above and then check flag values here
+ if ctxt.Arch.Family == sys.AMD64 && buildcfg.GOOS == "plan9" {
+ flag.BoolVar(&flag8, "8", false, "use 64-bit addresses in symbol table")
+ }
+ flagHeadType := flag.String("H", "", "set header `type`")
+ flag.BoolVar(&ctxt.linkShared, "linkshared", false, "link against installed Go shared libraries")
+ flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")
+ flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")
+ flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")
+ objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo)
+ objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
+ objabi.AddVersionFlag() // -V
+ objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
+ objabi.Flagcount("v", "print link trace", &ctxt.Debugvlog)
+ objabi.Flagfn1("importcfg", "read import configuration from `file`", ctxt.readImportCfg)
+
+ objabi.Flagparse(usage)
+
+ if ctxt.Debugvlog > 0 {
+ // dump symbol info on crash
+ defer func() { ctxt.loader.Dump() }()
+ }
+
+ switch *flagHeadType {
+ case "":
+ case "windowsgui":
+ ctxt.HeadType = objabi.Hwindows
+ windowsgui = true
+ default:
+ if err := ctxt.HeadType.Set(*flagHeadType); err != nil {
+ Errorf(nil, "%v", err)
+ usage()
+ }
+ }
+ if ctxt.HeadType == objabi.Hunknown {
+ ctxt.HeadType.Set(buildcfg.GOOS)
+ }
+
+ if !*flagAslr && ctxt.BuildMode != BuildModeCShared {
+ Errorf(nil, "-aslr=false is only allowed for -buildmode=c-shared")
+ usage()
+ }
+
+ if *FlagD && ctxt.UsesLibc() {
+ Exitf("dynamic linking required on %s; -d flag cannot be used", buildcfg.GOOS)
+ }
+
+ checkStrictDups = *FlagStrictDups
+
+ if ctxt.IsDarwin() && ctxt.BuildMode == BuildModeCShared {
+ *FlagW = true // default to -w in c-shared mode on darwin, see #61229
+ }
+
+ if !buildcfg.Experiment.RegabiWrappers {
+ abiInternalVer = 0
+ }
+
+ startProfile()
+ if ctxt.BuildMode == BuildModeUnset {
+ ctxt.BuildMode.Set("exe")
+ }
+
+ if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 {
+ usage()
+ }
+
+ if *flagOutfile == "" {
+ *flagOutfile = "a.out"
+ if ctxt.HeadType == objabi.Hwindows {
+ *flagOutfile += ".exe"
+ }
+ }
+
+ interpreter = *flagInterpreter
+
+ if *flagBuildid == "" && ctxt.Target.IsOpenbsd() {
+ // TODO(jsing): Remove once direct syscalls are no longer in use.
+ // OpenBSD 6.7 onwards will not permit direct syscalls from a
+ // dynamically linked binary unless it identifies the binary
+ // contains a .note.go.buildid ELF note. See issue #36435.
+ *flagBuildid = "go-openbsd"
+ }
+
+ // enable benchmarking
+ var bench *benchmark.Metrics
+ if len(*benchmarkFlag) != 0 {
+ if *benchmarkFlag == "mem" {
+ bench = benchmark.New(benchmark.GC, *benchmarkFileFlag)
+ } else if *benchmarkFlag == "cpu" {
+ bench = benchmark.New(benchmark.NoGC, *benchmarkFileFlag)
+ } else {
+ Errorf(nil, "unknown benchmark flag: %q", *benchmarkFlag)
+ usage()
+ }
+ }
+
+ bench.Start("libinit")
+ libinit(ctxt) // creates outfile
+ bench.Start("computeTLSOffset")
+ ctxt.computeTLSOffset()
+ bench.Start("Archinit")
+ thearch.Archinit(ctxt)
+
+ if ctxt.linkShared && !ctxt.IsELF {
+ Exitf("-linkshared can only be used on elf systems")
+ }
+
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("HEADER = -H%d -T0x%x -R0x%x\n", ctxt.HeadType, uint64(*FlagTextAddr), uint32(*FlagRound))
+ }
+
+ zerofp := goobj.FingerprintType{}
+ switch ctxt.BuildMode {
+ case BuildModeShared:
+ for i := 0; i < flag.NArg(); i++ {
+ arg := flag.Arg(i)
+ parts := strings.SplitN(arg, "=", 2)
+ var pkgpath, file string
+ if len(parts) == 1 {
+ pkgpath, file = "main", arg
+ } else {
+ pkgpath, file = parts[0], parts[1]
+ }
+ pkglistfornote = append(pkglistfornote, pkgpath...)
+ pkglistfornote = append(pkglistfornote, '\n')
+ addlibpath(ctxt, "command line", "command line", file, pkgpath, "", zerofp)
+ }
+ case BuildModePlugin:
+ addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "", zerofp)
+ default:
+ addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "", zerofp)
+ }
+ bench.Start("loadlib")
+ ctxt.loadlib()
+
+ bench.Start("deadcode")
+ deadcode(ctxt)
+
+ bench.Start("linksetup")
+ ctxt.linksetup()
+
+ bench.Start("dostrdata")
+ ctxt.dostrdata()
+ if buildcfg.Experiment.FieldTrack {
+ bench.Start("fieldtrack")
+ fieldtrack(ctxt.Arch, ctxt.loader)
+ }
+
+ bench.Start("dwarfGenerateDebugInfo")
+ dwarfGenerateDebugInfo(ctxt)
+
+ bench.Start("callgraph")
+ ctxt.callgraph()
+
+ bench.Start("doStackCheck")
+ ctxt.doStackCheck()
+
+ bench.Start("mangleTypeSym")
+ ctxt.mangleTypeSym()
+
+ if ctxt.IsELF {
+ bench.Start("doelf")
+ ctxt.doelf()
+ }
+ if ctxt.IsDarwin() {
+ bench.Start("domacho")
+ ctxt.domacho()
+ }
+ if ctxt.IsWindows() {
+ bench.Start("dope")
+ ctxt.dope()
+ bench.Start("windynrelocsyms")
+ ctxt.windynrelocsyms()
+ }
+ if ctxt.IsAIX() {
+ bench.Start("doxcoff")
+ ctxt.doxcoff()
+ }
+
+ bench.Start("textbuildid")
+ ctxt.textbuildid()
+ bench.Start("addexport")
+ ctxt.setArchSyms()
+ ctxt.addexport()
+ bench.Start("Gentext")
+ thearch.Gentext(ctxt, ctxt.loader) // trampolines, call stubs, etc.
+
+ bench.Start("textaddress")
+ ctxt.textaddress()
+ bench.Start("typelink")
+ ctxt.typelink()
+ bench.Start("buildinfo")
+ ctxt.buildinfo()
+ bench.Start("pclntab")
+ containers := ctxt.findContainerSyms()
+ pclnState := ctxt.pclntab(containers)
+ bench.Start("findfunctab")
+ ctxt.findfunctab(pclnState, containers)
+ bench.Start("dwarfGenerateDebugSyms")
+ dwarfGenerateDebugSyms(ctxt)
+ bench.Start("symtab")
+ symGroupType := ctxt.symtab(pclnState)
+ bench.Start("dodata")
+ ctxt.dodata(symGroupType)
+ bench.Start("address")
+ order := ctxt.address()
+ bench.Start("dwarfcompress")
+ dwarfcompress(ctxt)
+ bench.Start("layout")
+ filesize := ctxt.layout(order)
+
+ // Write out the output file.
+ // It is split into two parts (Asmb and Asmb2). The first
+ // part writes most of the content (sections and segments),
+ // for which we have computed the size and offset, in a
+ // mmap'd region. The second part writes more content, for
+ // which we don't know the size.
+ if ctxt.Arch.Family != sys.Wasm {
+ // Don't mmap if we're building for Wasm. Wasm file
+ // layout is very different so filesize is meaningless.
+ if err := ctxt.Out.Mmap(filesize); err != nil {
+ Exitf("mapping output file failed: %v", err)
+ }
+ }
+ // asmb will redirect symbols to the output file mmap, and relocations
+ // will be applied directly there.
+ bench.Start("Asmb")
+ asmb(ctxt)
+
+ exitIfErrors()
+
+ // Generate additional symbols for the native symbol table just prior
+ // to code generation.
+ bench.Start("GenSymsLate")
+ if thearch.GenSymsLate != nil {
+ thearch.GenSymsLate(ctxt, ctxt.loader)
+ }
+
+ bench.Start("Asmb2")
+ asmb2(ctxt)
+
+ bench.Start("Munmap")
+ ctxt.Out.Close() // Close handles Munmapping if necessary.
+
+ bench.Start("hostlink")
+ ctxt.hostlink()
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("%s", ctxt.loader.Stat())
+ ctxt.Logf("%d liveness data\n", liveness)
+ }
+ bench.Start("Flush")
+ ctxt.Bso.Flush()
+ bench.Start("archive")
+ ctxt.archive()
+ bench.Report(os.Stdout)
+
+ errorexit()
+}
+
+type Rpath struct {
+ set bool
+ val string
+}
+
+func (r *Rpath) Set(val string) error {
+ r.set = true
+ r.val = val
+ return nil
+}
+
+func (r *Rpath) String() string {
+ return r.val
+}
+
+func startProfile() {
+ if *cpuprofile != "" {
+ f, err := os.Create(*cpuprofile)
+ if err != nil {
+ log.Fatalf("%v", err)
+ }
+ if err := pprof.StartCPUProfile(f); err != nil {
+ log.Fatalf("%v", err)
+ }
+ AtExit(pprof.StopCPUProfile)
+ }
+ if *memprofile != "" {
+ if *memprofilerate != 0 {
+ runtime.MemProfileRate = int(*memprofilerate)
+ }
+ f, err := os.Create(*memprofile)
+ if err != nil {
+ log.Fatalf("%v", err)
+ }
+ AtExit(func() {
+ // Profile all outstanding allocations.
+ runtime.GC()
+ // compilebench parses the memory profile to extract memstats,
+ // which are only written in the legacy pprof format.
+ // See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap.
+ const writeLegacyFormat = 1
+ if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
+ log.Fatalf("%v", err)
+ }
+ })
+ }
+}
diff --git a/src/cmd/link/internal/ld/msync_darwin_libc.go b/src/cmd/link/internal/ld/msync_darwin_libc.go
new file mode 100644
index 0000000..eb2a526
--- /dev/null
+++ b/src/cmd/link/internal/ld/msync_darwin_libc.go
@@ -0,0 +1,12 @@
+// Copyright 2022 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.
+
+//go:build darwin && go1.20
+
+package ld
+
+import _ "unsafe" // for go:linkname
+
+//go:linkname msync syscall.msync
+func msync(b []byte, flags int) (err error)
diff --git a/src/cmd/link/internal/ld/msync_darwin_syscall.go b/src/cmd/link/internal/ld/msync_darwin_syscall.go
new file mode 100644
index 0000000..270d9f3
--- /dev/null
+++ b/src/cmd/link/internal/ld/msync_darwin_syscall.go
@@ -0,0 +1,24 @@
+// Copyright 2022 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.
+
+//go:build darwin && !go1.20
+
+package ld
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func msync(b []byte, flags int) (err error) {
+ var p unsafe.Pointer
+ if len(b) > 0 {
+ p = unsafe.Pointer(&b[0])
+ }
+ _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(p), uintptr(len(b)), uintptr(flags))
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
diff --git a/src/cmd/link/internal/ld/nooptcgolink_test.go b/src/cmd/link/internal/ld/nooptcgolink_test.go
new file mode 100644
index 0000000..646583f
--- /dev/null
+++ b/src/cmd/link/internal/ld/nooptcgolink_test.go
@@ -0,0 +1,29 @@
+// Copyright 2017 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.
+
+package ld
+
+import (
+ "internal/testenv"
+ "path/filepath"
+ "testing"
+)
+
+func TestNooptCgoBuild(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ t.Parallel()
+
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ dir := t.TempDir()
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out"))
+ cmd.Dir = filepath.Join(testenv.GOROOT(t), "src", "runtime", "testdata", "testprogcgo")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("go build output: %s", out)
+ t.Fatal(err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go
new file mode 100644
index 0000000..54fafca
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -0,0 +1,325 @@
+// Copyright 2017 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.
+
+package ld
+
+import (
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "encoding/binary"
+ "errors"
+ "log"
+ "os"
+)
+
+// If fallocate is not supported on this platform, return this error. The error
+// is ignored where needed, and OutBuf writes to heap memory.
+var errNoFallocate = errors.New("operation not supported")
+
+const outbufMode = 0775
+
+// OutBuf is a buffered file writer.
+//
+// It is similar to the Writer in cmd/internal/bio with a few small differences.
+//
+// First, it tracks the output architecture and uses it to provide
+// endian helpers.
+//
+// Second, it provides a very cheap offset counter that doesn't require
+// 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
+//
+// And finally, it provides a mechanism by which you can multithread the
+// writing of output files. This mechanism is accomplished by copying a OutBuf,
+// and using it in the thread/goroutine.
+//
+// Parallel OutBuf is intended to be used like:
+//
+// func write(out *OutBuf) {
+// var wg sync.WaitGroup
+// for i := 0; i < 10; i++ {
+// wg.Add(1)
+// view, err := out.View(start[i])
+// if err != nil {
+// // handle output
+// continue
+// }
+// go func(out *OutBuf, i int) {
+// // do output
+// wg.Done()
+// }(view, i)
+// }
+// wg.Wait()
+// }
+type OutBuf struct {
+ arch *sys.Arch
+ off int64
+
+ buf []byte // backing store of mmap'd output file
+ heap []byte // backing store for non-mmapped data
+
+ name string
+ f *os.File
+ encbuf [8]byte // temp buffer used by WriteN methods
+ isView bool // true if created from View()
+}
+
+func (out *OutBuf) Open(name string) error {
+ if out.f != nil {
+ return errors.New("cannot open more than one file")
+ }
+ f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, outbufMode)
+ if err != nil {
+ return err
+ }
+ out.off = 0
+ out.name = name
+ out.f = f
+ return nil
+}
+
+func NewOutBuf(arch *sys.Arch) *OutBuf {
+ return &OutBuf{
+ arch: arch,
+ }
+}
+
+var viewError = errors.New("output not mmapped")
+
+func (out *OutBuf) View(start uint64) (*OutBuf, error) {
+ return &OutBuf{
+ arch: out.arch,
+ name: out.name,
+ buf: out.buf,
+ heap: out.heap,
+ off: int64(start),
+ isView: true,
+ }, nil
+}
+
+var viewCloseError = errors.New("cannot Close OutBuf from View")
+
+func (out *OutBuf) Close() error {
+ if out.isView {
+ return viewCloseError
+ }
+ if out.isMmapped() {
+ out.copyHeap()
+ out.purgeSignatureCache()
+ out.munmap()
+ }
+ if out.f == nil {
+ return nil
+ }
+ if len(out.heap) != 0 {
+ if _, err := out.f.Write(out.heap); err != nil {
+ return err
+ }
+ }
+ if err := out.f.Close(); err != nil {
+ return err
+ }
+ out.f = nil
+ return nil
+}
+
+// ErrorClose closes the output file (if any).
+// It is supposed to be called only at exit on error, so it doesn't do
+// any clean up or buffer flushing, just closes the file.
+func (out *OutBuf) ErrorClose() {
+ if out.isView {
+ panic(viewCloseError)
+ }
+ if out.f == nil {
+ return
+ }
+ out.f.Close() // best effort, ignore error
+ out.f = nil
+}
+
+// isMmapped returns true if the OutBuf is mmaped.
+func (out *OutBuf) isMmapped() bool {
+ return len(out.buf) != 0
+}
+
+// Data returns the whole written OutBuf as a byte slice.
+func (out *OutBuf) Data() []byte {
+ if out.isMmapped() {
+ out.copyHeap()
+ return out.buf
+ }
+ return out.heap
+}
+
+// copyHeap copies the heap to the mmapped section of memory, returning true if
+// a copy takes place.
+func (out *OutBuf) copyHeap() bool {
+ if !out.isMmapped() { // only valuable for mmapped OutBufs.
+ return false
+ }
+ if out.isView {
+ panic("can't copyHeap a view")
+ }
+
+ bufLen := len(out.buf)
+ heapLen := len(out.heap)
+ total := uint64(bufLen + heapLen)
+ if heapLen != 0 {
+ if err := out.Mmap(total); err != nil { // Mmap will copy out.heap over to out.buf
+ Exitf("mapping output file failed: %v", err)
+ }
+ }
+ return true
+}
+
+// maxOutBufHeapLen limits the growth of the heap area.
+const maxOutBufHeapLen = 10 << 20
+
+// writeLoc determines the write location if a buffer is mmaped.
+// We maintain two write buffers, an mmapped section, and a heap section for
+// writing. When the mmapped section is full, we switch over the heap memory
+// for writing.
+func (out *OutBuf) writeLoc(lenToWrite int64) (int64, []byte) {
+ // See if we have enough space in the mmaped area.
+ bufLen := int64(len(out.buf))
+ if out.off+lenToWrite <= bufLen {
+ return out.off, out.buf
+ }
+
+ // Not enough space in the mmaped area, write to heap area instead.
+ heapPos := out.off - bufLen
+ heapLen := int64(len(out.heap))
+ lenNeeded := heapPos + lenToWrite
+ if lenNeeded > heapLen { // do we need to grow the heap storage?
+ // The heap variables aren't protected by a mutex. For now, just bomb if you
+ // try to use OutBuf in parallel. (Note this probably could be fixed.)
+ if out.isView {
+ panic("cannot write to heap in parallel")
+ }
+ // See if our heap would grow to be too large, and if so, copy it to the end
+ // of the mmapped area.
+ if heapLen > maxOutBufHeapLen && out.copyHeap() {
+ heapPos -= heapLen
+ lenNeeded = heapPos + lenToWrite
+ heapLen = 0
+ }
+ out.heap = append(out.heap, make([]byte, lenNeeded-heapLen)...)
+ }
+ return heapPos, out.heap
+}
+
+func (out *OutBuf) SeekSet(p int64) {
+ out.off = p
+}
+
+func (out *OutBuf) Offset() int64 {
+ return out.off
+}
+
+// Write writes the contents of v to the buffer.
+func (out *OutBuf) Write(v []byte) (int, error) {
+ n := len(v)
+ pos, buf := out.writeLoc(int64(n))
+ copy(buf[pos:], v)
+ out.off += int64(n)
+ return n, nil
+}
+
+func (out *OutBuf) Write8(v uint8) {
+ pos, buf := out.writeLoc(1)
+ buf[pos] = v
+ out.off++
+}
+
+// WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface.
+func (out *OutBuf) WriteByte(v byte) error {
+ out.Write8(v)
+ return nil
+}
+
+func (out *OutBuf) Write16(v uint16) {
+ out.arch.ByteOrder.PutUint16(out.encbuf[:], v)
+ out.Write(out.encbuf[:2])
+}
+
+func (out *OutBuf) Write32(v uint32) {
+ out.arch.ByteOrder.PutUint32(out.encbuf[:], v)
+ out.Write(out.encbuf[:4])
+}
+
+func (out *OutBuf) Write32b(v uint32) {
+ binary.BigEndian.PutUint32(out.encbuf[:], v)
+ out.Write(out.encbuf[:4])
+}
+
+func (out *OutBuf) Write64(v uint64) {
+ out.arch.ByteOrder.PutUint64(out.encbuf[:], v)
+ out.Write(out.encbuf[:8])
+}
+
+func (out *OutBuf) Write64b(v uint64) {
+ binary.BigEndian.PutUint64(out.encbuf[:], v)
+ out.Write(out.encbuf[:8])
+}
+
+func (out *OutBuf) WriteString(s string) {
+ pos, buf := out.writeLoc(int64(len(s)))
+ n := copy(buf[pos:], s)
+ if n != len(s) {
+ log.Fatalf("WriteString truncated. buffer size: %d, offset: %d, len(s)=%d", len(out.buf), out.off, len(s))
+ }
+ out.off += int64(n)
+}
+
+// WriteStringN writes the first n bytes of s.
+// If n is larger than len(s) then it is padded with zero bytes.
+func (out *OutBuf) WriteStringN(s string, n int) {
+ out.WriteStringPad(s, n, zeros[:])
+}
+
+// WriteStringPad writes the first n bytes of s.
+// If n is larger than len(s) then it is padded with the bytes in pad (repeated as needed).
+func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) {
+ if len(s) >= n {
+ out.WriteString(s[:n])
+ } else {
+ out.WriteString(s)
+ n -= len(s)
+ for n > len(pad) {
+ out.Write(pad)
+ n -= len(pad)
+
+ }
+ out.Write(pad[:n])
+ }
+}
+
+// WriteSym writes the content of a Symbol, and returns the output buffer
+// that we just wrote, so we can apply further edit to the symbol content.
+// For generator symbols, it also sets the symbol's Data to the output
+// buffer.
+func (out *OutBuf) WriteSym(ldr *loader.Loader, s loader.Sym) []byte {
+ if !ldr.IsGeneratedSym(s) {
+ P := ldr.Data(s)
+ n := int64(len(P))
+ pos, buf := out.writeLoc(n)
+ copy(buf[pos:], P)
+ out.off += n
+ ldr.FreeData(s)
+ return buf[pos : pos+n]
+ } else {
+ n := ldr.SymSize(s)
+ pos, buf := out.writeLoc(n)
+ out.off += n
+ ldr.MakeSymbolUpdater(s).SetData(buf[pos : pos+n])
+ return buf[pos : pos+n]
+ }
+}
diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go
new file mode 100644
index 0000000..17f7e2a
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_darwin.go
@@ -0,0 +1,48 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// Implemented in the syscall package.
+//
+//go:linkname fcntl syscall.fcntl
+func fcntl(fd int, cmd int, arg int) (int, error)
+
+func (out *OutBuf) fallocate(size uint64) error {
+ stat, err := out.f.Stat()
+ if err != nil {
+ return err
+ }
+ // F_PEOFPOSMODE allocates from the end of the file, so we want the size difference.
+ // Apparently, it uses the end of the allocation, instead of the logical end of the
+ // file.
+ cursize := uint64(stat.Sys().(*syscall.Stat_t).Blocks * 512) // allocated size
+ if size <= cursize {
+ return nil
+ }
+
+ store := &syscall.Fstore_t{
+ Flags: syscall.F_ALLOCATEALL,
+ Posmode: syscall.F_PEOFPOSMODE,
+ Offset: 0,
+ Length: int64(size - cursize),
+ }
+
+ _, err = fcntl(int(out.f.Fd()), syscall.F_PREALLOCATE, int(uintptr(unsafe.Pointer(store))))
+ return err
+}
+
+func (out *OutBuf) purgeSignatureCache() {
+ // Apparently, the Darwin kernel may cache the code signature at mmap.
+ // When we mmap the output buffer, it doesn't have a code signature
+ // (as we haven't generated one). Invalidate the kernel cache now that
+ // we have generated the signature. See issue #42684.
+ msync(out.buf, syscall.MS_INVALIDATE)
+ // Best effort. Ignore error.
+}
diff --git a/src/cmd/link/internal/ld/outbuf_linux.go b/src/cmd/link/internal/ld/outbuf_linux.go
new file mode 100644
index 0000000..bd9a0c6
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_linux.go
@@ -0,0 +1,11 @@
+// Copyright 2020 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.
+
+package ld
+
+import "syscall"
+
+func (out *OutBuf) fallocate(size uint64) error {
+ return syscall.Fallocate(int(out.f.Fd()), 0, 0, int64(size))
+}
diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go
new file mode 100644
index 0000000..7bb728a
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_mmap.go
@@ -0,0 +1,59 @@
+// Copyright 2019 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.
+
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || (solaris && go1.20)
+
+package ld
+
+import (
+ "syscall"
+)
+
+// Mmap maps the output file with the given size. It unmaps the old mapping
+// if it is already mapped. It also flushes any in-heap data to the new
+// mapping.
+func (out *OutBuf) Mmap(filesize uint64) (err error) {
+ oldlen := len(out.buf)
+ if oldlen != 0 {
+ out.munmap()
+ }
+
+ for {
+ if err = out.fallocate(filesize); err != syscall.EINTR {
+ break
+ }
+ }
+ if err != nil {
+ // Some file systems do not support fallocate. We ignore that error as linking
+ // can still take place, but you might SIGBUS when you write to the mmapped
+ // area.
+ if err != syscall.ENOTSUP && err != syscall.EPERM && err != errNoFallocate {
+ return err
+ }
+ }
+ err = out.f.Truncate(int64(filesize))
+ if err != nil {
+ Exitf("resize output file failed: %v", err)
+ }
+ out.buf, err = syscall.Mmap(int(out.f.Fd()), 0, int(filesize), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FILE)
+ if err != nil {
+ return err
+ }
+
+ // copy heap to new mapping
+ if uint64(oldlen+len(out.heap)) > filesize {
+ panic("mmap size too small")
+ }
+ copy(out.buf[oldlen:], out.heap)
+ out.heap = out.heap[:0]
+ return nil
+}
+
+func (out *OutBuf) munmap() {
+ if out.buf == nil {
+ return
+ }
+ syscall.Munmap(out.buf)
+ out.buf = nil
+}
diff --git a/src/cmd/link/internal/ld/outbuf_nofallocate.go b/src/cmd/link/internal/ld/outbuf_nofallocate.go
new file mode 100644
index 0000000..dd5afc6
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_nofallocate.go
@@ -0,0 +1,11 @@
+// Copyright 2020 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.
+
+//go:build !darwin && !linux
+
+package ld
+
+func (out *OutBuf) fallocate(size uint64) error {
+ return errNoFallocate
+}
diff --git a/src/cmd/link/internal/ld/outbuf_nommap.go b/src/cmd/link/internal/ld/outbuf_nommap.go
new file mode 100644
index 0000000..b1d3d27
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_nommap.go
@@ -0,0 +1,22 @@
+// Copyright 2019 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.
+
+//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !(solaris && go1.20) && !windows
+
+package ld
+
+// Mmap allocates an in-heap output buffer with the given size. It copies
+// any old data (if any) to the new buffer.
+func (out *OutBuf) Mmap(filesize uint64) error {
+ // We need space to put all the symbols before we apply relocations.
+ oldheap := out.heap
+ if filesize < uint64(len(oldheap)) {
+ panic("mmap size too small")
+ }
+ out.heap = make([]byte, filesize)
+ copy(out.heap, oldheap)
+ return nil
+}
+
+func (out *OutBuf) munmap() { panic("unreachable") }
diff --git a/src/cmd/link/internal/ld/outbuf_notdarwin.go b/src/cmd/link/internal/ld/outbuf_notdarwin.go
new file mode 100644
index 0000000..f9caa41
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_notdarwin.go
@@ -0,0 +1,10 @@
+// Copyright 2020 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.
+
+//go:build !darwin
+// +build !darwin
+
+package ld
+
+func (out *OutBuf) purgeSignatureCache() {}
diff --git a/src/cmd/link/internal/ld/outbuf_test.go b/src/cmd/link/internal/ld/outbuf_test.go
new file mode 100644
index 0000000..a7b105f
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_test.go
@@ -0,0 +1,93 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+// TestMMap ensures that we can actually mmap on every supported platform.
+func TestMMap(t *testing.T) {
+ switch runtime.GOOS {
+ default:
+ t.Skip("unsupported OS")
+ case "aix", "darwin", "ios", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "windows":
+ }
+ dir := t.TempDir()
+ filename := filepath.Join(dir, "foo.out")
+ ob := NewOutBuf(nil)
+ if err := ob.Open(filename); err != nil {
+ t.Fatalf("error opening file: %v", err)
+ }
+ defer ob.Close()
+ if err := ob.Mmap(1 << 20); err != nil {
+ t.Errorf("error mmapping file %v", err)
+ }
+ if !ob.isMmapped() {
+ t.Errorf("should be mmapped")
+ }
+}
+
+// TestWriteLoc ensures that the math surrounding writeLoc is correct.
+func TestWriteLoc(t *testing.T) {
+ tests := []struct {
+ bufLen int
+ off int64
+ heapLen int
+ lenToWrite int64
+ expectedHeapLen int
+ writePos int64
+ addressInHeap bool
+ }{
+ {100, 0, 0, 100, 0, 0, false},
+ {100, 100, 0, 100, 100, 0, true},
+ {10, 10, 0, 100, 100, 0, true},
+ {10, 20, 10, 100, 110, 10, true},
+ {0, 0, 0, 100, 100, 0, true},
+ }
+
+ for i, test := range tests {
+ ob := &OutBuf{
+ buf: make([]byte, test.bufLen),
+ off: test.off,
+ heap: make([]byte, test.heapLen),
+ }
+ pos, buf := ob.writeLoc(test.lenToWrite)
+ if pos != test.writePos {
+ t.Errorf("[%d] position = %d, expected %d", i, pos, test.writePos)
+ }
+ message := "mmapped area"
+ expected := ob.buf
+ if test.addressInHeap {
+ message = "heap"
+ expected = ob.heap
+ }
+ if &buf[0] != &expected[0] {
+ t.Errorf("[%d] expected position to be %q", i, message)
+ }
+ if len(ob.heap) != test.expectedHeapLen {
+ t.Errorf("[%d] expected len(ob.heap) == %d, got %d", i, test.expectedHeapLen, len(ob.heap))
+ }
+ }
+}
+
+func TestIsMmapped(t *testing.T) {
+ tests := []struct {
+ length int
+ expected bool
+ }{
+ {0, false},
+ {1, true},
+ }
+ for i, test := range tests {
+ ob := &OutBuf{buf: make([]byte, test.length)}
+ if v := ob.isMmapped(); v != test.expected {
+
+ t.Errorf("[%d] isMmapped == %t, expected %t", i, v, test.expected)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/outbuf_windows.go b/src/cmd/link/internal/ld/outbuf_windows.go
new file mode 100644
index 0000000..95937e7
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_windows.go
@@ -0,0 +1,79 @@
+// Copyright 2019 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.
+
+package ld
+
+import (
+ "internal/unsafeheader"
+ "syscall"
+ "unsafe"
+)
+
+// Mmap maps the output file with the given size. It unmaps the old mapping
+// if it is already mapped. It also flushes any in-heap data to the new
+// mapping.
+func (out *OutBuf) Mmap(filesize uint64) error {
+ oldlen := len(out.buf)
+ if oldlen != 0 {
+ out.munmap()
+ }
+
+ err := out.f.Truncate(int64(filesize))
+ if err != nil {
+ Exitf("resize output file failed: %v", err)
+ }
+
+ low, high := uint32(filesize), uint32(filesize>>32)
+ fmap, err := syscall.CreateFileMapping(syscall.Handle(out.f.Fd()), nil, syscall.PAGE_READWRITE, high, low, nil)
+ if err != nil {
+ return err
+ }
+ defer syscall.CloseHandle(fmap)
+
+ ptr, err := syscall.MapViewOfFile(fmap, syscall.FILE_MAP_READ|syscall.FILE_MAP_WRITE, 0, 0, uintptr(filesize))
+ if err != nil {
+ return err
+ }
+ bufHdr := (*unsafeheader.Slice)(unsafe.Pointer(&out.buf))
+ bufHdr.Data = unsafe.Pointer(ptr)
+ bufHdr.Len = int(filesize)
+ bufHdr.Cap = int(filesize)
+
+ // copy heap to new mapping
+ if uint64(oldlen+len(out.heap)) > filesize {
+ panic("mmap size too small")
+ }
+ copy(out.buf[oldlen:], out.heap)
+ out.heap = out.heap[:0]
+ return nil
+}
+
+func (out *OutBuf) munmap() {
+ if out.buf == nil {
+ return
+ }
+ // Apparently unmapping without flush may cause ACCESS_DENIED error
+ // (see issue 38440).
+ err := syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])), 0)
+ if err != nil {
+ Exitf("FlushViewOfFile failed: %v", err)
+ }
+ // Issue 44817: apparently the call below may be needed (according
+ // to the Windows docs) in addition to the FlushViewOfFile call
+ // above, " ... to flush all the dirty pages plus the metadata for
+ // the file and ensure that they are physically written to disk".
+ // Windows DOC links:
+ //
+ // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-flushviewoffile
+ // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers
+ err = syscall.FlushFileBuffers(syscall.Handle(out.f.Fd()))
+ if err != nil {
+ Exitf("FlushFileBuffers failed: %v", err)
+ }
+ err = syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])))
+ out.buf = nil
+ if err != nil {
+ Exitf("UnmapViewOfFile failed: %v", err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
new file mode 100644
index 0000000..34ab86c
--- /dev/null
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -0,0 +1,935 @@
+// Copyright 2013 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.
+
+package ld
+
+import (
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "fmt"
+ "internal/buildcfg"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+const funcSize = 11 * 4 // funcSize is the size of the _func object in runtime/runtime2.go
+
+// pclntab holds the state needed for pclntab generation.
+type pclntab struct {
+ // The first and last functions found.
+ firstFunc, lastFunc loader.Sym
+
+ // Running total size of pclntab.
+ size int64
+
+ // runtime.pclntab's symbols
+ carrier loader.Sym
+ pclntab loader.Sym
+ pcheader loader.Sym
+ funcnametab loader.Sym
+ findfunctab loader.Sym
+ cutab loader.Sym
+ filetab loader.Sym
+ pctab loader.Sym
+
+ // The number of functions + number of TEXT sections - 1. This is such an
+ // unexpected value because platforms that have more than one TEXT section
+ // get a dummy function inserted between because the external linker can place
+ // functions in those areas. We mark those areas as not covered by the Go
+ // runtime.
+ //
+ // On most platforms this is the number of reachable functions.
+ nfunc int32
+
+ // The number of filenames in runtime.filetab.
+ nfiles uint32
+}
+
+// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym.
+// It is the caller's responsibility to save the symbol in state.
+func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f generatorFunc) loader.Sym {
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
+ state.size += size
+ s := ctxt.createGeneratorSymbol(name, 0, sym.SPCLNTAB, size, f)
+ ctxt.loader.SetAttrReachable(s, true)
+ ctxt.loader.SetCarrierSym(s, state.carrier)
+ ctxt.loader.SetAttrNotInSymbolTable(s, true)
+ return s
+}
+
+// makePclntab makes a pclntab object, and assembles all the compilation units
+// we'll need to write pclntab. Returns the pclntab structure, a slice of the
+// CompilationUnits we need, and a slice of the function symbols we need to
+// generate pclntab.
+func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) {
+ ldr := ctxt.loader
+ state := new(pclntab)
+
+ // Gather some basic stats and info.
+ seenCUs := make(map[*sym.CompilationUnit]struct{})
+ compUnits := []*sym.CompilationUnit{}
+ funcs := []loader.Sym{}
+
+ for _, s := range ctxt.Textp {
+ if !emitPcln(ctxt, s, container) {
+ continue
+ }
+ funcs = append(funcs, s)
+ state.nfunc++
+ if state.firstFunc == 0 {
+ state.firstFunc = s
+ }
+ state.lastFunc = s
+
+ // We need to keep track of all compilation units we see. Some symbols
+ // (eg, go.buildid, _cgoexp_, etc) won't have a compilation unit.
+ cu := ldr.SymUnit(s)
+ if _, ok := seenCUs[cu]; cu != nil && !ok {
+ seenCUs[cu] = struct{}{}
+ cu.PclnIndex = len(compUnits)
+ compUnits = append(compUnits, cu)
+ }
+ }
+ return state, compUnits, funcs
+}
+
+func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
+ // We want to generate func table entries only for the "lowest
+ // level" symbols, not containers of subsymbols.
+ return !container.Has(s)
+}
+
+func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 {
+ ldr := ctxt.loader
+ target := ctxt.Target
+ deferreturn := uint32(0)
+ lastWasmAddr := uint32(0)
+
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if target.IsWasm() && r.Type() == objabi.R_ADDR {
+ // wasm/ssa.go generates an ARESUMEPOINT just
+ // before the deferreturn call. The "PC" of
+ // the deferreturn call is stored in the
+ // R_ADDR relocation on the ARESUMEPOINT.
+ lastWasmAddr = uint32(r.Add())
+ }
+ if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) {
+ if target.IsWasm() {
+ deferreturn = lastWasmAddr - 1
+ } else {
+ // Note: the relocation target is in the call instruction, but
+ // is not necessarily the whole instruction (for instance, on
+ // x86 the relocation applies to bytes [1:5] of the 5 byte call
+ // instruction).
+ deferreturn = uint32(r.Off())
+ switch target.Arch.Family {
+ case sys.AMD64, sys.I386:
+ deferreturn--
+ case sys.ARM, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64:
+ // no change
+ case sys.S390X:
+ deferreturn -= 2
+ default:
+ panic(fmt.Sprint("Unhandled architecture:", target.Arch.Family))
+ }
+ }
+ break // only need one
+ }
+ }
+ return deferreturn
+}
+
+// genInlTreeSym generates the InlTree sym for a function with the
+// specified FuncInfo.
+func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym {
+ ldr := ctxt.loader
+ its := ldr.CreateExtSym("", 0)
+ inlTreeSym := ldr.MakeSymbolUpdater(its)
+ // Note: the generated symbol is given a type of sym.SGOFUNC, as a
+ // signal to the symtab() phase that it needs to be grouped in with
+ // other similar symbols (gcdata, etc); the dodata() phase will
+ // eventually switch the type back to SRODATA.
+ inlTreeSym.SetType(sym.SGOFUNC)
+ ldr.SetAttrReachable(its, true)
+ ldr.SetSymAlign(its, 4) // it has 32-bit fields
+ ninl := fi.NumInlTree()
+ for i := 0; i < int(ninl); i++ {
+ call := fi.InlTree(i)
+ nameOff, ok := nameOffsets[call.Func]
+ if !ok {
+ panic("couldn't find function name offset")
+ }
+
+ inlFunc := ldr.FuncInfo(call.Func)
+ var funcID objabi.FuncID
+ startLine := int32(0)
+ if inlFunc.Valid() {
+ funcID = inlFunc.FuncID()
+ startLine = inlFunc.StartLine()
+ } else if !ctxt.linkShared {
+ // Inlined functions are always Go functions, and thus
+ // must have FuncInfo.
+ //
+ // Unfortunately, with -linkshared, the inlined
+ // function may be external symbols (from another
+ // shared library), and we don't load FuncInfo from the
+ // shared library. We will report potentially incorrect
+ // FuncID in this case. See https://go.dev/issue/55954.
+ panic(fmt.Sprintf("inlined function %s missing func info", ldr.SymName(call.Func)))
+ }
+
+ // Construct runtime.inlinedCall value.
+ const size = 16
+ inlTreeSym.SetUint8(arch, int64(i*size+0), uint8(funcID))
+ // Bytes 1-3 are unused.
+ inlTreeSym.SetUint32(arch, int64(i*size+4), uint32(nameOff))
+ inlTreeSym.SetUint32(arch, int64(i*size+8), uint32(call.ParentPC))
+ inlTreeSym.SetUint32(arch, int64(i*size+12), uint32(startLine))
+ }
+ return its
+}
+
+// makeInlSyms returns a map of loader.Sym that are created inlSyms.
+func makeInlSyms(ctxt *Link, funcs []loader.Sym, nameOffsets map[loader.Sym]uint32) map[loader.Sym]loader.Sym {
+ ldr := ctxt.loader
+ // Create the inline symbols we need.
+ inlSyms := make(map[loader.Sym]loader.Sym)
+ for _, s := range funcs {
+ if fi := ldr.FuncInfo(s); fi.Valid() {
+ fi.Preload()
+ if fi.NumInlTree() > 0 {
+ inlSyms[s] = genInlTreeSym(ctxt, ldr.SymUnit(s), fi, ctxt.Arch, nameOffsets)
+ }
+ }
+ }
+ return inlSyms
+}
+
+// generatePCHeader creates the runtime.pcheader symbol, setting it up as a
+// generator to fill in its data later.
+func (state *pclntab) generatePCHeader(ctxt *Link) {
+ ldr := ctxt.loader
+ textStartOff := int64(8 + 2*ctxt.Arch.PtrSize)
+ size := int64(8 + 8*ctxt.Arch.PtrSize)
+ writeHeader := func(ctxt *Link, s loader.Sym) {
+ header := ctxt.loader.MakeSymbolUpdater(s)
+
+ writeSymOffset := func(off int64, ws loader.Sym) int64 {
+ diff := ldr.SymValue(ws) - ldr.SymValue(s)
+ if diff <= 0 {
+ name := ldr.SymName(ws)
+ panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before %s(%x)", ldr.SymValue(s), name, ldr.SymValue(ws)))
+ }
+ return header.SetUintptr(ctxt.Arch, off, uintptr(diff))
+ }
+
+ // Write header.
+ // Keep in sync with runtime/symtab.go:pcHeader and package debug/gosym.
+ header.SetUint32(ctxt.Arch, 0, 0xfffffff1)
+ header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
+ header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
+ off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc))
+ off = header.SetUint(ctxt.Arch, off, uint64(state.nfiles))
+ if off != textStartOff {
+ panic(fmt.Sprintf("pcHeader textStartOff: %d != %d", off, textStartOff))
+ }
+ off += int64(ctxt.Arch.PtrSize) // skip runtimeText relocation
+ off = writeSymOffset(off, state.funcnametab)
+ off = writeSymOffset(off, state.cutab)
+ off = writeSymOffset(off, state.filetab)
+ off = writeSymOffset(off, state.pctab)
+ off = writeSymOffset(off, state.pclntab)
+ if off != size {
+ panic(fmt.Sprintf("pcHeader size: %d != %d", off, size))
+ }
+ }
+
+ state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
+ // Create the runtimeText relocation.
+ sb := ldr.MakeSymbolUpdater(state.pcheader)
+ sb.SetAddr(ctxt.Arch, textStartOff, ldr.Lookup("runtime.text", 0))
+}
+
+// walkFuncs iterates over the funcs, calling a function for each unique
+// function and inlined function.
+func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) {
+ ldr := ctxt.loader
+ seen := make(map[loader.Sym]struct{})
+ for _, s := range funcs {
+ if _, ok := seen[s]; !ok {
+ f(s)
+ seen[s] = struct{}{}
+ }
+
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+ for i, ni := 0, fi.NumInlTree(); i < int(ni); i++ {
+ call := fi.InlTree(i).Func
+ if _, ok := seen[call]; !ok {
+ f(call)
+ seen[call] = struct{}{}
+ }
+ }
+ }
+}
+
+// generateFuncnametab creates the function name table. Returns a map of
+// func symbol to the name offset in runtime.funcnamtab.
+func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 {
+ nameOffsets := make(map[loader.Sym]uint32, state.nfunc)
+
+ // The name used by the runtime is the concatenation of the 3 returned strings.
+ // For regular functions, only one returned string is nonempty.
+ // For generic functions, we use three parts so that we can print everything
+ // within the outermost "[]" as "...".
+ nameParts := func(name string) (string, string, string) {
+ i := strings.IndexByte(name, '[')
+ if i < 0 {
+ return name, "", ""
+ }
+ // TODO: use LastIndexByte once the bootstrap compiler is >= Go 1.5.
+ j := len(name) - 1
+ for j > i && name[j] != ']' {
+ j--
+ }
+ if j <= i {
+ return name, "", ""
+ }
+ return name[:i], "[...]", name[j+1:]
+ }
+
+ // Write the null terminated strings.
+ writeFuncNameTab := func(ctxt *Link, s loader.Sym) {
+ symtab := ctxt.loader.MakeSymbolUpdater(s)
+ for s, off := range nameOffsets {
+ a, b, c := nameParts(ctxt.loader.SymName(s))
+ o := int64(off)
+ o = symtab.AddStringAt(o, a)
+ o = symtab.AddStringAt(o, b)
+ _ = symtab.AddCStringAt(o, c)
+ }
+ }
+
+ // Loop through the CUs, and calculate the size needed.
+ var size int64
+ walkFuncs(ctxt, funcs, func(s loader.Sym) {
+ nameOffsets[s] = uint32(size)
+ a, b, c := nameParts(ctxt.loader.SymName(s))
+ size += int64(len(a) + len(b) + len(c) + 1) // NULL terminate
+ })
+
+ state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
+ return nameOffsets
+}
+
+// walkFilenames walks funcs, calling a function for each filename used in each
+// function's line table.
+func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) {
+ ldr := ctxt.loader
+
+ // Loop through all functions, finding the filenames we need.
+ for _, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+
+ cu := ldr.SymUnit(s)
+ for i, nf := 0, int(fi.NumFile()); i < nf; i++ {
+ f(cu, fi.File(i))
+ }
+ for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ {
+ call := fi.InlTree(i)
+ f(cu, call.File)
+ }
+ }
+}
+
+// generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice
+// of the index at which each CU begins in runtime.cutab.
+//
+// Function objects keep track of the files they reference to print the stack.
+// This function creates a per-CU list of filenames if CU[M] references
+// files[1-N], the following is generated:
+//
+// runtime.cutab:
+// CU[M]
+// offsetToFilename[0]
+// offsetToFilename[1]
+// ..
+//
+// runtime.filetab
+// filename[0]
+// filename[1]
+//
+// Looking up a filename then becomes:
+// 0. Given a func, and filename index [K]
+// 1. Get Func.CUIndex: M := func.cuOffset
+// 2. Find filename offset: fileOffset := runtime.cutab[M+K]
+// 3. Get the filename: getcstring(runtime.filetab[fileOffset])
+func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 {
+ // On a per-CU basis, keep track of all the filenames we need.
+ //
+ // Note, that we store the filenames in a separate section in the object
+ // files, and deduplicate based on the actual value. It would be better to
+ // store the filenames as symbols, using content addressable symbols (and
+ // then not loading extra filenames), and just use the hash value of the
+ // symbol name to do this cataloging.
+ //
+ // TODO: Store filenames as symbols. (Note this would be easiest if you
+ // also move strings to ALWAYS using the larger content addressable hash
+ // function, and use that hash value for uniqueness testing.)
+ cuEntries := make([]goobj.CUFileIndex, len(compUnits))
+ fileOffsets := make(map[string]uint32)
+
+ // Walk the filenames.
+ // We store the total filename string length we need to load, and the max
+ // file index we've seen per CU so we can calculate how large the
+ // CU->global table needs to be.
+ var fileSize int64
+ walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) {
+ // Note we use the raw filename for lookup, but use the expanded filename
+ // when we save the size.
+ filename := cu.FileTable[i]
+ if _, ok := fileOffsets[filename]; !ok {
+ fileOffsets[filename] = uint32(fileSize)
+ fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate
+ }
+
+ // Find the maximum file index we've seen.
+ if cuEntries[cu.PclnIndex] < i+1 {
+ cuEntries[cu.PclnIndex] = i + 1 // Store max + 1
+ }
+ })
+
+ // Calculate the size of the runtime.cutab variable.
+ var totalEntries uint32
+ cuOffsets := make([]uint32, len(cuEntries))
+ for i, entries := range cuEntries {
+ // Note, cutab is a slice of uint32, so an offset to a cu's entry is just the
+ // running total of all cu indices we've needed to store so far, not the
+ // number of bytes we've stored so far.
+ cuOffsets[i] = totalEntries
+ totalEntries += uint32(entries)
+ }
+
+ // Write cutab.
+ writeCutab := func(ctxt *Link, s loader.Sym) {
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+
+ var off int64
+ for i, max := range cuEntries {
+ // Write the per CU LUT.
+ cu := compUnits[i]
+ for j := goobj.CUFileIndex(0); j < max; j++ {
+ fileOffset, ok := fileOffsets[cu.FileTable[j]]
+ if !ok {
+ // We're looping through all possible file indices. It's possible a file's
+ // been deadcode eliminated, and although it's a valid file in the CU, it's
+ // not needed in this binary. When that happens, use an invalid offset.
+ fileOffset = ^uint32(0)
+ }
+ off = sb.SetUint32(ctxt.Arch, off, fileOffset)
+ }
+ }
+ }
+ state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab)
+
+ // Write filetab.
+ writeFiletab := func(ctxt *Link, s loader.Sym) {
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+
+ // Write the strings.
+ for filename, loc := range fileOffsets {
+ sb.AddStringAt(int64(loc), expandFile(filename))
+ }
+ }
+ state.nfiles = uint32(len(fileOffsets))
+ state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab)
+
+ return cuOffsets
+}
+
+// generatePctab creates the runtime.pctab variable, holding all the
+// deduplicated pcdata.
+func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
+ ldr := ctxt.loader
+
+ // Pctab offsets of 0 are considered invalid in the runtime. We respect
+ // that by just padding a single byte at the beginning of runtime.pctab,
+ // that way no real offsets can be zero.
+ size := int64(1)
+
+ // Walk the functions, finding offset to store each pcdata.
+ seen := make(map[loader.Sym]struct{})
+ saveOffset := func(pcSym loader.Sym) {
+ if _, ok := seen[pcSym]; !ok {
+ datSize := ldr.SymSize(pcSym)
+ if datSize != 0 {
+ ldr.SetSymValue(pcSym, size)
+ } else {
+ // Invalid PC data, record as zero.
+ ldr.SetSymValue(pcSym, 0)
+ }
+ size += datSize
+ seen[pcSym] = struct{}{}
+ }
+ }
+ var pcsp, pcline, pcfile, pcinline loader.Sym
+ var pcdata []loader.Sym
+ for _, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+ pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata)
+
+ pcSyms := []loader.Sym{pcsp, pcfile, pcline}
+ for _, pcSym := range pcSyms {
+ saveOffset(pcSym)
+ }
+ for _, pcSym := range pcdata {
+ saveOffset(pcSym)
+ }
+ if fi.NumInlTree() > 0 {
+ saveOffset(pcinline)
+ }
+ }
+
+ // TODO: There is no reason we need a generator for this variable, and it
+ // could be moved to a carrier symbol. However, carrier symbols containing
+ // carrier symbols don't work yet (as of Aug 2020). Once this is fixed,
+ // runtime.pctab could just be a carrier sym.
+ writePctab := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(s)
+ for sym := range seen {
+ sb.SetBytesAt(ldr.SymValue(sym), ldr.Data(sym))
+ }
+ }
+
+ state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
+}
+
+// numPCData returns the number of PCData syms for the FuncInfo.
+// NB: Preload must be called on valid FuncInfos before calling this function.
+func numPCData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo) uint32 {
+ if !fi.Valid() {
+ return 0
+ }
+ numPCData := uint32(ldr.NumPcdata(s))
+ if fi.NumInlTree() > 0 {
+ if numPCData < objabi.PCDATA_InlTreeIndex+1 {
+ numPCData = objabi.PCDATA_InlTreeIndex + 1
+ }
+ }
+ return numPCData
+}
+
+// generateFunctab creates the runtime.functab
+//
+// runtime.functab contains two things:
+//
+// - pc->func look up table.
+// - array of func objects, interleaved with pcdata and funcdata
+func (state *pclntab) generateFunctab(ctxt *Link, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
+ // Calculate the size of the table.
+ size, startLocations := state.calculateFunctabSize(ctxt, funcs)
+ writePcln := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(s)
+ // Write the data.
+ writePCToFunc(ctxt, sb, funcs, startLocations)
+ writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets)
+ }
+ state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln)
+}
+
+// funcData returns the funcdata and offsets for the FuncInfo.
+// The funcdata are written into runtime.functab after each func
+// object. This is a helper function to make querying the FuncInfo object
+// cleaner.
+//
+// NB: Preload must be called on the FuncInfo before calling.
+// NB: fdSyms is used as scratch space.
+func funcData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym) []loader.Sym {
+ fdSyms = fdSyms[:0]
+ if fi.Valid() {
+ fdSyms = ldr.Funcdata(s, fdSyms)
+ if fi.NumInlTree() > 0 {
+ if len(fdSyms) < objabi.FUNCDATA_InlTree+1 {
+ fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...)
+ }
+ fdSyms[objabi.FUNCDATA_InlTree] = inlSym
+ }
+ }
+ return fdSyms
+}
+
+// calculateFunctabSize calculates the size of the pclntab, and the offsets in
+// the output buffer for individual func entries.
+func (state pclntab) calculateFunctabSize(ctxt *Link, funcs []loader.Sym) (int64, []uint32) {
+ ldr := ctxt.loader
+ startLocations := make([]uint32, len(funcs))
+
+ // Allocate space for the pc->func table. This structure consists of a pc offset
+ // and an offset to the func structure. After that, we have a single pc
+ // value that marks the end of the last function in the binary.
+ size := int64(int(state.nfunc)*2*4 + 4)
+
+ // Now find the space for the func objects. We do this in a running manner,
+ // so that we can find individual starting locations.
+ for i, s := range funcs {
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
+ startLocations[i] = uint32(size)
+ fi := ldr.FuncInfo(s)
+ size += funcSize
+ if fi.Valid() {
+ fi.Preload()
+ numFuncData := ldr.NumFuncdata(s)
+ if fi.NumInlTree() > 0 {
+ if numFuncData < objabi.FUNCDATA_InlTree+1 {
+ numFuncData = objabi.FUNCDATA_InlTree + 1
+ }
+ }
+ size += int64(numPCData(ldr, s, fi) * 4)
+ size += int64(numFuncData * 4)
+ }
+ }
+
+ return size, startLocations
+}
+
+// writePCToFunc writes the PC->func lookup table.
+func writePCToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32) {
+ ldr := ctxt.loader
+ textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0))
+ pcOff := func(s loader.Sym) uint32 {
+ off := ldr.SymValue(s) - textStart
+ if off < 0 {
+ panic(fmt.Sprintf("expected func %s(%x) to be placed at or after textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
+ }
+ return uint32(off)
+ }
+ for i, s := range funcs {
+ sb.SetUint32(ctxt.Arch, int64(i*2*4), pcOff(s))
+ sb.SetUint32(ctxt.Arch, int64((i*2+1)*4), startLocations[i])
+ }
+
+ // Final entry of table is just end pc offset.
+ lastFunc := funcs[len(funcs)-1]
+ sb.SetUint32(ctxt.Arch, int64(len(funcs))*2*4, pcOff(lastFunc)+uint32(ldr.SymSize(lastFunc)))
+}
+
+// writeFuncs writes the func structures and pcdata to runtime.functab.
+func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
+ ldr := ctxt.loader
+ deferReturnSym := ldr.Lookup("runtime.deferreturn", abiInternalVer)
+ gofunc := ldr.Lookup("go:func.*", 0)
+ gofuncBase := ldr.SymValue(gofunc)
+ textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0))
+ funcdata := []loader.Sym{}
+ var pcsp, pcfile, pcline, pcinline loader.Sym
+ var pcdata []loader.Sym
+
+ // Write the individual func objects.
+ for i, s := range funcs {
+ startLine := int32(0)
+ fi := ldr.FuncInfo(s)
+ if fi.Valid() {
+ fi.Preload()
+ pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata)
+ startLine = fi.StartLine()
+ }
+
+ off := int64(startLocations[i])
+ // entryOff uint32 (offset of func entry PC from textStart)
+ entryOff := ldr.SymValue(s) - textStart
+ if entryOff < 0 {
+ panic(fmt.Sprintf("expected func %s(%x) to be placed before or at textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
+ }
+ off = sb.SetUint32(ctxt.Arch, off, uint32(entryOff))
+
+ // nameOff int32
+ nameOff, ok := nameOffsets[s]
+ if !ok {
+ panic("couldn't find function name offset")
+ }
+ off = sb.SetUint32(ctxt.Arch, off, uint32(nameOff))
+
+ // args int32
+ // TODO: Move into funcinfo.
+ args := uint32(0)
+ if fi.Valid() {
+ args = uint32(fi.Args())
+ }
+ off = sb.SetUint32(ctxt.Arch, off, args)
+
+ // deferreturn
+ deferreturn := computeDeferReturn(ctxt, deferReturnSym, s)
+ off = sb.SetUint32(ctxt.Arch, off, deferreturn)
+
+ // pcdata
+ if fi.Valid() {
+ off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcsp)))
+ off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcfile)))
+ off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcline)))
+ } else {
+ off += 12
+ }
+ off = sb.SetUint32(ctxt.Arch, off, uint32(numPCData(ldr, s, fi)))
+
+ // Store the offset to compilation unit's file table.
+ cuIdx := ^uint32(0)
+ if cu := ldr.SymUnit(s); cu != nil {
+ cuIdx = cuOffsets[cu.PclnIndex]
+ }
+ off = sb.SetUint32(ctxt.Arch, off, cuIdx)
+
+ // startLine int32
+ off = sb.SetUint32(ctxt.Arch, off, uint32(startLine))
+
+ // funcID uint8
+ var funcID objabi.FuncID
+ if fi.Valid() {
+ funcID = fi.FuncID()
+ }
+ off = sb.SetUint8(ctxt.Arch, off, uint8(funcID))
+
+ // flag uint8
+ var flag objabi.FuncFlag
+ if fi.Valid() {
+ flag = fi.FuncFlag()
+ }
+ off = sb.SetUint8(ctxt.Arch, off, uint8(flag))
+
+ off += 1 // pad
+
+ // nfuncdata must be the final entry.
+ funcdata = funcData(ldr, s, fi, 0, funcdata)
+ off = sb.SetUint8(ctxt.Arch, off, uint8(len(funcdata)))
+
+ // Output the pcdata.
+ if fi.Valid() {
+ for j, pcSym := range pcdata {
+ sb.SetUint32(ctxt.Arch, off+int64(j*4), uint32(ldr.SymValue(pcSym)))
+ }
+ if fi.NumInlTree() > 0 {
+ sb.SetUint32(ctxt.Arch, off+objabi.PCDATA_InlTreeIndex*4, uint32(ldr.SymValue(pcinline)))
+ }
+ }
+
+ // Write funcdata refs as offsets from go:func.* and go:funcrel.*.
+ funcdata = funcData(ldr, s, fi, inlSyms[s], funcdata)
+ // Missing funcdata will be ^0. See runtime/symtab.go:funcdata.
+ off = int64(startLocations[i] + funcSize + numPCData(ldr, s, fi)*4)
+ for j := range funcdata {
+ dataoff := off + int64(4*j)
+ fdsym := funcdata[j]
+ if fdsym == 0 {
+ sb.SetUint32(ctxt.Arch, dataoff, ^uint32(0)) // ^0 is a sentinel for "no value"
+ continue
+ }
+
+ if outer := ldr.OuterSym(fdsym); outer != gofunc {
+ panic(fmt.Sprintf("bad carrier sym for symbol %s (funcdata %s#%d), want go:func.* got %s", ldr.SymName(fdsym), ldr.SymName(s), j, ldr.SymName(outer)))
+ }
+ sb.SetUint32(ctxt.Arch, dataoff, uint32(ldr.SymValue(fdsym)-gofuncBase))
+ }
+ }
+}
+
+// pclntab initializes the pclntab symbol with
+// runtime function and file name information.
+
+// pclntab generates the pcln table for the link output.
+func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
+ // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the
+ // layout and data has changed since that time.
+ //
+ // As of August 2020, here's the layout of pclntab:
+ //
+ // .gopclntab/__gopclntab [elf/macho section]
+ // runtime.pclntab
+ // Carrier symbol for the entire pclntab section.
+ //
+ // runtime.pcheader (see: runtime/symtab.go:pcHeader)
+ // 8-byte magic
+ // nfunc [thearch.ptrsize bytes]
+ // offset to runtime.funcnametab from the beginning of runtime.pcheader
+ // offset to runtime.pclntab_old from beginning of runtime.pcheader
+ //
+ // runtime.funcnametab
+ // []list of null terminated function names
+ //
+ // runtime.cutab
+ // for i=0..#CUs
+ // for j=0..#max used file index in CU[i]
+ // uint32 offset into runtime.filetab for the filename[j]
+ //
+ // runtime.filetab
+ // []null terminated filename strings
+ //
+ // runtime.pctab
+ // []byte of deduplicated pc data.
+ //
+ // runtime.functab
+ // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
+ // end PC [thearch.ptrsize bytes]
+ // func structures, pcdata offsets, func data.
+
+ state, compUnits, funcs := makePclntab(ctxt, container)
+
+ ldr := ctxt.loader
+ state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
+ ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
+ ldr.SetAttrReachable(state.carrier, true)
+ setCarrierSym(sym.SPCLNTAB, state.carrier)
+
+ state.generatePCHeader(ctxt)
+ nameOffsets := state.generateFuncnametab(ctxt, funcs)
+ cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs)
+ state.generatePctab(ctxt, funcs)
+ inlSyms := makeInlSyms(ctxt, funcs, nameOffsets)
+ state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets)
+
+ return state
+}
+
+func gorootFinal() string {
+ root := buildcfg.GOROOT
+ if final := os.Getenv("GOROOT_FINAL"); final != "" {
+ root = final
+ }
+ return root
+}
+
+func expandGoroot(s string) string {
+ const n = len("$GOROOT")
+ if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
+ if final := gorootFinal(); final != "" {
+ return filepath.ToSlash(filepath.Join(final, s[n:]))
+ }
+ }
+ return s
+}
+
+const (
+ BUCKETSIZE = 256 * MINFUNC
+ SUBBUCKETS = 16
+ SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
+ NOIDX = 0x7fffffff
+)
+
+// findfunctab generates a lookup table to quickly find the containing
+// function for a pc. See src/runtime/symtab.go:findfunc for details.
+func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) {
+ ldr := ctxt.loader
+
+ // find min and max address
+ min := ldr.SymValue(ctxt.Textp[0])
+ lastp := ctxt.Textp[len(ctxt.Textp)-1]
+ max := ldr.SymValue(lastp) + ldr.SymSize(lastp)
+
+ // for each subbucket, compute the minimum of all symbol indexes
+ // that map to that subbucket.
+ n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
+
+ nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
+
+ size := 4*int64(nbuckets) + int64(n)
+
+ writeFindFuncTab := func(_ *Link, s loader.Sym) {
+ t := ldr.MakeSymbolUpdater(s)
+
+ indexes := make([]int32, n)
+ for i := int32(0); i < n; i++ {
+ indexes[i] = NOIDX
+ }
+ idx := int32(0)
+ for i, s := range ctxt.Textp {
+ if !emitPcln(ctxt, s, container) {
+ continue
+ }
+ p := ldr.SymValue(s)
+ var e loader.Sym
+ i++
+ if i < len(ctxt.Textp) {
+ e = ctxt.Textp[i]
+ }
+ for e != 0 && !emitPcln(ctxt, e, container) && i < len(ctxt.Textp) {
+ e = ctxt.Textp[i]
+ i++
+ }
+ q := max
+ if e != 0 {
+ q = ldr.SymValue(e)
+ }
+
+ //print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
+ for ; p < q; p += SUBBUCKETSIZE {
+ i = int((p - min) / SUBBUCKETSIZE)
+ if indexes[i] > idx {
+ indexes[i] = idx
+ }
+ }
+
+ i = int((q - 1 - min) / SUBBUCKETSIZE)
+ if indexes[i] > idx {
+ indexes[i] = idx
+ }
+ idx++
+ }
+
+ // fill in table
+ for i := int32(0); i < nbuckets; i++ {
+ base := indexes[i*SUBBUCKETS]
+ if base == NOIDX {
+ Errorf(nil, "hole in findfunctab")
+ }
+ t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base))
+ for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
+ idx = indexes[i*SUBBUCKETS+j]
+ if idx == NOIDX {
+ Errorf(nil, "hole in findfunctab")
+ }
+ if idx-base >= 256 {
+ Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
+ }
+
+ t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
+ }
+ }
+ }
+
+ state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab)
+ ldr.SetAttrReachable(state.findfunctab, true)
+ ldr.SetAttrLocal(state.findfunctab, true)
+}
+
+// findContainerSyms returns a bitmap, indexed by symbol number, where there's
+// a 1 for every container symbol.
+func (ctxt *Link) findContainerSyms() loader.Bitmap {
+ ldr := ctxt.loader
+ container := loader.MakeBitmap(ldr.NSym())
+ // Find container symbols and mark them as such.
+ for _, s := range ctxt.Textp {
+ outer := ldr.OuterSym(s)
+ if outer != 0 {
+ container.Set(outer)
+ }
+ }
+ return container
+}
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
new file mode 100644
index 0000000..0e29131
--- /dev/null
+++ b/src/cmd/link/internal/ld/pe.go
@@ -0,0 +1,1709 @@
+// Copyright 2009 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.
+
+// PE (Portable Executable) file writing
+// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/pe"
+ "encoding/binary"
+ "fmt"
+ "internal/buildcfg"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+type IMAGE_IMPORT_DESCRIPTOR struct {
+ OriginalFirstThunk uint32
+ TimeDateStamp uint32
+ ForwarderChain uint32
+ Name uint32
+ FirstThunk uint32
+}
+
+type IMAGE_EXPORT_DIRECTORY struct {
+ Characteristics uint32
+ TimeDateStamp uint32
+ MajorVersion uint16
+ MinorVersion uint16
+ Name uint32
+ Base uint32
+ NumberOfFunctions uint32
+ NumberOfNames uint32
+ AddressOfFunctions uint32
+ AddressOfNames uint32
+ AddressOfNameOrdinals uint32
+}
+
+var (
+ // PEBASE is the base address for the executable.
+ // It is small for 32-bit and large for 64-bit.
+ PEBASE int64
+
+ // SectionAlignment must be greater than or equal to FileAlignment.
+ // The default is the page size for the architecture.
+ PESECTALIGN int64 = 0x1000
+
+ // FileAlignment should be a power of 2 between 512 and 64 K, inclusive.
+ // The default is 512. If the SectionAlignment is less than
+ // the architecture's page size, then FileAlignment must match SectionAlignment.
+ PEFILEALIGN int64 = 2 << 8
+)
+
+const (
+ IMAGE_SCN_CNT_CODE = 0x00000020
+ IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
+ IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
+ IMAGE_SCN_LNK_OTHER = 0x00000100
+ IMAGE_SCN_LNK_INFO = 0x00000200
+ IMAGE_SCN_LNK_REMOVE = 0x00000800
+ IMAGE_SCN_LNK_COMDAT = 0x00001000
+ IMAGE_SCN_GPREL = 0x00008000
+ IMAGE_SCN_MEM_PURGEABLE = 0x00020000
+ IMAGE_SCN_MEM_16BIT = 0x00020000
+ IMAGE_SCN_MEM_LOCKED = 0x00040000
+ IMAGE_SCN_MEM_PRELOAD = 0x00080000
+ IMAGE_SCN_ALIGN_1BYTES = 0x00100000
+ IMAGE_SCN_ALIGN_2BYTES = 0x00200000
+ IMAGE_SCN_ALIGN_4BYTES = 0x00300000
+ IMAGE_SCN_ALIGN_8BYTES = 0x00400000
+ IMAGE_SCN_ALIGN_16BYTES = 0x00500000
+ IMAGE_SCN_ALIGN_32BYTES = 0x00600000
+ IMAGE_SCN_ALIGN_64BYTES = 0x00700000
+ IMAGE_SCN_ALIGN_128BYTES = 0x00800000
+ IMAGE_SCN_ALIGN_256BYTES = 0x00900000
+ IMAGE_SCN_ALIGN_512BYTES = 0x00A00000
+ IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000
+ IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000
+ IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000
+ IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000
+ IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000
+ IMAGE_SCN_MEM_DISCARDABLE = 0x02000000
+ IMAGE_SCN_MEM_NOT_CACHED = 0x04000000
+ IMAGE_SCN_MEM_NOT_PAGED = 0x08000000
+ IMAGE_SCN_MEM_SHARED = 0x10000000
+ IMAGE_SCN_MEM_EXECUTE = 0x20000000
+ IMAGE_SCN_MEM_READ = 0x40000000
+ IMAGE_SCN_MEM_WRITE = 0x80000000
+)
+
+// See https://docs.microsoft.com/en-us/windows/win32/debug/pe-format.
+// TODO(crawshaw): add these constants to debug/pe.
+const (
+ // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 and IMAGE_SYM_DTYPE_FUNCTION is 2
+ IMAGE_SYM_TYPE_NULL = 0
+ IMAGE_SYM_TYPE_STRUCT = 8
+ IMAGE_SYM_DTYPE_FUNCTION = 0x20
+ IMAGE_SYM_DTYPE_ARRAY = 0x30
+ IMAGE_SYM_CLASS_EXTERNAL = 2
+ IMAGE_SYM_CLASS_STATIC = 3
+
+ IMAGE_REL_I386_DIR32 = 0x0006
+ IMAGE_REL_I386_SECREL = 0x000B
+ IMAGE_REL_I386_REL32 = 0x0014
+
+ IMAGE_REL_AMD64_ADDR64 = 0x0001
+ IMAGE_REL_AMD64_ADDR32 = 0x0002
+ IMAGE_REL_AMD64_REL32 = 0x0004
+ IMAGE_REL_AMD64_SECREL = 0x000B
+
+ IMAGE_REL_ARM_ABSOLUTE = 0x0000
+ IMAGE_REL_ARM_ADDR32 = 0x0001
+ IMAGE_REL_ARM_ADDR32NB = 0x0002
+ IMAGE_REL_ARM_BRANCH24 = 0x0003
+ IMAGE_REL_ARM_BRANCH11 = 0x0004
+ IMAGE_REL_ARM_SECREL = 0x000F
+
+ IMAGE_REL_ARM64_ABSOLUTE = 0x0000
+ IMAGE_REL_ARM64_ADDR32 = 0x0001
+ IMAGE_REL_ARM64_ADDR32NB = 0x0002
+ IMAGE_REL_ARM64_BRANCH26 = 0x0003
+ IMAGE_REL_ARM64_PAGEBASE_REL21 = 0x0004
+ IMAGE_REL_ARM64_REL21 = 0x0005
+ IMAGE_REL_ARM64_PAGEOFFSET_12A = 0x0006
+ IMAGE_REL_ARM64_PAGEOFFSET_12L = 0x0007
+ IMAGE_REL_ARM64_SECREL = 0x0008
+ IMAGE_REL_ARM64_SECREL_LOW12A = 0x0009
+ IMAGE_REL_ARM64_SECREL_HIGH12A = 0x000A
+ IMAGE_REL_ARM64_SECREL_LOW12L = 0x000B
+ IMAGE_REL_ARM64_TOKEN = 0x000C
+ IMAGE_REL_ARM64_SECTION = 0x000D
+ IMAGE_REL_ARM64_ADDR64 = 0x000E
+ IMAGE_REL_ARM64_BRANCH19 = 0x000F
+ IMAGE_REL_ARM64_BRANCH14 = 0x0010
+ IMAGE_REL_ARM64_REL32 = 0x0011
+
+ IMAGE_REL_BASED_HIGHLOW = 3
+ IMAGE_REL_BASED_DIR64 = 10
+)
+
+const (
+ PeMinimumTargetMajorVersion = 6
+ PeMinimumTargetMinorVersion = 1
+)
+
+// DOS stub that prints out
+// "This program cannot be run in DOS mode."
+var dosstub = []uint8{
+ 0x4d,
+ 0x5a,
+ 0x90,
+ 0x00,
+ 0x03,
+ 0x00,
+ 0x04,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0xff,
+ 0xff,
+ 0x00,
+ 0x00,
+ 0x8b,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x40,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x80,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x0e,
+ 0x1f,
+ 0xba,
+ 0x0e,
+ 0x00,
+ 0xb4,
+ 0x09,
+ 0xcd,
+ 0x21,
+ 0xb8,
+ 0x01,
+ 0x4c,
+ 0xcd,
+ 0x21,
+ 0x54,
+ 0x68,
+ 0x69,
+ 0x73,
+ 0x20,
+ 0x70,
+ 0x72,
+ 0x6f,
+ 0x67,
+ 0x72,
+ 0x61,
+ 0x6d,
+ 0x20,
+ 0x63,
+ 0x61,
+ 0x6e,
+ 0x6e,
+ 0x6f,
+ 0x74,
+ 0x20,
+ 0x62,
+ 0x65,
+ 0x20,
+ 0x72,
+ 0x75,
+ 0x6e,
+ 0x20,
+ 0x69,
+ 0x6e,
+ 0x20,
+ 0x44,
+ 0x4f,
+ 0x53,
+ 0x20,
+ 0x6d,
+ 0x6f,
+ 0x64,
+ 0x65,
+ 0x2e,
+ 0x0d,
+ 0x0d,
+ 0x0a,
+ 0x24,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+}
+
+type Imp struct {
+ s loader.Sym
+ off uint64
+ next *Imp
+ argsize int
+}
+
+type Dll struct {
+ name string
+ nameoff uint64
+ thunkoff uint64
+ ms *Imp
+ next *Dll
+}
+
+var (
+ rsrcsyms []loader.Sym
+ PESECTHEADR int32
+ PEFILEHEADR int32
+ pe64 int
+ dr *Dll
+
+ dexport = make([]loader.Sym, 0, 1024)
+)
+
+// peStringTable is a COFF string table.
+type peStringTable struct {
+ strings []string
+ stringsLen int
+}
+
+// size returns size of string table t.
+func (t *peStringTable) size() int {
+ // string table starts with 4-byte length at the beginning
+ return t.stringsLen + 4
+}
+
+// add adds string str to string table t.
+func (t *peStringTable) add(str string) int {
+ off := t.size()
+ t.strings = append(t.strings, str)
+ t.stringsLen += len(str) + 1 // each string will have 0 appended to it
+ return off
+}
+
+// write writes string table t into the output file.
+func (t *peStringTable) write(out *OutBuf) {
+ out.Write32(uint32(t.size()))
+ for _, s := range t.strings {
+ out.WriteString(s)
+ out.Write8(0)
+ }
+}
+
+// peSection represents section from COFF section table.
+type peSection struct {
+ name string
+ shortName string
+ index int // one-based index into the Section Table
+ virtualSize uint32
+ virtualAddress uint32
+ sizeOfRawData uint32
+ pointerToRawData uint32
+ pointerToRelocations uint32
+ numberOfRelocations uint16
+ characteristics uint32
+}
+
+// checkOffset verifies COFF section sect offset in the file.
+func (sect *peSection) checkOffset(off int64) {
+ if off != int64(sect.pointerToRawData) {
+ Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(off))
+ errorexit()
+ }
+}
+
+// checkSegment verifies COFF section sect matches address
+// and file offset provided in segment seg.
+func (sect *peSection) checkSegment(seg *sym.Segment) {
+ if seg.Vaddr-uint64(PEBASE) != uint64(sect.virtualAddress) {
+ Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-uint64(PEBASE))))
+ errorexit()
+ }
+ if seg.Fileoff != uint64(sect.pointerToRawData) {
+ Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(seg.Fileoff)))
+ errorexit()
+ }
+}
+
+// pad adds zeros to the section sect. It writes as many bytes
+// as necessary to make section sect.SizeOfRawData bytes long.
+// It assumes that n bytes are already written to the file.
+func (sect *peSection) pad(out *OutBuf, n uint32) {
+ out.WriteStringN("", int(sect.sizeOfRawData-n))
+}
+
+// write writes COFF section sect into the output file.
+func (sect *peSection) write(out *OutBuf, linkmode LinkMode) error {
+ h := pe.SectionHeader32{
+ VirtualSize: sect.virtualSize,
+ SizeOfRawData: sect.sizeOfRawData,
+ PointerToRawData: sect.pointerToRawData,
+ PointerToRelocations: sect.pointerToRelocations,
+ NumberOfRelocations: sect.numberOfRelocations,
+ Characteristics: sect.characteristics,
+ }
+ if linkmode != LinkExternal {
+ h.VirtualAddress = sect.virtualAddress
+ }
+ copy(h.Name[:], sect.shortName)
+ return binary.Write(out, binary.LittleEndian, h)
+}
+
+// emitRelocations emits the relocation entries for the sect.
+// The actual relocations are emitted by relocfn.
+// This updates the corresponding PE section table entry
+// with the relocation offset and count.
+func (sect *peSection) emitRelocations(out *OutBuf, relocfn func() int) {
+ sect.pointerToRelocations = uint32(out.Offset())
+ // first entry: extended relocs
+ out.Write32(0) // placeholder for number of relocation + 1
+ out.Write32(0)
+ out.Write16(0)
+
+ n := relocfn() + 1
+
+ cpos := out.Offset()
+ out.SeekSet(int64(sect.pointerToRelocations))
+ out.Write32(uint32(n))
+ out.SeekSet(cpos)
+ if n > 0x10000 {
+ n = 0x10000
+ sect.characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL
+ } else {
+ sect.pointerToRelocations += 10 // skip the extend reloc entry
+ }
+ sect.numberOfRelocations = uint16(n - 1)
+}
+
+// peFile is used to build COFF file.
+type peFile struct {
+ sections []*peSection
+ stringTable peStringTable
+ textSect *peSection
+ rdataSect *peSection
+ dataSect *peSection
+ bssSect *peSection
+ ctorsSect *peSection
+ nextSectOffset uint32
+ nextFileOffset uint32
+ symtabOffset int64 // offset to the start of symbol table
+ symbolCount int // number of symbol table records written
+ dataDirectory [16]pe.DataDirectory
+}
+
+// addSection adds section to the COFF file f.
+func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection {
+ sect := &peSection{
+ name: name,
+ shortName: name,
+ index: len(f.sections) + 1,
+ virtualAddress: f.nextSectOffset,
+ pointerToRawData: f.nextFileOffset,
+ }
+ f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN))
+ if filesize > 0 {
+ sect.virtualSize = uint32(sectsize)
+ sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN))
+ f.nextFileOffset += sect.sizeOfRawData
+ } else {
+ sect.sizeOfRawData = uint32(sectsize)
+ }
+ f.sections = append(f.sections, sect)
+ return sect
+}
+
+// addDWARFSection adds DWARF section to the COFF file f.
+// This function is similar to addSection, but DWARF section names are
+// longer than 8 characters, so they need to be stored in the string table.
+func (f *peFile) addDWARFSection(name string, size int) *peSection {
+ if size == 0 {
+ Exitf("DWARF section %q is empty", name)
+ }
+ // DWARF section names are longer than 8 characters.
+ // PE format requires such names to be stored in string table,
+ // and section names replaced with slash (/) followed by
+ // correspondent string table index.
+ // see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx
+ // for details
+ off := f.stringTable.add(name)
+ h := f.addSection(name, size, size)
+ h.shortName = fmt.Sprintf("/%d", off)
+ h.characteristics = IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA
+ return h
+}
+
+// addDWARF adds DWARF information to the COFF file f.
+func (f *peFile) addDWARF() {
+ if *FlagS { // disable symbol table
+ return
+ }
+ if *FlagW { // disable dwarf
+ return
+ }
+ for _, sect := range Segdwarf.Sections {
+ h := f.addDWARFSection(sect.Name, int(sect.Length))
+ fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff
+ if uint64(h.pointerToRawData) != fileoff {
+ Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff)
+ }
+ }
+}
+
+// addInitArray adds .ctors COFF section to the file f.
+func (f *peFile) addInitArray(ctxt *Link) *peSection {
+ // The size below was determined by the specification for array relocations,
+ // and by observing what GCC writes here. If the initarray section grows to
+ // contain more than one constructor entry, the size will need to be 8 * constructor_count.
+ // However, the entire Go runtime is initialized from just one function, so it is unlikely
+ // that this will need to grow in the future.
+ var size int
+ var alignment uint32
+ switch buildcfg.GOARCH {
+ default:
+ Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", buildcfg.GOARCH)
+ case "386", "arm":
+ size = 4
+ alignment = IMAGE_SCN_ALIGN_4BYTES
+ case "amd64", "arm64":
+ size = 8
+ alignment = IMAGE_SCN_ALIGN_8BYTES
+ }
+ sect := f.addSection(".ctors", size, size)
+ sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | alignment
+ sect.sizeOfRawData = uint32(size)
+ ctxt.Out.SeekSet(int64(sect.pointerToRawData))
+ sect.checkOffset(ctxt.Out.Offset())
+
+ init_entry := ctxt.loader.Lookup(*flagEntrySymbol, 0)
+ addr := uint64(ctxt.loader.SymValue(init_entry)) - ctxt.loader.SymSect(init_entry).Vaddr
+ switch buildcfg.GOARCH {
+ case "386", "arm":
+ ctxt.Out.Write32(uint32(addr))
+ case "amd64", "arm64":
+ ctxt.Out.Write64(addr)
+ }
+ return sect
+}
+
+// emitRelocations emits relocation entries for go.o in external linking.
+func (f *peFile) emitRelocations(ctxt *Link) {
+ for ctxt.Out.Offset()&7 != 0 {
+ ctxt.Out.Write8(0)
+ }
+
+ ldr := ctxt.loader
+
+ // relocsect relocates symbols from first in section sect, and returns
+ // the total number of relocations emitted.
+ relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) int {
+ // If main section has no bits, nothing to relocate.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return 0
+ }
+ sect.Reloff = uint64(ctxt.Out.Offset())
+ for i, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if uint64(ldr.SymValue(s)) >= sect.Vaddr {
+ syms = syms[i:]
+ break
+ }
+ }
+ eaddr := int64(sect.Vaddr + sect.Length)
+ for _, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymValue(s) >= eaddr {
+ break
+ }
+ // Compute external relocations on the go, and pass to PEreloc1
+ // to stream out.
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rr, ok := extreloc(ctxt, ldr, s, r)
+ if !ok {
+ continue
+ }
+ if rr.Xsym == 0 {
+ ctxt.Errorf(s, "missing xsym in relocation")
+ continue
+ }
+ if ldr.SymDynid(rr.Xsym) < 0 {
+ ctxt.Errorf(s, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()))
+ }
+ if !thearch.PEreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) {
+ ctxt.Errorf(s, "unsupported obj reloc %v/%d to %s", r.Type(), r.Siz(), ldr.SymName(r.Sym()))
+ }
+ }
+ }
+ sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
+ const relocLen = 4 + 4 + 2
+ return int(sect.Rellen / relocLen)
+ }
+
+ sects := []struct {
+ peSect *peSection
+ seg *sym.Segment
+ syms []loader.Sym
+ }{
+ {f.textSect, &Segtext, ctxt.Textp},
+ {f.rdataSect, &Segrodata, ctxt.datap},
+ {f.dataSect, &Segdata, ctxt.datap},
+ }
+ for _, s := range sects {
+ s.peSect.emitRelocations(ctxt.Out, func() int {
+ var n int
+ for _, sect := range s.seg.Sections {
+ n += relocsect(sect, s.syms, s.seg.Vaddr)
+ }
+ return n
+ })
+ }
+
+dwarfLoop:
+ for i := 0; i < len(Segdwarf.Sections); i++ {
+ sect := Segdwarf.Sections[i]
+ si := dwarfp[i]
+ if si.secSym() != loader.Sym(sect.Sym) ||
+ ldr.SymSect(si.secSym()) != sect {
+ panic("inconsistency between dwarfp and Segdwarf")
+ }
+ for _, pesect := range f.sections {
+ if sect.Name == pesect.name {
+ pesect.emitRelocations(ctxt.Out, func() int {
+ return relocsect(sect, si.syms, sect.Vaddr)
+ })
+ continue dwarfLoop
+ }
+ }
+ Errorf(nil, "emitRelocations: could not find %q section", sect.Name)
+ }
+
+ if f.ctorsSect == nil {
+ return
+ }
+
+ f.ctorsSect.emitRelocations(ctxt.Out, func() int {
+ dottext := ldr.Lookup(".text", 0)
+ ctxt.Out.Write32(0)
+ ctxt.Out.Write32(uint32(ldr.SymDynid(dottext)))
+ switch buildcfg.GOARCH {
+ default:
+ ctxt.Errorf(dottext, "unknown architecture for PE: %q\n", buildcfg.GOARCH)
+ case "386":
+ ctxt.Out.Write16(IMAGE_REL_I386_DIR32)
+ case "amd64":
+ ctxt.Out.Write16(IMAGE_REL_AMD64_ADDR64)
+ case "arm":
+ ctxt.Out.Write16(IMAGE_REL_ARM_ADDR32)
+ case "arm64":
+ ctxt.Out.Write16(IMAGE_REL_ARM64_ADDR64)
+ }
+ return 1
+ })
+}
+
+// writeSymbol appends symbol s to file f symbol table.
+// It also sets s.Dynid to written symbol number.
+func (f *peFile) writeSymbol(out *OutBuf, ldr *loader.Loader, s loader.Sym, name string, value int64, sectidx int, typ uint16, class uint8) {
+ if len(name) > 8 {
+ out.Write32(0)
+ out.Write32(uint32(f.stringTable.add(name)))
+ } else {
+ out.WriteStringN(name, 8)
+ }
+ out.Write32(uint32(value))
+ out.Write16(uint16(sectidx))
+ out.Write16(typ)
+ out.Write8(class)
+ out.Write8(0) // no aux entries
+
+ ldr.SetSymDynid(s, int32(f.symbolCount))
+
+ f.symbolCount++
+}
+
+// mapToPESection searches peFile f for s symbol's location.
+// It returns PE section index, and offset within that section.
+func (f *peFile) mapToPESection(ldr *loader.Loader, s loader.Sym, linkmode LinkMode) (pesectidx int, offset int64, err error) {
+ sect := ldr.SymSect(s)
+ if sect == nil {
+ return 0, 0, fmt.Errorf("could not map %s symbol with no section", ldr.SymName(s))
+ }
+ if sect.Seg == &Segtext {
+ return f.textSect.index, int64(uint64(ldr.SymValue(s)) - Segtext.Vaddr), nil
+ }
+ if sect.Seg == &Segrodata {
+ return f.rdataSect.index, int64(uint64(ldr.SymValue(s)) - Segrodata.Vaddr), nil
+ }
+ if sect.Seg != &Segdata {
+ return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", ldr.SymName(s))
+ }
+ v := uint64(ldr.SymValue(s)) - Segdata.Vaddr
+ if linkmode != LinkExternal {
+ return f.dataSect.index, int64(v), nil
+ }
+ if ldr.SymType(s) == sym.SDATA {
+ return f.dataSect.index, int64(v), nil
+ }
+ // Note: although address of runtime.edata (type sym.SDATA) is at the start of .bss section
+ // it still belongs to the .data section, not the .bss section.
+ if v < Segdata.Filelen {
+ return f.dataSect.index, int64(v), nil
+ }
+ return f.bssSect.index, int64(v - Segdata.Filelen), nil
+}
+
+var isLabel = make(map[loader.Sym]bool)
+
+func AddPELabelSym(ldr *loader.Loader, s loader.Sym) {
+ isLabel[s] = true
+}
+
+// writeSymbols writes all COFF symbol table records.
+func (f *peFile) writeSymbols(ctxt *Link) {
+ ldr := ctxt.loader
+ addsym := func(s loader.Sym) {
+ t := ldr.SymType(s)
+ if ldr.SymSect(s) == nil && t != sym.SDYNIMPORT && t != sym.SHOSTOBJ && t != sym.SUNDEFEXT {
+ return
+ }
+
+ name := ldr.SymName(s)
+
+ // Only windows/386 requires underscore prefix on external symbols.
+ if ctxt.Is386() && ctxt.IsExternal() &&
+ (t == sym.SHOSTOBJ || t == sym.SUNDEFEXT || ldr.AttrCgoExport(s) ||
+ // TODO(cuonglm): remove this hack
+ //
+ // Previously, windows/386 requires underscore prefix on external symbols,
+ // but that's only applied for SHOSTOBJ/SUNDEFEXT or cgo export symbols.
+ // "go.buildid" is STEXT, "type.*" is STYPE, thus they are not prefixed
+ // with underscore.
+ //
+ // In external linking mode, the external linker can't resolve them as
+ // external symbols. But we are lucky that they have "." in their name,
+ // so the external linker see them as Forwarder RVA exports. See:
+ //
+ // - https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table
+ // - https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=ld/pe-dll.c;h=e7b82ba6ffadf74dc1b9ee71dc13d48336941e51;hb=HEAD#l972)
+ //
+ // CL 317917 changes "." to ":" in symbols name, so theses symbols can not be
+ // found by external linker anymore. So a hacky way is adding the
+ // underscore prefix for these 2 symbols. I don't have enough knowledge to
+ // verify whether adding the underscore for all STEXT/STYPE symbols are
+ // fine, even if it could be, that would be done in future CL.
+ name == "go:buildid" || name == "type:*") {
+ name = "_" + name
+ }
+
+ name = mangleABIName(ctxt, ldr, s, name)
+
+ var peSymType uint16
+ if ctxt.IsExternal() {
+ peSymType = IMAGE_SYM_TYPE_NULL
+ } else {
+ // TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308
+ // peSymType = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT
+ peSymType = 0x0308 // "array of structs"
+ }
+ sect, value, err := f.mapToPESection(ldr, s, ctxt.LinkMode)
+ if err != nil {
+ if t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT {
+ peSymType = IMAGE_SYM_DTYPE_FUNCTION
+ } else {
+ ctxt.Errorf(s, "addpesym: %v", err)
+ }
+ }
+ class := IMAGE_SYM_CLASS_EXTERNAL
+ if ldr.IsFileLocal(s) || ldr.AttrVisibilityHidden(s) || ldr.AttrLocal(s) {
+ class = IMAGE_SYM_CLASS_STATIC
+ }
+ f.writeSymbol(ctxt.Out, ldr, s, name, value, sect, peSymType, uint8(class))
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ // Include section symbols as external, because
+ // .ctors and .debug_* section relocations refer to it.
+ for _, pesect := range f.sections {
+ s := ldr.LookupOrCreateSym(pesect.name, 0)
+ f.writeSymbol(ctxt.Out, ldr, s, pesect.name, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC)
+ }
+ }
+
+ // Add special runtime.text and runtime.etext symbols.
+ s := ldr.Lookup("runtime.text", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ addsym(s)
+ }
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ addsym(s)
+ }
+
+ // Add text symbols.
+ for _, s := range ctxt.Textp {
+ addsym(s)
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym) bool {
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ name := ldr.SymName(s) // TODO: try not to read the name
+ if name == "" || name[0] == '.' {
+ return false
+ }
+ return true
+ }
+
+ // Add data symbols and external references.
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ t := ldr.SymType(s)
+ if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata
+ if t == sym.STLSBSS {
+ continue
+ }
+ if !shouldBeInSymbolTable(s) {
+ continue
+ }
+ addsym(s)
+ }
+
+ switch t {
+ case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT:
+ addsym(s)
+ default:
+ if len(isLabel) > 0 && isLabel[s] {
+ addsym(s)
+ }
+ }
+ }
+}
+
+// writeSymbolTableAndStringTable writes out symbol and string tables for peFile f.
+func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) {
+ f.symtabOffset = ctxt.Out.Offset()
+
+ // write COFF symbol table
+ if !*FlagS || ctxt.LinkMode == LinkExternal {
+ f.writeSymbols(ctxt)
+ }
+
+ // update COFF file header and section table
+ size := f.stringTable.size() + 18*f.symbolCount
+ var h *peSection
+ if ctxt.LinkMode != LinkExternal {
+ // We do not really need .symtab for go.o, and if we have one, ld
+ // will also include it in the exe, and that will confuse windows.
+ h = f.addSection(".symtab", size, size)
+ h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
+ h.checkOffset(f.symtabOffset)
+ }
+
+ // write COFF string table
+ f.stringTable.write(ctxt.Out)
+ if ctxt.LinkMode != LinkExternal {
+ h.pad(ctxt.Out, uint32(size))
+ }
+}
+
+// writeFileHeader writes COFF file header for peFile f.
+func (f *peFile) writeFileHeader(ctxt *Link) {
+ var fh pe.FileHeader
+
+ switch ctxt.Arch.Family {
+ default:
+ Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
+ case sys.AMD64:
+ fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64
+ case sys.I386:
+ fh.Machine = pe.IMAGE_FILE_MACHINE_I386
+ case sys.ARM:
+ fh.Machine = pe.IMAGE_FILE_MACHINE_ARMNT
+ case sys.ARM64:
+ fh.Machine = pe.IMAGE_FILE_MACHINE_ARM64
+ }
+
+ fh.NumberOfSections = uint16(len(f.sections))
+
+ // Being able to produce identical output for identical input is
+ // much more beneficial than having build timestamp in the header.
+ fh.TimeDateStamp = 0
+
+ if ctxt.LinkMode == LinkExternal {
+ fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED
+ } else {
+ fh.Characteristics = pe.IMAGE_FILE_EXECUTABLE_IMAGE | pe.IMAGE_FILE_DEBUG_STRIPPED
+ switch ctxt.Arch.Family {
+ case sys.AMD64, sys.I386:
+ if ctxt.BuildMode != BuildModePIE {
+ fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED
+ }
+ }
+ }
+ if pe64 != 0 {
+ var oh64 pe.OptionalHeader64
+ fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64))
+ fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE
+ } else {
+ var oh pe.OptionalHeader32
+ fh.SizeOfOptionalHeader = uint16(binary.Size(&oh))
+ fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE
+ }
+
+ fh.PointerToSymbolTable = uint32(f.symtabOffset)
+ fh.NumberOfSymbols = uint32(f.symbolCount)
+
+ binary.Write(ctxt.Out, binary.LittleEndian, &fh)
+}
+
+// writeOptionalHeader writes COFF optional header for peFile f.
+func (f *peFile) writeOptionalHeader(ctxt *Link) {
+ var oh pe.OptionalHeader32
+ var oh64 pe.OptionalHeader64
+
+ if pe64 != 0 {
+ oh64.Magic = 0x20b // PE32+
+ } else {
+ oh.Magic = 0x10b // PE32
+ oh.BaseOfData = f.dataSect.virtualAddress
+ }
+
+ // Fill out both oh64 and oh. We only use one. Oh well.
+ oh64.MajorLinkerVersion = 3
+ oh.MajorLinkerVersion = 3
+ oh64.MinorLinkerVersion = 0
+ oh.MinorLinkerVersion = 0
+ oh64.SizeOfCode = f.textSect.sizeOfRawData
+ oh.SizeOfCode = f.textSect.sizeOfRawData
+ oh64.SizeOfInitializedData = f.dataSect.sizeOfRawData
+ oh.SizeOfInitializedData = f.dataSect.sizeOfRawData
+ oh64.SizeOfUninitializedData = 0
+ oh.SizeOfUninitializedData = 0
+ if ctxt.LinkMode != LinkExternal {
+ oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
+ oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
+ }
+ oh64.BaseOfCode = f.textSect.virtualAddress
+ oh.BaseOfCode = f.textSect.virtualAddress
+ oh64.ImageBase = uint64(PEBASE)
+ oh.ImageBase = uint32(PEBASE)
+ oh64.SectionAlignment = uint32(PESECTALIGN)
+ oh.SectionAlignment = uint32(PESECTALIGN)
+ oh64.FileAlignment = uint32(PEFILEALIGN)
+ oh.FileAlignment = uint32(PEFILEALIGN)
+ oh64.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion
+ oh.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion
+ oh64.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion
+ oh.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion
+ oh64.MajorImageVersion = 1
+ oh.MajorImageVersion = 1
+ oh64.MinorImageVersion = 0
+ oh.MinorImageVersion = 0
+ oh64.MajorSubsystemVersion = PeMinimumTargetMajorVersion
+ oh.MajorSubsystemVersion = PeMinimumTargetMajorVersion
+ oh64.MinorSubsystemVersion = PeMinimumTargetMinorVersion
+ oh.MinorSubsystemVersion = PeMinimumTargetMinorVersion
+ oh64.SizeOfImage = f.nextSectOffset
+ oh.SizeOfImage = f.nextSectOffset
+ oh64.SizeOfHeaders = uint32(PEFILEHEADR)
+ oh.SizeOfHeaders = uint32(PEFILEHEADR)
+ if windowsgui {
+ oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
+ oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
+ } else {
+ oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
+ oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
+ }
+
+ // Mark as having awareness of terminal services, to avoid ancient compatibility hacks.
+ oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
+ oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
+
+ // Enable DEP
+ oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
+ oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
+
+ // The DLL can be relocated at load time.
+ if needPEBaseReloc(ctxt) {
+ oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+ oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+ }
+
+ // Image can handle a high entropy 64-bit virtual address space.
+ if ctxt.BuildMode == BuildModePIE {
+ oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA
+ }
+
+ // Disable stack growth as we don't want Windows to
+ // fiddle with the thread stack limits, which we set
+ // ourselves to circumvent the stack checks in the
+ // Windows exception dispatcher.
+ // Commit size must be strictly less than reserve
+ // size otherwise reserve will be rounded up to a
+ // larger size, as verified with VMMap.
+
+ // On 64-bit, we always reserve 2MB stacks. "Pure" Go code is
+ // okay with much smaller stacks, but the syscall package
+ // makes it easy to call into arbitrary C code without cgo,
+ // and system calls even in "pure" Go code are actually C
+ // calls that may need more stack than we think.
+ //
+ // The default stack reserve size directly affects only the main
+ // thread.
+ //
+ // For other threads, the runtime explicitly asks the kernel
+ // to use the default stack size so that all stacks are
+ // consistent.
+ //
+ // At thread start, in minit, the runtime queries the OS for
+ // the actual stack bounds so that the stack size doesn't need
+ // to be hard-coded into the runtime.
+ oh64.SizeOfStackReserve = 0x00200000
+ if !iscgo {
+ oh64.SizeOfStackCommit = 0x00001000
+ } else {
+ // TODO(brainman): Maybe remove optional header writing altogether for cgo.
+ // For cgo it is the external linker that is building final executable.
+ // And it probably does not use any information stored in optional header.
+ oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages
+ }
+
+ oh.SizeOfStackReserve = 0x00100000
+ if !iscgo {
+ oh.SizeOfStackCommit = 0x00001000
+ } else {
+ oh.SizeOfStackCommit = 0x00100000 - 0x2000 // account for 2 guard pages
+ }
+
+ oh64.SizeOfHeapReserve = 0x00100000
+ oh.SizeOfHeapReserve = 0x00100000
+ oh64.SizeOfHeapCommit = 0x00001000
+ oh.SizeOfHeapCommit = 0x00001000
+ oh64.NumberOfRvaAndSizes = 16
+ oh.NumberOfRvaAndSizes = 16
+
+ if pe64 != 0 {
+ oh64.DataDirectory = f.dataDirectory
+ } else {
+ oh.DataDirectory = f.dataDirectory
+ }
+
+ if pe64 != 0 {
+ binary.Write(ctxt.Out, binary.LittleEndian, &oh64)
+ } else {
+ binary.Write(ctxt.Out, binary.LittleEndian, &oh)
+ }
+}
+
+var pefile peFile
+
+func Peinit(ctxt *Link) {
+ var l int
+
+ if ctxt.Arch.PtrSize == 8 {
+ // 64-bit architectures
+ pe64 = 1
+ PEBASE = 1 << 32
+ if ctxt.Arch.Family == sys.AMD64 {
+ // TODO(rsc): For cgo we currently use 32-bit relocations
+ // that fail when PEBASE is too large.
+ // We need to fix this, but for now, use a smaller PEBASE.
+ PEBASE = 1 << 22
+ }
+ var oh64 pe.OptionalHeader64
+ l = binary.Size(&oh64)
+ } else {
+ // 32-bit architectures
+ PEBASE = 1 << 22
+ var oh pe.OptionalHeader32
+ l = binary.Size(&oh)
+ }
+
+ if ctxt.LinkMode == LinkExternal {
+ // .rdata section will contain "masks" and "shifts" symbols, and they
+ // need to be aligned to 16-bytes. So make all sections aligned
+ // to 32-byte and mark them all IMAGE_SCN_ALIGN_32BYTES so external
+ // linker will honour that requirement.
+ PESECTALIGN = 32
+ PEFILEALIGN = 0
+ // We are creating an object file. The absolute address is irrelevant.
+ PEBASE = 0
+ }
+
+ var sh [16]pe.SectionHeader32
+ var fh pe.FileHeader
+ PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN))
+ if ctxt.LinkMode != LinkExternal {
+ PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN))
+ } else {
+ PESECTHEADR = 0
+ }
+ pefile.nextSectOffset = uint32(PESECTHEADR)
+ pefile.nextFileOffset = uint32(PEFILEHEADR)
+
+ if ctxt.LinkMode == LinkInternal {
+ // some mingw libs depend on this symbol, for example, FindPESectionByName
+ for _, name := range [2]string{"__image_base__", "_image_base__"} {
+ sb := ctxt.loader.CreateSymForUpdate(name, 0)
+ sb.SetType(sym.SDATA)
+ sb.SetValue(PEBASE)
+ ctxt.loader.SetAttrSpecial(sb.Sym(), true)
+ ctxt.loader.SetAttrLocal(sb.Sym(), true)
+ }
+ }
+
+ HEADR = PEFILEHEADR
+ if *FlagTextAddr == -1 {
+ *FlagTextAddr = PEBASE + int64(PESECTHEADR)
+ }
+ if *FlagRound == -1 {
+ *FlagRound = int(PESECTALIGN)
+ }
+}
+
+func pewrite(ctxt *Link) {
+ ctxt.Out.SeekSet(0)
+ if ctxt.LinkMode != LinkExternal {
+ ctxt.Out.Write(dosstub)
+ ctxt.Out.WriteStringN("PE", 4)
+ }
+
+ pefile.writeFileHeader(ctxt)
+
+ pefile.writeOptionalHeader(ctxt)
+
+ for _, sect := range pefile.sections {
+ sect.write(ctxt.Out, ctxt.LinkMode)
+ }
+}
+
+func strput(out *OutBuf, s string) {
+ out.WriteString(s)
+ out.Write8(0)
+ // string must be padded to even size
+ if (len(s)+1)%2 != 0 {
+ out.Write8(0)
+ }
+}
+
+func initdynimport(ctxt *Link) *Dll {
+ ldr := ctxt.loader
+ var d *Dll
+
+ dr = nil
+ var m *Imp
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) || ldr.SymType(s) != sym.SDYNIMPORT {
+ continue
+ }
+ dynlib := ldr.SymDynimplib(s)
+ for d = dr; d != nil; d = d.next {
+ if d.name == dynlib {
+ m = new(Imp)
+ break
+ }
+ }
+
+ if d == nil {
+ d = new(Dll)
+ d.name = dynlib
+ d.next = dr
+ dr = d
+ m = new(Imp)
+ }
+
+ // Because external link requires properly stdcall decorated name,
+ // all external symbols in runtime use %n to denote that the number
+ // of uinptrs this function consumes. Store the argsize and discard
+ // the %n suffix if any.
+ m.argsize = -1
+ extName := ldr.SymExtname(s)
+ if i := strings.IndexByte(extName, '%'); i >= 0 {
+ var err error
+ m.argsize, err = strconv.Atoi(extName[i+1:])
+ if err != nil {
+ ctxt.Errorf(s, "failed to parse stdcall decoration: %v", err)
+ }
+ m.argsize *= ctxt.Arch.PtrSize
+ ldr.SetSymExtname(s, extName[:i])
+ }
+
+ m.s = s
+ m.next = d.ms
+ d.ms = m
+ }
+
+ if ctxt.IsExternal() {
+ // Add real symbol name
+ for d := dr; d != nil; d = d.next {
+ for m = d.ms; m != nil; m = m.next {
+ sb := ldr.MakeSymbolUpdater(m.s)
+ sb.SetType(sym.SDATA)
+ sb.Grow(int64(ctxt.Arch.PtrSize))
+ dynName := sb.Extname()
+ // only windows/386 requires stdcall decoration
+ if ctxt.Is386() && m.argsize >= 0 {
+ dynName += fmt.Sprintf("@%d", m.argsize)
+ }
+ dynSym := ldr.CreateSymForUpdate(dynName, 0)
+ dynSym.SetType(sym.SHOSTOBJ)
+ r, _ := sb.AddRel(objabi.R_ADDR)
+ r.SetSym(dynSym.Sym())
+ r.SetSiz(uint8(ctxt.Arch.PtrSize))
+ }
+ }
+ } else {
+ dynamic := ldr.CreateSymForUpdate(".windynamic", 0)
+ dynamic.SetType(sym.SWINDOWS)
+ for d := dr; d != nil; d = d.next {
+ for m = d.ms; m != nil; m = m.next {
+ sb := ldr.MakeSymbolUpdater(m.s)
+ sb.SetType(sym.SWINDOWS)
+ sb.SetValue(dynamic.Size())
+ dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize))
+ dynamic.AddInteriorSym(m.s)
+ }
+
+ dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize))
+ }
+ }
+
+ return dr
+}
+
+// peimporteddlls returns the gcc command line argument to link all imported
+// DLLs.
+func peimporteddlls() []string {
+ var dlls []string
+
+ for d := dr; d != nil; d = d.next {
+ dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll"))
+ }
+
+ return dlls
+}
+
+func addimports(ctxt *Link, datsect *peSection) {
+ ldr := ctxt.loader
+ startoff := ctxt.Out.Offset()
+ dynamic := ldr.LookupOrCreateSym(".windynamic", 0)
+
+ // skip import descriptor table (will write it later)
+ n := uint64(0)
+
+ for d := dr; d != nil; d = d.next {
+ n++
+ }
+ ctxt.Out.SeekSet(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1))
+
+ // write dll names
+ for d := dr; d != nil; d = d.next {
+ d.nameoff = uint64(ctxt.Out.Offset()) - uint64(startoff)
+ strput(ctxt.Out, d.name)
+ }
+
+ // write function names
+ for d := dr; d != nil; d = d.next {
+ for m := d.ms; m != nil; m = m.next {
+ m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff)
+ ctxt.Out.Write16(0) // hint
+ strput(ctxt.Out, ldr.SymExtname(m.s))
+ }
+ }
+
+ // write OriginalFirstThunks
+ oftbase := uint64(ctxt.Out.Offset()) - uint64(startoff)
+
+ n = uint64(ctxt.Out.Offset())
+ for d := dr; d != nil; d = d.next {
+ d.thunkoff = uint64(ctxt.Out.Offset()) - n
+ for m := d.ms; m != nil; m = m.next {
+ if pe64 != 0 {
+ ctxt.Out.Write64(m.off)
+ } else {
+ ctxt.Out.Write32(uint32(m.off))
+ }
+ }
+
+ if pe64 != 0 {
+ ctxt.Out.Write64(0)
+ } else {
+ ctxt.Out.Write32(0)
+ }
+ }
+
+ // add pe section and pad it at the end
+ n = uint64(ctxt.Out.Offset()) - uint64(startoff)
+
+ isect := pefile.addSection(".idata", int(n), int(n))
+ isect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
+ isect.checkOffset(startoff)
+ isect.pad(ctxt.Out, uint32(n))
+ endoff := ctxt.Out.Offset()
+
+ // write FirstThunks (allocated in .data section)
+ ftbase := uint64(ldr.SymValue(dynamic)) - uint64(datsect.virtualAddress) - uint64(PEBASE)
+
+ ctxt.Out.SeekSet(int64(uint64(datsect.pointerToRawData) + ftbase))
+ for d := dr; d != nil; d = d.next {
+ for m := d.ms; m != nil; m = m.next {
+ if pe64 != 0 {
+ ctxt.Out.Write64(m.off)
+ } else {
+ ctxt.Out.Write32(uint32(m.off))
+ }
+ }
+
+ if pe64 != 0 {
+ ctxt.Out.Write64(0)
+ } else {
+ ctxt.Out.Write32(0)
+ }
+ }
+
+ // finally write import descriptor table
+ out := ctxt.Out
+ out.SeekSet(startoff)
+
+ for d := dr; d != nil; d = d.next {
+ out.Write32(uint32(uint64(isect.virtualAddress) + oftbase + d.thunkoff))
+ out.Write32(0)
+ out.Write32(0)
+ out.Write32(uint32(uint64(isect.virtualAddress) + d.nameoff))
+ out.Write32(uint32(uint64(datsect.virtualAddress) + ftbase + d.thunkoff))
+ }
+
+ out.Write32(0) //end
+ out.Write32(0)
+ out.Write32(0)
+ out.Write32(0)
+ out.Write32(0)
+
+ // update data directory
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(ldr.SymValue(dynamic) - PEBASE)
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(ldr.SymSize(dynamic))
+
+ out.SeekSet(endoff)
+}
+
+func initdynexport(ctxt *Link) {
+ ldr := ctxt.loader
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) || !ldr.AttrCgoExportDynamic(s) {
+ continue
+ }
+ if len(dexport)+1 > cap(dexport) {
+ ctxt.Errorf(s, "pe dynexport table is full")
+ errorexit()
+ }
+
+ dexport = append(dexport, s)
+ }
+
+ sort.Slice(dexport, func(i, j int) bool { return ldr.SymExtname(dexport[i]) < ldr.SymExtname(dexport[j]) })
+}
+
+func addexports(ctxt *Link) {
+ ldr := ctxt.loader
+ var e IMAGE_EXPORT_DIRECTORY
+
+ nexport := len(dexport)
+ size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1
+ for _, s := range dexport {
+ size += len(ldr.SymExtname(s)) + 1
+ }
+
+ if nexport == 0 {
+ return
+ }
+
+ sect := pefile.addSection(".edata", size, size)
+ sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+ sect.checkOffset(ctxt.Out.Offset())
+ va := int(sect.virtualAddress)
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va)
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize
+
+ vaName := va + binary.Size(&e) + nexport*4
+ vaAddr := va + binary.Size(&e)
+ vaNa := va + binary.Size(&e) + nexport*8
+
+ e.Characteristics = 0
+ e.MajorVersion = 0
+ e.MinorVersion = 0
+ e.NumberOfFunctions = uint32(nexport)
+ e.NumberOfNames = uint32(nexport)
+ e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names.
+ e.Base = 1
+ e.AddressOfFunctions = uint32(vaAddr)
+ e.AddressOfNames = uint32(vaName)
+ e.AddressOfNameOrdinals = uint32(vaNa)
+
+ out := ctxt.Out
+
+ // put IMAGE_EXPORT_DIRECTORY
+ binary.Write(out, binary.LittleEndian, &e)
+
+ // put EXPORT Address Table
+ for _, s := range dexport {
+ out.Write32(uint32(ldr.SymValue(s) - PEBASE))
+ }
+
+ // put EXPORT Name Pointer Table
+ v := int(e.Name + uint32(len(*flagOutfile)) + 1)
+
+ for _, s := range dexport {
+ out.Write32(uint32(v))
+ v += len(ldr.SymExtname(s)) + 1
+ }
+
+ // put EXPORT Ordinal Table
+ for i := 0; i < nexport; i++ {
+ out.Write16(uint16(i))
+ }
+
+ // put Names
+ out.WriteStringN(*flagOutfile, len(*flagOutfile)+1)
+
+ for _, s := range dexport {
+ name := ldr.SymExtname(s)
+ out.WriteStringN(name, len(name)+1)
+ }
+ sect.pad(out, uint32(size))
+}
+
+// peBaseRelocEntry represents a single relocation entry.
+type peBaseRelocEntry struct {
+ typeOff uint16
+}
+
+// peBaseRelocBlock represents a Base Relocation Block. A block
+// is a collection of relocation entries in a page, where each
+// entry describes a single relocation.
+// The block page RVA (Relative Virtual Address) is the index
+// into peBaseRelocTable.blocks.
+type peBaseRelocBlock struct {
+ entries []peBaseRelocEntry
+}
+
+// pePages is a type used to store the list of pages for which there
+// are base relocation blocks. This is defined as a type so that
+// it can be sorted.
+type pePages []uint32
+
+func (p pePages) Len() int { return len(p) }
+func (p pePages) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p pePages) Less(i, j int) bool { return p[i] < p[j] }
+
+// A PE base relocation table is a list of blocks, where each block
+// contains relocation information for a single page. The blocks
+// must be emitted in order of page virtual address.
+// See https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-reloc-section-image-only
+type peBaseRelocTable struct {
+ blocks map[uint32]peBaseRelocBlock
+
+ // pePages is a list of keys into blocks map.
+ // It is stored separately for ease of sorting.
+ pages pePages
+}
+
+func (rt *peBaseRelocTable) init(ctxt *Link) {
+ rt.blocks = make(map[uint32]peBaseRelocBlock)
+}
+
+func (rt *peBaseRelocTable) addentry(ldr *loader.Loader, s loader.Sym, r *loader.Reloc) {
+ // pageSize is the size in bytes of a page
+ // described by a base relocation block.
+ const pageSize = 0x1000
+ const pageMask = pageSize - 1
+
+ addr := ldr.SymValue(s) + int64(r.Off()) - int64(PEBASE)
+ page := uint32(addr &^ pageMask)
+ off := uint32(addr & pageMask)
+
+ b, ok := rt.blocks[page]
+ if !ok {
+ rt.pages = append(rt.pages, page)
+ }
+
+ e := peBaseRelocEntry{
+ typeOff: uint16(off & 0xFFF),
+ }
+
+ // Set entry type
+ switch r.Siz() {
+ default:
+ Exitf("unsupported relocation size %d\n", r.Siz)
+ case 4:
+ e.typeOff |= uint16(IMAGE_REL_BASED_HIGHLOW << 12)
+ case 8:
+ e.typeOff |= uint16(IMAGE_REL_BASED_DIR64 << 12)
+ }
+
+ b.entries = append(b.entries, e)
+ rt.blocks[page] = b
+}
+
+func (rt *peBaseRelocTable) write(ctxt *Link) {
+ out := ctxt.Out
+
+ // sort the pages array
+ sort.Sort(rt.pages)
+
+ for _, p := range rt.pages {
+ b := rt.blocks[p]
+ const sizeOfPEbaseRelocBlock = 8 // 2 * sizeof(uint32)
+ blockSize := uint32(sizeOfPEbaseRelocBlock + len(b.entries)*2)
+ out.Write32(p)
+ out.Write32(blockSize)
+
+ for _, e := range b.entries {
+ out.Write16(e.typeOff)
+ }
+ }
+}
+
+func addPEBaseRelocSym(ldr *loader.Loader, s loader.Sym, rt *peBaseRelocTable) {
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Type() >= objabi.ElfRelocOffset {
+ continue
+ }
+ if r.Siz() == 0 { // informational relocation
+ continue
+ }
+ if r.Type() == objabi.R_DWARFFILEREF {
+ continue
+ }
+ rs := r.Sym()
+ if rs == 0 {
+ continue
+ }
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+
+ switch r.Type() {
+ default:
+ case objabi.R_ADDR:
+ rt.addentry(ldr, s, &r)
+ }
+ }
+}
+
+func needPEBaseReloc(ctxt *Link) bool {
+ // Non-PIE x86 binaries don't need the base relocation table.
+ // Everyone else does.
+ if (ctxt.Arch.Family == sys.I386 || ctxt.Arch.Family == sys.AMD64) && ctxt.BuildMode != BuildModePIE {
+ return false
+ }
+ return true
+}
+
+func addPEBaseReloc(ctxt *Link) {
+ if !needPEBaseReloc(ctxt) {
+ return
+ }
+
+ var rt peBaseRelocTable
+ rt.init(ctxt)
+
+ // Get relocation information
+ ldr := ctxt.loader
+ for _, s := range ctxt.Textp {
+ addPEBaseRelocSym(ldr, s, &rt)
+ }
+ for _, s := range ctxt.datap {
+ addPEBaseRelocSym(ldr, s, &rt)
+ }
+
+ // Write relocation information
+ startoff := ctxt.Out.Offset()
+ rt.write(ctxt)
+ size := ctxt.Out.Offset() - startoff
+
+ // Add a PE section and pad it at the end
+ rsect := pefile.addSection(".reloc", int(size), int(size))
+ rsect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
+ rsect.checkOffset(startoff)
+ rsect.pad(ctxt.Out, uint32(size))
+
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize
+}
+
+func (ctxt *Link) dope() {
+ initdynimport(ctxt)
+ initdynexport(ctxt)
+}
+
+func setpersrc(ctxt *Link, syms []loader.Sym) {
+ if len(rsrcsyms) != 0 {
+ Errorf(nil, "too many .rsrc sections")
+ }
+ rsrcsyms = syms
+}
+
+func addpersrc(ctxt *Link) {
+ if len(rsrcsyms) == 0 {
+ return
+ }
+
+ var size int64
+ for _, rsrcsym := range rsrcsyms {
+ size += ctxt.loader.SymSize(rsrcsym)
+ }
+ h := pefile.addSection(".rsrc", int(size), int(size))
+ h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA
+ h.checkOffset(ctxt.Out.Offset())
+
+ for _, rsrcsym := range rsrcsyms {
+ // A split resource happens when the actual resource data and its relocations are
+ // split across multiple sections, denoted by a $01 or $02 at the end of the .rsrc
+ // section name.
+ splitResources := strings.Contains(ctxt.loader.SymName(rsrcsym), ".rsrc$")
+ relocs := ctxt.loader.Relocs(rsrcsym)
+ data := ctxt.loader.Data(rsrcsym)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ p := data[r.Off():]
+ val := uint32(int64(h.virtualAddress) + r.Add())
+ if splitResources {
+ // If we're a split resource section, and that section has relocation
+ // symbols, then the data that it points to doesn't actually begin at
+ // the virtual address listed in this current section, but rather
+ // begins at the section immediately after this one. So, in order to
+ // calculate the proper virtual address of the data it's pointing to,
+ // we have to add the length of this section to the virtual address.
+ // This works because .rsrc sections are divided into two (but not more)
+ // of these sections.
+ val += uint32(len(data))
+ }
+ binary.LittleEndian.PutUint32(p, val)
+ }
+ ctxt.Out.Write(data)
+ }
+ h.pad(ctxt.Out, uint32(size))
+
+ // update data directory
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress
+ pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize
+}
+
+func asmbPe(ctxt *Link) {
+ t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length))
+ t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
+ if ctxt.LinkMode == LinkExternal {
+ // some data symbols (e.g. masks) end up in the .text section, and they normally
+ // expect larger alignment requirement than the default text section alignment.
+ t.characteristics |= IMAGE_SCN_ALIGN_32BYTES
+ }
+ t.checkSegment(&Segtext)
+ pefile.textSect = t
+
+ ro := pefile.addSection(".rdata", int(Segrodata.Length), int(Segrodata.Length))
+ ro.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+ if ctxt.LinkMode == LinkExternal {
+ // some data symbols (e.g. masks) end up in the .rdata section, and they normally
+ // expect larger alignment requirement than the default text section alignment.
+ ro.characteristics |= IMAGE_SCN_ALIGN_32BYTES
+ }
+ ro.checkSegment(&Segrodata)
+ pefile.rdataSect = ro
+
+ var d *peSection
+ if ctxt.LinkMode != LinkExternal {
+ d = pefile.addSection(".data", int(Segdata.Length), int(Segdata.Filelen))
+ d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
+ d.checkSegment(&Segdata)
+ pefile.dataSect = d
+ } else {
+ d = pefile.addSection(".data", int(Segdata.Filelen), int(Segdata.Filelen))
+ d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
+ d.checkSegment(&Segdata)
+ pefile.dataSect = d
+
+ b := pefile.addSection(".bss", int(Segdata.Length-Segdata.Filelen), 0)
+ b.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
+ b.pointerToRawData = 0
+ pefile.bssSect = b
+ }
+
+ pefile.addDWARF()
+
+ if ctxt.LinkMode == LinkExternal {
+ pefile.ctorsSect = pefile.addInitArray(ctxt)
+ }
+
+ ctxt.Out.SeekSet(int64(pefile.nextFileOffset))
+ if ctxt.LinkMode != LinkExternal {
+ addimports(ctxt, d)
+ addexports(ctxt)
+ addPEBaseReloc(ctxt)
+ }
+ pefile.writeSymbolTableAndStringTable(ctxt)
+ addpersrc(ctxt)
+ if ctxt.LinkMode == LinkExternal {
+ pefile.emitRelocations(ctxt)
+ }
+
+ pewrite(ctxt)
+}
diff --git a/src/cmd/link/internal/ld/stackcheck.go b/src/cmd/link/internal/ld/stackcheck.go
new file mode 100644
index 0000000..c82dafe
--- /dev/null
+++ b/src/cmd/link/internal/ld/stackcheck.go
@@ -0,0 +1,421 @@
+// Copyright 2022 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.
+
+package ld
+
+import (
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "fmt"
+ "internal/buildcfg"
+ "sort"
+ "strings"
+)
+
+type stackCheck struct {
+ ctxt *Link
+ ldr *loader.Loader
+ morestack loader.Sym
+ callSize int // The number of bytes added by a CALL
+
+ // height records the maximum number of bytes a function and
+ // its callees can add to the stack without a split check.
+ height map[loader.Sym]int16
+
+ // graph records the out-edges from each symbol. This is only
+ // populated on a second pass if the first pass reveals an
+ // over-limit function.
+ graph map[loader.Sym][]stackCheckEdge
+}
+
+type stackCheckEdge struct {
+ growth int // Stack growth in bytes at call to target
+ target loader.Sym // 0 for stack growth without a call
+}
+
+// stackCheckCycle is a sentinel stored in the height map to detect if
+// we've found a cycle. This is effectively an "infinite" stack
+// height, so we use the closest value to infinity that we can.
+const stackCheckCycle int16 = 1<<15 - 1
+
+// stackCheckIndirect is a sentinel Sym value used to represent the
+// target of an indirect/closure call.
+const stackCheckIndirect loader.Sym = -1
+
+// doStackCheck walks the call tree to check that there is always
+// enough stack space for call frames, especially for a chain of
+// nosplit functions.
+//
+// It walks all functions to accumulate the number of bytes they can
+// grow the stack by without a split check and checks this against the
+// limit.
+func (ctxt *Link) doStackCheck() {
+ sc := newStackCheck(ctxt, false)
+
+ // limit is number of bytes a splittable function ensures are
+ // available on the stack. If any call chain exceeds this
+ // depth, the stack check test fails.
+ //
+ // The call to morestack in every splittable function ensures
+ // that there are at least StackLimit bytes available below SP
+ // when morestack returns.
+ limit := objabi.StackLimit(*flagRace) - sc.callSize
+ if buildcfg.GOARCH == "arm64" {
+ // Need an extra 8 bytes below SP to save FP.
+ limit -= 8
+ }
+
+ // Compute stack heights without any back-tracking information.
+ // This will almost certainly succeed and we can simply
+ // return. If it fails, we do a second pass with back-tracking
+ // to produce a good error message.
+ //
+ // This accumulates stack heights bottom-up so it only has to
+ // visit every function once.
+ var failed []loader.Sym
+ for _, s := range ctxt.Textp {
+ if sc.check(s) > limit {
+ failed = append(failed, s)
+ }
+ }
+
+ if len(failed) > 0 {
+ // Something was over-limit, so now we do the more
+ // expensive work to report a good error. First, for
+ // the over-limit functions, redo the stack check but
+ // record the graph this time.
+ sc = newStackCheck(ctxt, true)
+ for _, s := range failed {
+ sc.check(s)
+ }
+
+ // Find the roots of the graph (functions that are not
+ // called by any other function).
+ roots := sc.findRoots()
+
+ // Find and report all paths that go over the limit.
+ // This accumulates stack depths top-down. This is
+ // much less efficient because we may have to visit
+ // the same function multiple times at different
+ // depths, but lets us find all paths.
+ for _, root := range roots {
+ ctxt.Errorf(root, "nosplit stack over %d byte limit", limit)
+ chain := []stackCheckChain{{stackCheckEdge{0, root}, false}}
+ sc.report(root, limit, &chain)
+ }
+ }
+}
+
+func newStackCheck(ctxt *Link, graph bool) *stackCheck {
+ sc := &stackCheck{
+ ctxt: ctxt,
+ ldr: ctxt.loader,
+ morestack: ctxt.loader.Lookup("runtime.morestack", 0),
+ height: make(map[loader.Sym]int16, len(ctxt.Textp)),
+ }
+ // Compute stack effect of a CALL operation. 0 on LR machines.
+ // 1 register pushed on non-LR machines.
+ if !ctxt.Arch.HasLR {
+ sc.callSize = ctxt.Arch.RegSize
+ }
+
+ if graph {
+ // We're going to record the call graph.
+ sc.graph = make(map[loader.Sym][]stackCheckEdge)
+ }
+
+ return sc
+}
+
+func (sc *stackCheck) symName(sym loader.Sym) string {
+ switch sym {
+ case stackCheckIndirect:
+ return "indirect"
+ case 0:
+ return "leaf"
+ }
+ return fmt.Sprintf("%s<%d>", sc.ldr.SymName(sym), sc.ldr.SymVersion(sym))
+}
+
+// check returns the stack height of sym. It populates sc.height and
+// sc.graph for sym and every function in its call tree.
+func (sc *stackCheck) check(sym loader.Sym) int {
+ if h, ok := sc.height[sym]; ok {
+ // We've already visited this symbol or we're in a cycle.
+ return int(h)
+ }
+ // Store the sentinel so we can detect cycles.
+ sc.height[sym] = stackCheckCycle
+ // Compute and record the height and optionally edges.
+ h, edges := sc.computeHeight(sym, *flagDebugNosplit || sc.graph != nil)
+ if h > int(stackCheckCycle) { // Prevent integer overflow
+ h = int(stackCheckCycle)
+ }
+ sc.height[sym] = int16(h)
+ if sc.graph != nil {
+ sc.graph[sym] = edges
+ }
+
+ if *flagDebugNosplit {
+ for _, edge := range edges {
+ fmt.Printf("nosplit: %s +%d", sc.symName(sym), edge.growth)
+ if edge.target == 0 {
+ // Local stack growth or leaf function.
+ fmt.Printf("\n")
+ } else {
+ fmt.Printf(" -> %s\n", sc.symName(edge.target))
+ }
+ }
+ }
+
+ return h
+}
+
+// computeHeight returns the stack height of sym. If graph is true, it
+// also returns the out-edges of sym.
+//
+// Caching is applied to this in check. Call check instead of calling
+// this directly.
+func (sc *stackCheck) computeHeight(sym loader.Sym, graph bool) (int, []stackCheckEdge) {
+ ldr := sc.ldr
+
+ // Check special cases.
+ if sym == sc.morestack {
+ // morestack looks like it calls functions, but they
+ // either happen only when already on the system stack
+ // (where there is ~infinite space), or after
+ // switching to the system stack. Hence, its stack
+ // height on the user stack is 0.
+ return 0, nil
+ }
+ if sym == stackCheckIndirect {
+ // Assume that indirect/closure calls are always to
+ // splittable functions, so they just need enough room
+ // to call morestack.
+ return sc.callSize, []stackCheckEdge{{sc.callSize, sc.morestack}}
+ }
+
+ // Ignore calls to external functions. Assume that these calls
+ // are only ever happening on the system stack, where there's
+ // plenty of room.
+ if ldr.AttrExternal(sym) {
+ return 0, nil
+ }
+ if info := ldr.FuncInfo(sym); !info.Valid() { // also external
+ return 0, nil
+ }
+
+ // Track the maximum height of this function and, if we're
+ // recording the graph, its out-edges.
+ var edges []stackCheckEdge
+ maxHeight := 0
+ ctxt := sc.ctxt
+ // addEdge adds a stack growth out of this function to
+ // function "target" or, if target == 0, a local stack growth
+ // within the function.
+ addEdge := func(growth int, target loader.Sym) {
+ if graph {
+ edges = append(edges, stackCheckEdge{growth, target})
+ }
+ height := growth
+ if target != 0 { // Don't walk into the leaf "edge"
+ height += sc.check(target)
+ }
+ if height > maxHeight {
+ maxHeight = height
+ }
+ }
+
+ if !ldr.IsNoSplit(sym) {
+ // Splittable functions start with a call to
+ // morestack, after which their height is 0. Account
+ // for the height of the call to morestack.
+ addEdge(sc.callSize, sc.morestack)
+ return maxHeight, edges
+ }
+
+ // This function is nosplit, so it adjusts SP without a split
+ // check.
+ //
+ // Walk through SP adjustments in function, consuming relocs
+ // and following calls.
+ maxLocalHeight := 0
+ relocs, ri := ldr.Relocs(sym), 0
+ pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
+ for pcsp.Init(ldr.Data(ldr.Pcsp(sym))); !pcsp.Done; pcsp.Next() {
+ // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
+ height := int(pcsp.Value)
+ if height > maxLocalHeight {
+ maxLocalHeight = height
+ }
+
+ // Process calls in this span.
+ for ; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if uint32(r.Off()) >= pcsp.NextPC {
+ break
+ }
+ t := r.Type()
+ if t.IsDirectCall() || t == objabi.R_CALLIND {
+ growth := height + sc.callSize
+ var target loader.Sym
+ if t == objabi.R_CALLIND {
+ target = stackCheckIndirect
+ } else {
+ target = r.Sym()
+ }
+ addEdge(growth, target)
+ }
+ }
+ }
+ if maxLocalHeight > maxHeight {
+ // This is either a leaf function, or the function
+ // grew its stack to larger than the maximum call
+ // height between calls. Either way, record that local
+ // stack growth.
+ addEdge(maxLocalHeight, 0)
+ }
+
+ return maxHeight, edges
+}
+
+func (sc *stackCheck) findRoots() []loader.Sym {
+ // Collect all nodes.
+ nodes := make(map[loader.Sym]struct{})
+ for k := range sc.graph {
+ nodes[k] = struct{}{}
+ }
+
+ // Start a DFS from each node and delete all reachable
+ // children. If we encounter an unrooted cycle, this will
+ // delete everything in that cycle, so we detect this case and
+ // track the lowest-numbered node encountered in the cycle and
+ // put that node back as a root.
+ var walk func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym)
+ walk = func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym) {
+ if _, ok := nodes[sym]; !ok {
+ // We already deleted this node.
+ return false, 0
+ }
+ delete(nodes, sym)
+
+ if origin == sym {
+ // We found an unrooted cycle. We already
+ // deleted all children of this node. Walk
+ // back up, tracking the lowest numbered
+ // symbol in this cycle.
+ return true, sym
+ }
+
+ // Delete children of this node.
+ for _, out := range sc.graph[sym] {
+ if c, l := walk(origin, out.target); c {
+ cycle = true
+ if lowest == 0 {
+ // On first cycle detection,
+ // add sym to the set of
+ // lowest-numbered candidates.
+ lowest = sym
+ }
+ if l < lowest {
+ lowest = l
+ }
+ }
+ }
+ return
+ }
+ for k := range nodes {
+ // Delete all children of k.
+ for _, out := range sc.graph[k] {
+ if cycle, lowest := walk(k, out.target); cycle {
+ // This is an unrooted cycle so we
+ // just deleted everything. Put back
+ // the lowest-numbered symbol.
+ nodes[lowest] = struct{}{}
+ }
+ }
+ }
+
+ // Sort roots by height. This makes the result deterministic
+ // and also improves the error reporting.
+ var roots []loader.Sym
+ for k := range nodes {
+ roots = append(roots, k)
+ }
+ sort.Slice(roots, func(i, j int) bool {
+ h1, h2 := sc.height[roots[i]], sc.height[roots[j]]
+ if h1 != h2 {
+ return h1 > h2
+ }
+ // Secondary sort by Sym.
+ return roots[i] < roots[j]
+ })
+ return roots
+}
+
+type stackCheckChain struct {
+ stackCheckEdge
+ printed bool
+}
+
+func (sc *stackCheck) report(sym loader.Sym, depth int, chain *[]stackCheckChain) {
+ // Walk the out-edges of sym. We temporarily pull the edges
+ // out of the graph to detect cycles and prevent infinite
+ // recursion.
+ edges, ok := sc.graph[sym]
+ isCycle := !(ok || sym == 0)
+ delete(sc.graph, sym)
+ for _, out := range edges {
+ *chain = append(*chain, stackCheckChain{out, false})
+ sc.report(out.target, depth-out.growth, chain)
+ *chain = (*chain)[:len(*chain)-1]
+ }
+ sc.graph[sym] = edges
+
+ // If we've reached the end of a chain and it went over the
+ // stack limit or was a cycle that would eventually go over,
+ // print the whole chain.
+ //
+ // We should either be in morestack (which has no out-edges)
+ // or the sentinel 0 Sym "called" from a leaf function (which
+ // has no out-edges), or we came back around a cycle (possibly
+ // to ourselves) and edges was temporarily nil'd.
+ if len(edges) == 0 && (depth < 0 || isCycle) {
+ var indent string
+ for i := range *chain {
+ ent := &(*chain)[i]
+ if ent.printed {
+ // Already printed on an earlier part
+ // of this call tree.
+ continue
+ }
+ ent.printed = true
+
+ if i == 0 {
+ // chain[0] is just the root function,
+ // not a stack growth.
+ fmt.Printf("%s\n", sc.symName(ent.target))
+ continue
+ }
+
+ indent = strings.Repeat(" ", i)
+ fmt.Print(indent)
+ // Grows the stack X bytes and (maybe) calls Y.
+ fmt.Printf("grows %d bytes", ent.growth)
+ if ent.target == 0 {
+ // Not a call, just a leaf. Print nothing.
+ } else {
+ fmt.Printf(", calls %s", sc.symName(ent.target))
+ }
+ fmt.Printf("\n")
+ }
+ // Print how far over this chain went.
+ if isCycle {
+ fmt.Printf("%sinfinite cycle\n", indent)
+ } else {
+ fmt.Printf("%s%d bytes over limit\n", indent, -depth)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/stackcheck_test.go b/src/cmd/link/internal/ld/stackcheck_test.go
new file mode 100644
index 0000000..dd7e205
--- /dev/null
+++ b/src/cmd/link/internal/ld/stackcheck_test.go
@@ -0,0 +1,87 @@
+// Copyright 2022 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.
+
+package ld
+
+import (
+ "fmt"
+ "internal/testenv"
+ "os"
+ "regexp"
+ "strconv"
+ "testing"
+)
+
+// See also $GOROOT/test/nosplit.go for multi-platform edge case tests.
+
+func TestStackCheckOutput(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", os.DevNull, "./testdata/stackcheck")
+ // The rules for computing frame sizes on all of the
+ // architectures are complicated, so just do this on amd64.
+ cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux")
+ outB, err := cmd.CombinedOutput()
+
+ if err == nil {
+ t.Fatalf("expected link to fail")
+ }
+ out := string(outB)
+
+ t.Logf("linker output:\n%s", out)
+
+ // Get expected limit.
+ limitRe := regexp.MustCompile(`nosplit stack over (\d+) byte limit`)
+ m := limitRe.FindStringSubmatch(out)
+ if m == nil {
+ t.Fatalf("no overflow errors in output")
+ }
+ limit, _ := strconv.Atoi(m[1])
+
+ wantMap := map[string]string{
+ "main.startSelf": fmt.Sprintf(
+ `main.startSelf<0>
+ grows 1008 bytes
+ %d bytes over limit
+`, 1008-limit),
+ "main.startChain": fmt.Sprintf(
+ `main.startChain<0>
+ grows 32 bytes, calls main.chain0<0>
+ grows 48 bytes, calls main.chainEnd<0>
+ grows 1008 bytes
+ %d bytes over limit
+ grows 32 bytes, calls main.chain2<0>
+ grows 80 bytes, calls main.chainEnd<0>
+ grows 1008 bytes
+ %d bytes over limit
+`, 32+48+1008-limit, 32+80+1008-limit),
+ "main.startRec": `main.startRec<0>
+ grows 8 bytes, calls main.startRec0<0>
+ grows 8 bytes, calls main.startRec<0>
+ infinite cycle
+`,
+ }
+
+ // Parse stanzas
+ stanza := regexp.MustCompile(`^(.*): nosplit stack over \d+ byte limit\n(.*\n(?: .*\n)*)`)
+ // Strip comments from cmd/go
+ out = regexp.MustCompile(`(?m)^#.*\n`).ReplaceAllString(out, "")
+ for len(out) > 0 {
+ m := stanza.FindStringSubmatch(out)
+ if m == nil {
+ t.Fatalf("unexpected output:\n%s", out)
+ }
+ out = out[len(m[0]):]
+ fn := m[1]
+ got := m[2]
+
+ want, ok := wantMap[fn]
+ if !ok {
+ t.Errorf("unexpected function: %s", fn)
+ } else if want != got {
+ t.Errorf("want:\n%sgot:\n%s", want, got)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go
new file mode 100644
index 0000000..d51a59e
--- /dev/null
+++ b/src/cmd/link/internal/ld/sym.go
@@ -0,0 +1,117 @@
+// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "internal/buildcfg"
+ "log"
+ "runtime"
+)
+
+func linknew(arch *sys.Arch) *Link {
+ ler := loader.ErrorReporter{AfterErrorAction: afterErrorAction}
+ ctxt := &Link{
+ Target: Target{Arch: arch},
+ version: sym.SymVerStatic,
+ outSem: make(chan int, 2*runtime.GOMAXPROCS(0)),
+ Out: NewOutBuf(arch),
+ LibraryByPkg: make(map[string]*sym.Library),
+ numelfsym: 1,
+ ErrorReporter: ErrorReporter{ErrorReporter: ler},
+ generatorSyms: make(map[loader.Sym]generatorFunc),
+ }
+
+ if buildcfg.GOARCH != arch.Name {
+ log.Fatalf("invalid buildcfg.GOARCH %s (want %s)", buildcfg.GOARCH, arch.Name)
+ }
+
+ AtExit(func() {
+ if nerrors > 0 {
+ ctxt.Out.ErrorClose()
+ mayberemoveoutfile()
+ }
+ })
+
+ return ctxt
+}
+
+// computeTLSOffset records the thread-local storage offset.
+// Not used for Android where the TLS offset is determined at runtime.
+func (ctxt *Link) computeTLSOffset() {
+ switch ctxt.HeadType {
+ default:
+ log.Fatalf("unknown thread-local storage offset for %v", ctxt.HeadType)
+
+ case objabi.Hplan9, objabi.Hwindows, objabi.Hjs, objabi.Haix:
+ break
+
+ case objabi.Hlinux,
+ objabi.Hfreebsd,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd,
+ objabi.Hdragonfly,
+ objabi.Hsolaris:
+ /*
+ * ELF uses TLS offset negative from FS.
+ * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS).
+ * Known to low-level assembly in package runtime and runtime/cgo.
+ */
+ ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize
+
+ case objabi.Hdarwin:
+ /*
+ * OS X system constants - offset from 0(GS) to our TLS.
+ */
+ switch ctxt.Arch.Family {
+ default:
+ log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name)
+
+ /*
+ * For x86, Apple has reserved a slot in the TLS for Go. See issue 23617.
+ * That slot is at offset 0x30 on amd64.
+ * The slot will hold the G pointer.
+ * These constants should match those in runtime/sys_darwin_amd64.s
+ * and runtime/cgo/gcc_darwin_amd64.c.
+ */
+ case sys.AMD64:
+ ctxt.Tlsoffset = 0x30
+
+ case sys.ARM64:
+ ctxt.Tlsoffset = 0 // dummy value, not needed
+ }
+ }
+
+}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
new file mode 100644
index 0000000..21a1466
--- /dev/null
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -0,0 +1,890 @@
+// Inferno utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "internal/buildcfg"
+ "path/filepath"
+ "strings"
+)
+
+// Symbol table.
+
+func putelfstr(s string) int {
+ if len(Elfstrdat) == 0 && s != "" {
+ // first entry must be empty string
+ putelfstr("")
+ }
+
+ off := len(Elfstrdat)
+ Elfstrdat = append(Elfstrdat, s...)
+ Elfstrdat = append(Elfstrdat, 0)
+ return off
+}
+
+func putelfsyment(out *OutBuf, off int, addr int64, size int64, info uint8, shndx elf.SectionIndex, other int) {
+ if elf64 {
+ out.Write32(uint32(off))
+ out.Write8(info)
+ out.Write8(uint8(other))
+ out.Write16(uint16(shndx))
+ out.Write64(uint64(addr))
+ out.Write64(uint64(size))
+ symSize += ELF64SYMSIZE
+ } else {
+ out.Write32(uint32(off))
+ out.Write32(uint32(addr))
+ out.Write32(uint32(size))
+ out.Write8(info)
+ out.Write8(uint8(other))
+ out.Write16(uint16(shndx))
+ symSize += ELF32SYMSIZE
+ }
+}
+
+func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
+ ldr := ctxt.loader
+ addr := ldr.SymValue(x)
+ size := ldr.SymSize(x)
+
+ xo := x
+ if ldr.OuterSym(x) != 0 {
+ xo = ldr.OuterSym(x)
+ }
+ xot := ldr.SymType(xo)
+ xosect := ldr.SymSect(xo)
+
+ var elfshnum elf.SectionIndex
+ if xot == sym.SDYNIMPORT || xot == sym.SHOSTOBJ || xot == sym.SUNDEFEXT {
+ elfshnum = elf.SHN_UNDEF
+ size = 0
+ } else {
+ if xosect == nil {
+ ldr.Errorf(x, "missing section in putelfsym")
+ return
+ }
+ if xosect.Elfsect == nil {
+ ldr.Errorf(x, "missing ELF section in putelfsym")
+ return
+ }
+ elfshnum = xosect.Elfsect.(*ElfShdr).shnum
+ }
+
+ sname := ldr.SymExtname(x)
+ sname = mangleABIName(ctxt, ldr, x, sname)
+
+ // One pass for each binding: elf.STB_LOCAL, elf.STB_GLOBAL,
+ // maybe one day elf.STB_WEAK.
+ bind := elf.STB_GLOBAL
+ if ldr.IsFileLocal(x) && !isStaticTmp(sname) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
+ // Static tmp is package local, but a package can be shared among multiple DSOs.
+ // They need to have a single view of the static tmp that are writable.
+ bind = elf.STB_LOCAL
+ }
+
+ // In external linking mode, we have to invoke gcc with -rdynamic
+ // to get the exported symbols put into the dynamic symbol table.
+ // To avoid filling the dynamic table with lots of unnecessary symbols,
+ // mark all Go symbols local (not global) in the final executable.
+ // But when we're dynamically linking, we need all those global symbols.
+ if !ctxt.DynlinkingGo() && ctxt.IsExternal() && !ldr.AttrCgoExportStatic(x) && elfshnum != elf.SHN_UNDEF {
+ bind = elf.STB_LOCAL
+ }
+
+ if ctxt.LinkMode == LinkExternal && elfshnum != elf.SHN_UNDEF {
+ addr -= int64(xosect.Vaddr)
+ }
+ other := int(elf.STV_DEFAULT)
+ if ldr.AttrVisibilityHidden(x) {
+ // TODO(mwhudson): We only set AttrVisibilityHidden in ldelf, i.e. when
+ // internally linking. But STV_HIDDEN visibility only matters in object
+ // files and shared libraries, and as we are a long way from implementing
+ // internal linking for shared libraries and only create object files when
+ // externally linking, I don't think this makes a lot of sense.
+ other = int(elf.STV_HIDDEN)
+ }
+ if ctxt.IsPPC64() && typ == elf.STT_FUNC && ldr.AttrShared(x) && ldr.SymName(x) != "runtime.duffzero" && ldr.SymName(x) != "runtime.duffcopy" {
+ // On ppc64 the top three bits of the st_other field indicate how
+ // many instructions separate the global and local entry points. In
+ // our case it is two instructions, indicated by the value 3.
+ // The conditions here match those in preprocess in
+ // cmd/internal/obj/ppc64/obj9.go, which is where the
+ // instructions are inserted.
+ other |= 3 << 5
+ }
+
+ // When dynamically linking, we create Symbols by reading the names from
+ // the symbol tables of the shared libraries and so the names need to
+ // match exactly. Tools like DTrace will have to wait for now.
+ if !ctxt.DynlinkingGo() {
+ // Rewrite · to . for ASCII-only tools like DTrace (sigh)
+ sname = strings.Replace(sname, "·", ".", -1)
+ }
+
+ if ctxt.DynlinkingGo() && bind == elf.STB_GLOBAL && curbind == elf.STB_LOCAL && ldr.SymType(x) == sym.STEXT {
+ // When dynamically linking, we want references to functions defined
+ // in this module to always be to the function object, not to the
+ // PLT. We force this by writing an additional local symbol for every
+ // global function symbol and making all relocations against the
+ // global symbol refer to this local symbol instead (see
+ // (*sym.Symbol).ElfsymForReloc). This is approximately equivalent to the
+ // ELF linker -Bsymbolic-functions option, but that is buggy on
+ // several platforms.
+ putelfsyment(ctxt.Out, putelfstr("local."+sname), addr, size, elf.ST_INFO(elf.STB_LOCAL, typ), elfshnum, other)
+ ldr.SetSymLocalElfSym(x, int32(ctxt.numelfsym))
+ ctxt.numelfsym++
+ return
+ } else if bind != curbind {
+ return
+ }
+
+ putelfsyment(ctxt.Out, putelfstr(sname), addr, size, elf.ST_INFO(bind, typ), elfshnum, other)
+ ldr.SetSymElfSym(x, int32(ctxt.numelfsym))
+ ctxt.numelfsym++
+}
+
+func putelfsectionsym(ctxt *Link, out *OutBuf, s loader.Sym, shndx elf.SectionIndex) {
+ putelfsyment(out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_SECTION), shndx, 0)
+ ctxt.loader.SetSymElfSym(s, int32(ctxt.numelfsym))
+ ctxt.numelfsym++
+}
+
+func genelfsym(ctxt *Link, elfbind elf.SymBind) {
+ ldr := ctxt.loader
+
+ // runtime.text marker symbol(s).
+ s := ldr.Lookup("runtime.text", 0)
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
+ for k, sect := range Segtext.Sections[1:] {
+ n := k + 1
+ if sect.Name != ".text" || (ctxt.IsAIX() && ctxt.IsExternal()) {
+ // On AIX, runtime.text.X are symbols already in the symtab.
+ break
+ }
+ s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
+ if s == 0 {
+ break
+ }
+ if ldr.SymType(s) != sym.STEXT {
+ panic("unexpected type for runtime.text symbol")
+ }
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
+ }
+
+ // Text symbols.
+ for _, s := range ctxt.Textp {
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
+ }
+
+ // runtime.etext marker symbol.
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym) bool {
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ // FIXME: avoid having to do name inspections here.
+ // NB: the restrictions below on file local symbols are a bit
+ // arbitrary -- if it turns out we need nameless static
+ // symbols they could be relaxed/removed.
+ sn := ldr.SymName(s)
+ if (sn == "" || sn[0] == '.') && ldr.IsFileLocal(s) {
+ panic(fmt.Sprintf("unexpected file local symbol %d %s<%d>\n",
+ s, sn, ldr.SymVersion(s)))
+ }
+ if (sn == "" || sn[0] == '.') && !ldr.IsFileLocal(s) {
+ return false
+ }
+ return true
+ }
+
+ // Data symbols.
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ st := ldr.SymType(s)
+ if st >= sym.SELFRXSECT && st < sym.SXREF {
+ typ := elf.STT_OBJECT
+ if st == sym.STLSBSS {
+ if ctxt.IsInternal() {
+ continue
+ }
+ typ = elf.STT_TLS
+ }
+ if !shouldBeInSymbolTable(s) {
+ continue
+ }
+ putelfsym(ctxt, s, typ, elfbind)
+ continue
+ }
+ if st == sym.SHOSTOBJ || st == sym.SDYNIMPORT || st == sym.SUNDEFEXT {
+ putelfsym(ctxt, s, ldr.SymElfType(s), elfbind)
+ }
+ }
+}
+
+func asmElfSym(ctxt *Link) {
+
+ // the first symbol entry is reserved
+ putelfsyment(ctxt.Out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE), 0, 0)
+
+ dwarfaddelfsectionsyms(ctxt)
+
+ // Some linkers will add a FILE sym if one is not present.
+ // Avoid having the working directory inserted into the symbol table.
+ // It is added with a name to avoid problems with external linking
+ // encountered on some versions of Solaris. See issue #14957.
+ putelfsyment(ctxt.Out, putelfstr("go.go"), 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_FILE), elf.SHN_ABS, 0)
+ ctxt.numelfsym++
+
+ bindings := []elf.SymBind{elf.STB_LOCAL, elf.STB_GLOBAL}
+ for _, elfbind := range bindings {
+ if elfbind == elf.STB_GLOBAL {
+ elfglobalsymndx = ctxt.numelfsym
+ }
+ genelfsym(ctxt, elfbind)
+ }
+}
+
+func putplan9sym(ctxt *Link, ldr *loader.Loader, s loader.Sym, char SymbolType) {
+ t := int(char)
+ if ldr.IsFileLocal(s) {
+ t += 'a' - 'A'
+ }
+ l := 4
+ addr := ldr.SymValue(s)
+ if ctxt.IsAMD64() && !flag8 {
+ ctxt.Out.Write32b(uint32(addr >> 32))
+ l = 8
+ }
+
+ ctxt.Out.Write32b(uint32(addr))
+ ctxt.Out.Write8(uint8(t + 0x80)) /* 0x80 is variable length */
+
+ name := ldr.SymName(s)
+ name = mangleABIName(ctxt, ldr, s, name)
+ ctxt.Out.WriteString(name)
+ ctxt.Out.Write8(0)
+
+ symSize += int32(l) + 1 + int32(len(name)) + 1
+}
+
+func asmbPlan9Sym(ctxt *Link) {
+ ldr := ctxt.loader
+
+ // Add special runtime.text and runtime.etext symbols.
+ s := ldr.Lookup("runtime.text", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ putplan9sym(ctxt, ldr, s, TextSym)
+ }
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ putplan9sym(ctxt, ldr, s, TextSym)
+ }
+
+ // Add text symbols.
+ for _, s := range ctxt.Textp {
+ putplan9sym(ctxt, ldr, s, TextSym)
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym) bool {
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ name := ldr.SymName(s) // TODO: try not to read the name
+ if name == "" || name[0] == '.' {
+ return false
+ }
+ return true
+ }
+
+ // Add data symbols and external references.
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ t := ldr.SymType(s)
+ if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata
+ if t == sym.STLSBSS {
+ continue
+ }
+ if !shouldBeInSymbolTable(s) {
+ continue
+ }
+ char := DataSym
+ if t == sym.SBSS || t == sym.SNOPTRBSS {
+ char = BSSSym
+ }
+ putplan9sym(ctxt, ldr, s, char)
+ }
+ }
+}
+
+type byPkg []*sym.Library
+
+func (libs byPkg) Len() int {
+ return len(libs)
+}
+
+func (libs byPkg) Less(a, b int) bool {
+ return libs[a].Pkg < libs[b].Pkg
+}
+
+func (libs byPkg) Swap(a, b int) {
+ libs[a], libs[b] = libs[b], libs[a]
+}
+
+// Create a table with information on the text sections.
+// Return the symbol of the table, and number of sections.
+func textsectionmap(ctxt *Link) (loader.Sym, uint32) {
+ ldr := ctxt.loader
+ t := ldr.CreateSymForUpdate("runtime.textsectionmap", 0)
+ t.SetType(sym.SRODATA)
+ nsections := int64(0)
+
+ for _, sect := range Segtext.Sections {
+ if sect.Name == ".text" {
+ nsections++
+ } else {
+ break
+ }
+ }
+ t.Grow(3 * nsections * int64(ctxt.Arch.PtrSize))
+
+ off := int64(0)
+ n := 0
+
+ // The vaddr for each text section is the difference between the section's
+ // Vaddr and the Vaddr for the first text section as determined at compile
+ // time.
+
+ // The symbol for the first text section is named runtime.text as before.
+ // Additional text sections are named runtime.text.n where n is the
+ // order of creation starting with 1. These symbols provide the section's
+ // address after relocation by the linker.
+
+ textbase := Segtext.Sections[0].Vaddr
+ for _, sect := range Segtext.Sections {
+ if sect.Name != ".text" {
+ break
+ }
+ // The fields written should match runtime/symtab.go:textsect.
+ // They are designed to minimize runtime calculations.
+ vaddr := sect.Vaddr - textbase
+ off = t.SetUint(ctxt.Arch, off, vaddr) // field vaddr
+ end := vaddr + sect.Length
+ off = t.SetUint(ctxt.Arch, off, end) // field end
+ name := "runtime.text"
+ if n != 0 {
+ name = fmt.Sprintf("runtime.text.%d", n)
+ }
+ s := ldr.Lookup(name, 0)
+ if s == 0 {
+ ctxt.Errorf(s, "Unable to find symbol %s\n", name)
+ }
+ off = t.SetAddr(ctxt.Arch, off, s) // field baseaddr
+ n++
+ }
+ return t.Sym(), uint32(n)
+}
+
+func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
+ ldr := ctxt.loader
+
+ if !ctxt.IsAIX() {
+ switch ctxt.BuildMode {
+ case BuildModeCArchive, BuildModeCShared:
+ s := ldr.Lookup(*flagEntrySymbol, sym.SymVerABI0)
+ if s != 0 {
+ addinitarrdata(ctxt, ldr, s)
+ }
+ }
+ }
+
+ // Define these so that they'll get put into the symbol table.
+ // data.c:/^address will provide the actual values.
+ ctxt.xdefine("runtime.rodata", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.erodata", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.types", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.etypes", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, 0)
+ ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, 0)
+ ctxt.xdefine("runtime.data", sym.SDATA, 0)
+ ctxt.xdefine("runtime.edata", sym.SDATA, 0)
+ ctxt.xdefine("runtime.bss", sym.SBSS, 0)
+ ctxt.xdefine("runtime.ebss", sym.SBSS, 0)
+ ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, 0)
+ ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, 0)
+ ctxt.xdefine("runtime.covctrs", sym.SNOPTRBSS, 0)
+ ctxt.xdefine("runtime.ecovctrs", sym.SNOPTRBSS, 0)
+ ctxt.xdefine("runtime.end", sym.SBSS, 0)
+ ctxt.xdefine("runtime.epclntab", sym.SRODATA, 0)
+ ctxt.xdefine("runtime.esymtab", sym.SRODATA, 0)
+
+ // garbage collection symbols
+ s := ldr.CreateSymForUpdate("runtime.gcdata", 0)
+ s.SetType(sym.SRODATA)
+ s.SetSize(0)
+ ctxt.xdefine("runtime.egcdata", sym.SRODATA, 0)
+
+ s = ldr.CreateSymForUpdate("runtime.gcbss", 0)
+ s.SetType(sym.SRODATA)
+ s.SetSize(0)
+ ctxt.xdefine("runtime.egcbss", sym.SRODATA, 0)
+
+ // pseudo-symbols to mark locations of type, string, and go string data.
+ var symtype, symtyperel loader.Sym
+ if !ctxt.DynlinkingGo() {
+ if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
+ s = ldr.CreateSymForUpdate("type:*", 0)
+ s.SetType(sym.STYPE)
+ s.SetSize(0)
+ s.SetAlign(int32(ctxt.Arch.PtrSize))
+ symtype = s.Sym()
+
+ s = ldr.CreateSymForUpdate("typerel.*", 0)
+ s.SetType(sym.STYPERELRO)
+ s.SetSize(0)
+ s.SetAlign(int32(ctxt.Arch.PtrSize))
+ symtyperel = s.Sym()
+ } else {
+ s = ldr.CreateSymForUpdate("type:*", 0)
+ s.SetType(sym.STYPE)
+ s.SetSize(0)
+ s.SetAlign(int32(ctxt.Arch.PtrSize))
+ symtype = s.Sym()
+ symtyperel = s.Sym()
+ }
+ setCarrierSym(sym.STYPE, symtype)
+ setCarrierSym(sym.STYPERELRO, symtyperel)
+ }
+
+ groupSym := func(name string, t sym.SymKind) loader.Sym {
+ s := ldr.CreateSymForUpdate(name, 0)
+ s.SetType(t)
+ s.SetSize(0)
+ s.SetAlign(int32(ctxt.Arch.PtrSize))
+ s.SetLocal(true)
+ setCarrierSym(t, s.Sym())
+ return s.Sym()
+ }
+ var (
+ symgostring = groupSym("go:string.*", sym.SGOSTRING)
+ symgofunc = groupSym("go:func.*", sym.SGOFUNC)
+ symgcbits = groupSym("runtime.gcbits.*", sym.SGCBITS)
+ )
+
+ symgofuncrel := symgofunc
+ if ctxt.UseRelro() {
+ symgofuncrel = groupSym("go:funcrel.*", sym.SGOFUNCRELRO)
+ }
+
+ symt := ldr.CreateSymForUpdate("runtime.symtab", 0)
+ symt.SetType(sym.SSYMTAB)
+ symt.SetSize(0)
+ symt.SetLocal(true)
+
+ // assign specific types so that they sort together.
+ // within a type they sort by size, so the .* symbols
+ // just defined above will be first.
+ // hide the specific symbols.
+ // Some of these symbol section conditions are duplicated
+ // in cmd/internal/obj.contentHashSection.
+ nsym := loader.Sym(ldr.NSym())
+ symGroupType := make([]sym.SymKind, nsym)
+ for s := loader.Sym(1); s < nsym; s++ {
+ if (!ctxt.IsExternal() && ldr.IsFileLocal(s) && !ldr.IsFromAssembly(s) && ldr.SymPkg(s) != "") || (ctxt.LinkMode == LinkInternal && ldr.SymType(s) == sym.SCOVERAGE_COUNTER) {
+ ldr.SetAttrNotInSymbolTable(s, true)
+ }
+ if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || (ldr.SymType(s) != sym.SRODATA && ldr.SymType(s) != sym.SGOFUNC) {
+ continue
+ }
+
+ name := ldr.SymName(s)
+ switch {
+ case strings.HasPrefix(name, "go:string."):
+ symGroupType[s] = sym.SGOSTRING
+ ldr.SetAttrNotInSymbolTable(s, true)
+ ldr.SetCarrierSym(s, symgostring)
+
+ case strings.HasPrefix(name, "runtime.gcbits."),
+ strings.HasPrefix(name, "type:.gcprog."):
+ symGroupType[s] = sym.SGCBITS
+ ldr.SetAttrNotInSymbolTable(s, true)
+ ldr.SetCarrierSym(s, symgcbits)
+
+ case strings.HasSuffix(name, "·f"):
+ if !ctxt.DynlinkingGo() {
+ ldr.SetAttrNotInSymbolTable(s, true)
+ }
+ if ctxt.UseRelro() {
+ symGroupType[s] = sym.SGOFUNCRELRO
+ if !ctxt.DynlinkingGo() {
+ ldr.SetCarrierSym(s, symgofuncrel)
+ }
+ } else {
+ symGroupType[s] = sym.SGOFUNC
+ ldr.SetCarrierSym(s, symgofunc)
+ }
+
+ case strings.HasPrefix(name, "gcargs."),
+ strings.HasPrefix(name, "gclocals."),
+ strings.HasPrefix(name, "gclocals·"),
+ ldr.SymType(s) == sym.SGOFUNC && s != symgofunc, // inltree, see pcln.go
+ strings.HasSuffix(name, ".opendefer"),
+ strings.HasSuffix(name, ".arginfo0"),
+ strings.HasSuffix(name, ".arginfo1"),
+ strings.HasSuffix(name, ".argliveinfo"),
+ strings.HasSuffix(name, ".wrapinfo"),
+ strings.HasSuffix(name, ".args_stackmap"),
+ strings.HasSuffix(name, ".stkobj"):
+ ldr.SetAttrNotInSymbolTable(s, true)
+ symGroupType[s] = sym.SGOFUNC
+ ldr.SetCarrierSym(s, symgofunc)
+ if ctxt.Debugvlog != 0 {
+ align := ldr.SymAlign(s)
+ liveness += (ldr.SymSize(s) + int64(align) - 1) &^ (int64(align) - 1)
+ }
+
+ // Note: Check for "type:" prefix after checking for .arginfo1 suffix.
+ // That way symbols like "type:.eq.[2]interface {}.arginfo1" that belong
+ // in go:func.* end up there.
+ case strings.HasPrefix(name, "type:"):
+ if !ctxt.DynlinkingGo() {
+ ldr.SetAttrNotInSymbolTable(s, true)
+ }
+ if ctxt.UseRelro() {
+ symGroupType[s] = sym.STYPERELRO
+ if symtyperel != 0 {
+ ldr.SetCarrierSym(s, symtyperel)
+ }
+ } else {
+ symGroupType[s] = sym.STYPE
+ if symtyperel != 0 {
+ ldr.SetCarrierSym(s, symtype)
+ }
+ }
+ }
+ }
+
+ if ctxt.BuildMode == BuildModeShared {
+ abihashgostr := ldr.CreateSymForUpdate("go:link.abihash."+filepath.Base(*flagOutfile), 0)
+ abihashgostr.SetType(sym.SRODATA)
+ hashsym := ldr.LookupOrCreateSym("go:link.abihashbytes", 0)
+ abihashgostr.AddAddr(ctxt.Arch, hashsym)
+ abihashgostr.AddUint(ctxt.Arch, uint64(ldr.SymSize(hashsym)))
+ }
+ if ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() {
+ for _, l := range ctxt.Library {
+ s := ldr.CreateSymForUpdate("go:link.pkghashbytes."+l.Pkg, 0)
+ s.SetType(sym.SRODATA)
+ s.SetSize(int64(len(l.Fingerprint)))
+ s.SetData(l.Fingerprint[:])
+ str := ldr.CreateSymForUpdate("go:link.pkghash."+l.Pkg, 0)
+ str.SetType(sym.SRODATA)
+ str.AddAddr(ctxt.Arch, s.Sym())
+ str.AddUint(ctxt.Arch, uint64(len(l.Fingerprint)))
+ }
+ }
+
+ textsectionmapSym, nsections := textsectionmap(ctxt)
+
+ // Information about the layout of the executable image for the
+ // runtime to use. Any changes here must be matched by changes to
+ // the definition of moduledata in runtime/symtab.go.
+ // This code uses several global variables that are set by pcln.go:pclntab.
+ moduledata := ldr.MakeSymbolUpdater(ctxt.Moduledata)
+ // The pcHeader
+ moduledata.AddAddr(ctxt.Arch, pcln.pcheader)
+ // The function name slice
+ moduledata.AddAddr(ctxt.Arch, pcln.funcnametab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab)))
+ // The cutab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.cutab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab)))
+ // The filetab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.filetab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
+ // The pctab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.pctab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab)))
+ // The pclntab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab)))
+ // The ftab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
+ moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
+ moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
+ // findfunctab
+ moduledata.AddAddr(ctxt.Arch, pcln.findfunctab)
+ // minpc, maxpc
+ moduledata.AddAddr(ctxt.Arch, pcln.firstFunc)
+ moduledata.AddAddrPlus(ctxt.Arch, pcln.lastFunc, ldr.SymSize(pcln.lastFunc))
+ // pointers to specific parts of the module
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.text", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etext", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.noptrdata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.enoptrdata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.data", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.edata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.bss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.ebss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.noptrbss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.enoptrbss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.covctrs", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.ecovctrs", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.end", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.gcdata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.gcbss", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.types", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etypes", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.rodata", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("go:func.*", 0))
+
+ if ctxt.IsAIX() && ctxt.IsExternal() {
+ // Add R_XCOFFREF relocation to prevent ld's garbage collection of
+ // the following symbols. They might not be referenced in the program.
+ addRef := func(name string) {
+ s := ldr.Lookup(name, 0)
+ if s == 0 {
+ return
+ }
+ r, _ := moduledata.AddRel(objabi.R_XCOFFREF)
+ r.SetSym(s)
+ r.SetSiz(uint8(ctxt.Arch.PtrSize))
+ }
+ addRef("runtime.rodata")
+ addRef("runtime.erodata")
+ addRef("runtime.epclntab")
+ // As we use relative addressing for text symbols in functab, it is
+ // important that the offsets we computed stay unchanged by the external
+ // linker, i.e. all symbols in Textp should not be removed.
+ // Most of them are actually referenced (our deadcode pass ensures that),
+ // except go:buildid which is generated late and not used by the program.
+ addRef("go:buildid")
+ }
+
+ // text section information
+ moduledata.AddAddr(ctxt.Arch, textsectionmapSym)
+ moduledata.AddUint(ctxt.Arch, uint64(nsections))
+ moduledata.AddUint(ctxt.Arch, uint64(nsections))
+
+ // The typelinks slice
+ typelinkSym := ldr.Lookup("runtime.typelink", 0)
+ ntypelinks := uint64(ldr.SymSize(typelinkSym)) / 4
+ moduledata.AddAddr(ctxt.Arch, typelinkSym)
+ moduledata.AddUint(ctxt.Arch, ntypelinks)
+ moduledata.AddUint(ctxt.Arch, ntypelinks)
+ // The itablinks slice
+ itablinkSym := ldr.Lookup("runtime.itablink", 0)
+ nitablinks := uint64(ldr.SymSize(itablinkSym)) / uint64(ctxt.Arch.PtrSize)
+ moduledata.AddAddr(ctxt.Arch, itablinkSym)
+ moduledata.AddUint(ctxt.Arch, nitablinks)
+ moduledata.AddUint(ctxt.Arch, nitablinks)
+ // The ptab slice
+ if ptab := ldr.Lookup("go:plugin.tabs", 0); ptab != 0 && ldr.AttrReachable(ptab) {
+ ldr.SetAttrLocal(ptab, true)
+ if ldr.SymType(ptab) != sym.SRODATA {
+ panic(fmt.Sprintf("go:plugin.tabs is %v, not SRODATA", ldr.SymType(ptab)))
+ }
+ nentries := uint64(len(ldr.Data(ptab)) / 8) // sizeof(nameOff) + sizeof(typeOff)
+ moduledata.AddAddr(ctxt.Arch, ptab)
+ moduledata.AddUint(ctxt.Arch, nentries)
+ moduledata.AddUint(ctxt.Arch, nentries)
+ } else {
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0)
+ }
+ if ctxt.BuildMode == BuildModePlugin {
+ addgostring(ctxt, ldr, moduledata, "go:link.thispluginpath", objabi.PathToPrefix(*flagPluginPath))
+
+ pkghashes := ldr.CreateSymForUpdate("go:link.pkghashes", 0)
+ pkghashes.SetLocal(true)
+ pkghashes.SetType(sym.SRODATA)
+
+ for i, l := range ctxt.Library {
+ // pkghashes[i].name
+ addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go:link.pkgname.%d", i), l.Pkg)
+ // pkghashes[i].linktimehash
+ addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go:link.pkglinkhash.%d", i), string(l.Fingerprint[:]))
+ // pkghashes[i].runtimehash
+ hash := ldr.Lookup("go:link.pkghash."+l.Pkg, 0)
+ pkghashes.AddAddr(ctxt.Arch, hash)
+ }
+ moduledata.AddAddr(ctxt.Arch, pkghashes.Sym())
+ moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library)))
+ moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library)))
+ } else {
+ moduledata.AddUint(ctxt.Arch, 0) // pluginpath
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0) // pkghashes slice
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0)
+ }
+ if len(ctxt.Shlibs) > 0 {
+ thismodulename := filepath.Base(*flagOutfile)
+ switch ctxt.BuildMode {
+ case BuildModeExe, BuildModePIE:
+ // When linking an executable, outfile is just "a.out". Make
+ // it something slightly more comprehensible.
+ thismodulename = "the executable"
+ }
+ addgostring(ctxt, ldr, moduledata, "go:link.thismodulename", thismodulename)
+
+ modulehashes := ldr.CreateSymForUpdate("go:link.abihashes", 0)
+ modulehashes.SetLocal(true)
+ modulehashes.SetType(sym.SRODATA)
+
+ for i, shlib := range ctxt.Shlibs {
+ // modulehashes[i].modulename
+ modulename := filepath.Base(shlib.Path)
+ addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go:link.libname.%d", i), modulename)
+
+ // modulehashes[i].linktimehash
+ addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go:link.linkhash.%d", i), string(shlib.Hash))
+
+ // modulehashes[i].runtimehash
+ abihash := ldr.LookupOrCreateSym("go:link.abihash."+modulename, 0)
+ ldr.SetAttrReachable(abihash, true)
+ modulehashes.AddAddr(ctxt.Arch, abihash)
+ }
+
+ moduledata.AddAddr(ctxt.Arch, modulehashes.Sym())
+ moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs)))
+ moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs)))
+ } else {
+ moduledata.AddUint(ctxt.Arch, 0) // modulename
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0) // moduleshashes slice
+ moduledata.AddUint(ctxt.Arch, 0)
+ moduledata.AddUint(ctxt.Arch, 0)
+ }
+
+ hasmain := ctxt.BuildMode == BuildModeExe || ctxt.BuildMode == BuildModePIE
+ if hasmain {
+ moduledata.AddUint8(1)
+ } else {
+ moduledata.AddUint8(0)
+ }
+
+ // The rest of moduledata is zero initialized.
+ // When linking an object that does not contain the runtime we are
+ // creating the moduledata from scratch and it does not have a
+ // compiler-provided size, so read it from the type data.
+ moduledatatype := ldr.Lookup("type:runtime.moduledata", 0)
+ moduledata.SetSize(decodetypeSize(ctxt.Arch, ldr.Data(moduledatatype)))
+ moduledata.Grow(moduledata.Size())
+
+ lastmoduledatap := ldr.CreateSymForUpdate("runtime.lastmoduledatap", 0)
+ if lastmoduledatap.Type() != sym.SDYNIMPORT {
+ lastmoduledatap.SetType(sym.SNOPTRDATA)
+ lastmoduledatap.SetSize(0) // overwrite existing value
+ lastmoduledatap.SetData(nil)
+ lastmoduledatap.AddAddr(ctxt.Arch, moduledata.Sym())
+ }
+ return symGroupType
+}
+
+// CarrierSymByType tracks carrier symbols and their sizes.
+var CarrierSymByType [sym.SXREF]struct {
+ Sym loader.Sym
+ Size int64
+}
+
+func setCarrierSym(typ sym.SymKind, s loader.Sym) {
+ if CarrierSymByType[typ].Sym != 0 {
+ panic(fmt.Sprintf("carrier symbol for type %v already set", typ))
+ }
+ CarrierSymByType[typ].Sym = s
+}
+
+func setCarrierSize(typ sym.SymKind, sz int64) {
+ if CarrierSymByType[typ].Size != 0 {
+ panic(fmt.Sprintf("carrier symbol size for type %v already set", typ))
+ }
+ CarrierSymByType[typ].Size = sz
+}
+
+func isStaticTmp(name string) bool {
+ return strings.Contains(name, "."+obj.StaticNamePref)
+}
+
+// Mangle function name with ABI information.
+func mangleABIName(ctxt *Link, ldr *loader.Loader, x loader.Sym, name string) string {
+ // For functions with ABI wrappers, we have to make sure that we
+ // don't wind up with two symbol table entries with the same
+ // name (since this will generated an error from the external
+ // linker). If we have wrappers, keep the ABIInternal name
+ // unmangled since we want cross-load-module calls to target
+ // ABIInternal, and rename other symbols.
+ //
+ // TODO: avoid the ldr.Lookup calls below by instead using an aux
+ // sym or marker relocation to associate the wrapper with the
+ // wrapped function.
+ if !buildcfg.Experiment.RegabiWrappers {
+ return name
+ }
+
+ if ldr.SymType(x) == sym.STEXT && ldr.SymVersion(x) != sym.SymVerABIInternal && ldr.SymVersion(x) < sym.SymVerStatic {
+ if s2 := ldr.Lookup(name, sym.SymVerABIInternal); s2 != 0 && ldr.SymType(s2) == sym.STEXT {
+ name = fmt.Sprintf("%s.abi%d", name, ldr.SymVersion(x))
+ }
+ }
+
+ // When loading a shared library, if a symbol has only one ABI,
+ // and the name is not mangled, we don't know what ABI it is.
+ // So we always mangle ABIInternal function name in shared linkage,
+ // except symbols that are exported to C. Type symbols are always
+ // ABIInternal so they are not mangled.
+ if ctxt.IsShared() {
+ if ldr.SymType(x) == sym.STEXT && ldr.SymVersion(x) == sym.SymVerABIInternal && !ldr.AttrCgoExport(x) && !strings.HasPrefix(name, "type:") {
+ name = fmt.Sprintf("%s.abiinternal", name)
+ }
+ }
+
+ return name
+}
diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go
new file mode 100644
index 0000000..d0ce99f
--- /dev/null
+++ b/src/cmd/link/internal/ld/target.go
@@ -0,0 +1,206 @@
+// Copyright 2020 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.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "encoding/binary"
+)
+
+// Target holds the configuration we're building for.
+type Target struct {
+ Arch *sys.Arch
+
+ HeadType objabi.HeadType
+
+ LinkMode LinkMode
+ BuildMode BuildMode
+
+ linkShared bool
+ canUsePlugins bool
+ IsELF bool
+}
+
+//
+// Target type functions
+//
+
+func (t *Target) IsExe() bool {
+ return t.BuildMode == BuildModeExe
+}
+
+func (t *Target) IsShared() bool {
+ return t.BuildMode == BuildModeShared
+}
+
+func (t *Target) IsPlugin() bool {
+ return t.BuildMode == BuildModePlugin
+}
+
+func (t *Target) IsInternal() bool {
+ return t.LinkMode == LinkInternal
+}
+
+func (t *Target) IsExternal() bool {
+ return t.LinkMode == LinkExternal
+}
+
+func (t *Target) IsPIE() bool {
+ return t.BuildMode == BuildModePIE
+}
+
+func (t *Target) IsSharedGoLink() bool {
+ return t.linkShared
+}
+
+func (t *Target) CanUsePlugins() bool {
+ return t.canUsePlugins
+}
+
+func (t *Target) IsElf() bool {
+ t.mustSetHeadType()
+ return t.IsELF
+}
+
+func (t *Target) IsDynlinkingGo() bool {
+ return t.IsShared() || t.IsSharedGoLink() || t.IsPlugin() || t.CanUsePlugins()
+}
+
+// UseRelro reports whether to make use of "read only relocations" aka
+// relro.
+func (t *Target) UseRelro() bool {
+ switch t.BuildMode {
+ case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin:
+ return t.IsELF || t.HeadType == objabi.Haix || t.HeadType == objabi.Hdarwin
+ default:
+ if t.HeadType == objabi.Hdarwin && t.IsARM64() {
+ // On darwin/ARM64, everything is PIE.
+ return true
+ }
+ return t.linkShared || (t.HeadType == objabi.Haix && t.LinkMode == LinkExternal)
+ }
+}
+
+//
+// Processor functions
+//
+
+func (t *Target) Is386() bool {
+ return t.Arch.Family == sys.I386
+}
+
+func (t *Target) IsARM() bool {
+ return t.Arch.Family == sys.ARM
+}
+
+func (t *Target) IsARM64() bool {
+ return t.Arch.Family == sys.ARM64
+}
+
+func (t *Target) IsAMD64() bool {
+ return t.Arch.Family == sys.AMD64
+}
+
+func (t *Target) IsMIPS() bool {
+ return t.Arch.Family == sys.MIPS
+}
+
+func (t *Target) IsMIPS64() bool {
+ return t.Arch.Family == sys.MIPS64
+}
+
+func (t *Target) IsLOONG64() bool {
+ return t.Arch.Family == sys.Loong64
+}
+
+func (t *Target) IsPPC64() bool {
+ return t.Arch.Family == sys.PPC64
+}
+
+func (t *Target) IsRISCV64() bool {
+ return t.Arch.Family == sys.RISCV64
+}
+
+func (t *Target) IsS390X() bool {
+ return t.Arch.Family == sys.S390X
+}
+
+func (t *Target) IsWasm() bool {
+ return t.Arch.Family == sys.Wasm
+}
+
+//
+// OS Functions
+//
+
+func (t *Target) IsLinux() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hlinux
+}
+
+func (t *Target) IsDarwin() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hdarwin
+}
+
+func (t *Target) IsWindows() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hwindows
+}
+
+func (t *Target) IsPlan9() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hplan9
+}
+
+func (t *Target) IsAIX() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Haix
+}
+
+func (t *Target) IsSolaris() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hsolaris
+}
+
+func (t *Target) IsNetbsd() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hnetbsd
+}
+
+func (t *Target) IsOpenbsd() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hopenbsd
+}
+
+func (t *Target) IsFreebsd() bool {
+ t.mustSetHeadType()
+ return t.HeadType == objabi.Hfreebsd
+}
+
+func (t *Target) mustSetHeadType() {
+ if t.HeadType == objabi.Hunknown {
+ panic("HeadType is not set")
+ }
+}
+
+//
+// MISC
+//
+
+func (t *Target) IsBigEndian() bool {
+ return t.Arch.ByteOrder == binary.BigEndian
+}
+
+func (t *Target) UsesLibc() bool {
+ t.mustSetHeadType()
+ switch t.HeadType {
+ case objabi.Haix, objabi.Hdarwin, objabi.Hopenbsd, objabi.Hsolaris, objabi.Hwindows:
+ // platforms where we use libc for syscalls.
+ return true
+ }
+ return false
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
new file mode 100644
index 0000000..32a24cf
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
@@ -0,0 +1,30 @@
+// Copyright 2020 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.
+
+// Test that a method of a reachable type is not necessarily
+// live even if it matches an interface method, as long as
+// the type is never converted to an interface.
+
+package main
+
+type I interface{ M() }
+
+type T int
+
+func (T) M() { println("XXX") }
+
+var p *T
+var e interface{}
+
+func main() {
+ p = new(T) // used T, but never converted to interface in any reachable code
+ e.(I).M() // used I and I.M
+}
+
+func Unused() { // convert T to interface, but this function is not reachable
+ var i I = T(0)
+ i.M()
+}
+
+var Unused2 interface{} = T(1) // convert T to interface, in an unreachable global initializer
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go
new file mode 100644
index 0000000..48ba55d
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go
@@ -0,0 +1,22 @@
+// Copyright 2020 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.
+
+// Test that a method *is* live if it matches an interface
+// method and the type is "indirectly" converted to an
+// interface through reflection.
+
+package main
+
+import "reflect"
+
+type I interface{ M() }
+
+type T int
+
+func (T) M() { println("XXX") }
+
+func main() {
+ e := reflect.ValueOf([]T{1}).Index(0).Interface()
+ e.(I).M()
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go
new file mode 100644
index 0000000..37c8937
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go
@@ -0,0 +1,29 @@
+// Copyright 2020 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.
+
+// Like ifacemethod2.go, this tests that a method *is* live
+// if the type is "indirectly" converted to an interface
+// using reflection with a method descriptor as intermediate.
+
+package main
+
+import "reflect"
+
+type S int
+
+func (s S) M() { println("S.M") }
+
+type I interface{ M() }
+
+type T float64
+
+func (t T) F(s S) {}
+
+func main() {
+ var t T
+ ft := reflect.TypeOf(t).Method(0).Type
+ at := ft.In(1)
+ v := reflect.New(at).Elem()
+ v.Interface().(I).M()
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go
new file mode 100644
index 0000000..4af47ad
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go
@@ -0,0 +1,25 @@
+// Copyright 2020 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.
+
+// Test that a live type's method is not live even if
+// it matches an interface method, as long as the interface
+// method is not used.
+
+package main
+
+type T int
+
+//go:noinline
+func (T) M() {}
+
+type I interface{ M() }
+
+var p *T
+var pp *I
+
+func main() {
+ p = new(T) // use type T
+ pp = new(I) // use type I
+ *pp = *p // convert T to I, build itab
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go b/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go
new file mode 100644
index 0000000..af95e46
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go
@@ -0,0 +1,24 @@
+// Copyright 2020 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.
+
+// This example uses reflect.Value.Call, but not
+// reflect.{Value,Type}.Method. This should not
+// need to bring all methods live.
+
+package main
+
+import "reflect"
+
+func f() { println("call") }
+
+type T int
+
+func (T) M() {}
+
+func main() {
+ v := reflect.ValueOf(f)
+ v.Call(nil)
+ i := interface{}(T(1))
+ println(i)
+}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go b/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go
new file mode 100644
index 0000000..8246093
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go
@@ -0,0 +1,16 @@
+// Copyright 2020 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.
+
+// Test that a live variable doesn't bring its type
+// descriptor live.
+
+package main
+
+type T [10]string
+
+var t T
+
+func main() {
+ println(t[8])
+}
diff --git a/src/cmd/link/internal/ld/testdata/httptest/main/main.go b/src/cmd/link/internal/ld/testdata/httptest/main/main.go
new file mode 100644
index 0000000..1bce301
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/httptest/main/main.go
@@ -0,0 +1,22 @@
+// A small test program that uses the net/http package. There is
+// nothing special about net/http here, this is just a convenient way
+// to pull in a lot of code.
+
+package main
+
+import (
+ "net/http"
+ "net/http/httptest"
+)
+
+type statusHandler int
+
+func (h *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(int(*h))
+}
+
+func main() {
+ status := statusHandler(http.StatusNotFound)
+ s := httptest.NewServer(&status)
+ defer s.Close()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue10978/main.go b/src/cmd/link/internal/ld/testdata/issue10978/main.go
new file mode 100644
index 0000000..5e8c097
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue10978/main.go
@@ -0,0 +1,27 @@
+// 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.
+
+package main
+
+func undefined()
+
+func defined1() int {
+ // To check multiple errors for a single symbol,
+ // reference undefined more than once.
+ undefined()
+ undefined()
+ return 0
+}
+
+func defined2() {
+ undefined()
+ undefined()
+}
+
+func init() {
+ _ = defined1()
+ defined2()
+}
+
+// The "main" function remains undeclared.
diff --git a/src/cmd/link/internal/ld/testdata/issue10978/main.s b/src/cmd/link/internal/ld/testdata/issue10978/main.s
new file mode 100644
index 0000000..1d00e76
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue10978/main.s
@@ -0,0 +1 @@
+// This file is needed to make "go build" work for package with external functions.
diff --git a/src/cmd/link/internal/ld/testdata/issue25459/a/a.go b/src/cmd/link/internal/ld/testdata/issue25459/a/a.go
new file mode 100644
index 0000000..6032d76
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue25459/a/a.go
@@ -0,0 +1,27 @@
+package a
+
+const Always = true
+
+var Count int
+
+type FuncReturningInt func() int
+
+var PointerToConstIf FuncReturningInt
+
+func ConstIf() int {
+ if Always {
+ return 1
+ }
+ var imdead [4]int
+ imdead[Count] = 1
+ return imdead[0]
+}
+
+func CallConstIf() int {
+ Count += 3
+ return ConstIf()
+}
+
+func Another() {
+ defer func() { PointerToConstIf = ConstIf; Count += 1 }()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue25459/main/main.go b/src/cmd/link/internal/ld/testdata/issue25459/main/main.go
new file mode 100644
index 0000000..7b5796d
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue25459/main/main.go
@@ -0,0 +1,10 @@
+package main
+
+import "cmd/link/internal/ld/testdata/issue25459/a"
+
+var Glob int
+
+func main() {
+ a.Another()
+ Glob += a.ConstIf() + a.CallConstIf()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go b/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go
new file mode 100644
index 0000000..ca57749
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go
@@ -0,0 +1,16 @@
+package b
+
+var q int
+
+func Top(x int) int {
+ q += 1
+ if q != x {
+ return 3
+ }
+ return 4
+}
+
+func OOO(x int) int {
+ defer func() { q += x & 7 }()
+ return Top(x + 1)
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue26237/main/main.go b/src/cmd/link/internal/ld/testdata/issue26237/main/main.go
new file mode 100644
index 0000000..fdb1223
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue26237/main/main.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+ "fmt"
+
+ b "cmd/link/internal/ld/testdata/issue26237/b.dir"
+)
+
+var skyx int
+
+func main() {
+ skyx += b.OOO(skyx)
+ if b.Top(1) == 99 {
+ fmt.Printf("Beware the Jabberwock, my son!\n")
+ }
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m b/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m
new file mode 100644
index 0000000..9462788
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m
@@ -0,0 +1,16 @@
+// Copyright 2019 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.
+
+#import <Foundation/Foundation.h>
+#import <AppKit/NSAppearance.h>
+
+BOOL function(void) {
+#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED > 101300)
+ NSAppearance *darkAppearance;
+ if (@available(macOS 10.14, *)) {
+ darkAppearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
+ }
+#endif
+ return NO;
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go b/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go
new file mode 100644
index 0000000..514b9b9
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go
@@ -0,0 +1,19 @@
+// Copyright 2019 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.
+
+package lib
+
+/*
+#cgo darwin CFLAGS: -D__MAC_OS_X_VERSION_MAX_ALLOWED=101450
+#cgo darwin LDFLAGS: -framework Foundation -framework AppKit
+#include "stdlib.h"
+int function(void);
+*/
+import "C"
+import "fmt"
+
+func DoC() {
+ C.function()
+ fmt.Println("called c function")
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue32233/main/main.go b/src/cmd/link/internal/ld/testdata/issue32233/main/main.go
new file mode 100644
index 0000000..ed0838a
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue32233/main/main.go
@@ -0,0 +1,11 @@
+// Copyright 2019 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.
+
+package main
+
+import "cmd/link/internal/ld/testdata/issue32233/lib"
+
+func main() {
+ lib.DoC()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue38192/main.go b/src/cmd/link/internal/ld/testdata/issue38192/main.go
new file mode 100644
index 0000000..3b7df60
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue38192/main.go
@@ -0,0 +1,11 @@
+// Copyright 2020 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.
+
+package main
+
+func singleInstruction()
+
+func main() {
+ singleInstruction()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue38192/oneline.s b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s
new file mode 100644
index 0000000..f5290d7
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s
@@ -0,0 +1,8 @@
+// Copyright 2020 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.
+
+#include "textflag.h"
+
+TEXT ·singleInstruction(SB),NOSPLIT,$0
+ RET
diff --git a/src/cmd/link/internal/ld/testdata/issue39256/x.go b/src/cmd/link/internal/ld/testdata/issue39256/x.go
new file mode 100644
index 0000000..97bc1cc
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue39256/x.go
@@ -0,0 +1,22 @@
+// Copyright 2020 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.
+
+package main
+
+import (
+ _ "unsafe"
+)
+
+//go:cgo_import_dynamic libc_getpid getpid "libc.so"
+//go:cgo_import_dynamic libc_kill kill "libc.so"
+//go:cgo_import_dynamic libc_close close "libc.so"
+//go:cgo_import_dynamic libc_open open "libc.so"
+
+//go:cgo_import_dynamic _ _ "libc.so"
+
+func trampoline()
+
+func main() {
+ trampoline()
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue39256/x.s b/src/cmd/link/internal/ld/testdata/issue39256/x.s
new file mode 100644
index 0000000..41a54b2
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue39256/x.s
@@ -0,0 +1,10 @@
+// Copyright 2020 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.
+
+TEXT ·trampoline(SB),0,$0
+ CALL libc_getpid(SB)
+ CALL libc_kill(SB)
+ CALL libc_open(SB)
+ CALL libc_close(SB)
+ RET
diff --git a/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go
new file mode 100644
index 0000000..76e0ea1
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go
@@ -0,0 +1,15 @@
+// Copyright 2020 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.
+
+package main
+
+var G int
+
+func main() {
+ if G != 101 {
+ println("not 101")
+ } else {
+ println("well now that's interesting")
+ }
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue42484/main.go b/src/cmd/link/internal/ld/testdata/issue42484/main.go
new file mode 100644
index 0000000..60fc110
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue42484/main.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ a := 0
+ a++
+ b := 0
+ f1(a, b)
+}
+
+func f1(a, b int) {
+ fmt.Printf("%d %d\n", a, b)
+}
diff --git a/src/cmd/link/internal/ld/testdata/stackcheck/main.go b/src/cmd/link/internal/ld/testdata/stackcheck/main.go
new file mode 100644
index 0000000..b708cc5
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/stackcheck/main.go
@@ -0,0 +1,20 @@
+// Copyright 2022 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.
+
+package main
+
+func main() { asmMain() }
+
+func asmMain()
+
+func startSelf()
+
+func startChain()
+func chain0()
+func chain1()
+func chain2()
+func chainEnd()
+
+func startRec()
+func startRec0()
diff --git a/src/cmd/link/internal/ld/testdata/stackcheck/main.s b/src/cmd/link/internal/ld/testdata/stackcheck/main.s
new file mode 100644
index 0000000..10f6a3f
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/stackcheck/main.s
@@ -0,0 +1,40 @@
+// Copyright 2022 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.
+
+#define NOSPLIT 7
+
+TEXT ·asmMain(SB),0,$0-0
+ CALL ·startSelf(SB)
+ CALL ·startChain(SB)
+ CALL ·startRec(SB)
+ RET
+
+// Test reporting of basic over-the-limit
+TEXT ·startSelf(SB),NOSPLIT,$1000-0
+ RET
+
+// Test reporting of multiple over-the-limit chains
+TEXT ·startChain(SB),NOSPLIT,$16-0
+ CALL ·chain0(SB)
+ CALL ·chain1(SB)
+ CALL ·chain2(SB)
+ RET
+TEXT ·chain0(SB),NOSPLIT,$32-0
+ CALL ·chainEnd(SB)
+ RET
+TEXT ·chain1(SB),NOSPLIT,$48-0 // Doesn't go over
+ RET
+TEXT ·chain2(SB),NOSPLIT,$64-0
+ CALL ·chainEnd(SB)
+ RET
+TEXT ·chainEnd(SB),NOSPLIT,$1000-0 // Should be reported twice
+ RET
+
+// Test reporting of rootless recursion
+TEXT ·startRec(SB),NOSPLIT,$0-0
+ CALL ·startRec0(SB)
+ RET
+TEXT ·startRec0(SB),NOSPLIT,$0-0
+ CALL ·startRec(SB)
+ RET
diff --git a/src/cmd/link/internal/ld/typelink.go b/src/cmd/link/internal/ld/typelink.go
new file mode 100644
index 0000000..5eca6e0
--- /dev/null
+++ b/src/cmd/link/internal/ld/typelink.go
@@ -0,0 +1,72 @@
+// Copyright 2016 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.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "sort"
+)
+
+type byTypeStr []typelinkSortKey
+
+type typelinkSortKey struct {
+ TypeStr string
+ Type loader.Sym
+}
+
+func (s byTypeStr) Less(i, j int) bool { return s[i].TypeStr < s[j].TypeStr }
+func (s byTypeStr) Len() int { return len(s) }
+func (s byTypeStr) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// typelink generates the typelink table which is used by reflect.typelinks().
+// Types that should be added to the typelinks table are marked with the
+// MakeTypelink attribute by the compiler.
+func (ctxt *Link) typelink() {
+ ldr := ctxt.loader
+ typelinks := byTypeStr{}
+ var itabs []loader.Sym
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.IsTypelink(s) {
+ typelinks = append(typelinks, typelinkSortKey{decodetypeStr(ldr, ctxt.Arch, s), s})
+ } else if ldr.IsItab(s) {
+ itabs = append(itabs, s)
+ }
+ }
+ sort.Sort(typelinks)
+
+ tl := ldr.CreateSymForUpdate("runtime.typelink", 0)
+ tl.SetType(sym.STYPELINK)
+ ldr.SetAttrLocal(tl.Sym(), true)
+ tl.SetSize(int64(4 * len(typelinks)))
+ tl.Grow(tl.Size())
+ relocs := tl.AddRelocs(len(typelinks))
+ for i, s := range typelinks {
+ r := relocs.At(i)
+ r.SetSym(s.Type)
+ r.SetOff(int32(i * 4))
+ r.SetSiz(4)
+ r.SetType(objabi.R_ADDROFF)
+ }
+
+ ptrsize := ctxt.Arch.PtrSize
+ il := ldr.CreateSymForUpdate("runtime.itablink", 0)
+ il.SetType(sym.SITABLINK)
+ ldr.SetAttrLocal(il.Sym(), true)
+ il.SetSize(int64(ptrsize * len(itabs)))
+ il.Grow(il.Size())
+ relocs = il.AddRelocs(len(itabs))
+ for i, s := range itabs {
+ r := relocs.At(i)
+ r.SetSym(s)
+ r.SetOff(int32(i * ptrsize))
+ r.SetSiz(uint8(ptrsize))
+ r.SetType(objabi.R_ADDR)
+ }
+}
diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go
new file mode 100644
index 0000000..779f498
--- /dev/null
+++ b/src/cmd/link/internal/ld/util.go
@@ -0,0 +1,114 @@
+// Copyright 2015 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.
+
+package ld
+
+import (
+ "cmd/link/internal/loader"
+ "encoding/binary"
+ "fmt"
+ "os"
+)
+
+var atExitFuncs []func()
+
+func AtExit(f func()) {
+ atExitFuncs = append(atExitFuncs, f)
+}
+
+// runAtExitFuncs runs the queued set of AtExit functions.
+func runAtExitFuncs() {
+ for i := len(atExitFuncs) - 1; i >= 0; i-- {
+ atExitFuncs[i]()
+ }
+ atExitFuncs = nil
+}
+
+// Exit exits with code after executing all atExitFuncs.
+func Exit(code int) {
+ runAtExitFuncs()
+ os.Exit(code)
+}
+
+// Exitf logs an error message then calls Exit(2).
+func Exitf(format string, a ...interface{}) {
+ fmt.Fprintf(os.Stderr, os.Args[0]+": "+format+"\n", a...)
+ nerrors++
+ Exit(2)
+}
+
+// afterErrorAction updates 'nerrors' on error and invokes exit or
+// panics in the proper circumstances.
+func afterErrorAction() {
+ nerrors++
+ if *flagH {
+ panic("error")
+ }
+ if nerrors > 20 {
+ Exitf("too many errors")
+ }
+}
+
+// Errorf logs an error message.
+//
+// If more than 20 errors have been printed, exit with an error.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+//
+// TODO: remove. Use ctxt.Errorf instead.
+// All remaining calls use nil as first arg.
+func Errorf(dummy *int, format string, args ...interface{}) {
+ format += "\n"
+ fmt.Fprintf(os.Stderr, format, args...)
+ afterErrorAction()
+}
+
+// Errorf method logs an error message.
+//
+// If more than 20 errors have been printed, exit with an error.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+func (ctxt *Link) Errorf(s loader.Sym, format string, args ...interface{}) {
+ if ctxt.loader != nil {
+ ctxt.loader.Errorf(s, format, args...)
+ return
+ }
+ // Note: this is not expected to happen very often.
+ format = fmt.Sprintf("sym %d: %s", s, format)
+ format += "\n"
+ fmt.Fprintf(os.Stderr, format, args...)
+ afterErrorAction()
+}
+
+func artrim(x []byte) string {
+ i := 0
+ j := len(x)
+ for i < len(x) && x[i] == ' ' {
+ i++
+ }
+ for j > i && x[j-1] == ' ' {
+ j--
+ }
+ return string(x[i:j])
+}
+
+func stringtouint32(x []uint32, s string) {
+ for i := 0; len(s) > 0; i++ {
+ var buf [4]byte
+ s = s[copy(buf[:], s):]
+ x[i] = binary.LittleEndian.Uint32(buf[:])
+ }
+}
+
+// contains reports whether v is in s.
+func contains(s []string, v string) bool {
+ for _, x := range s {
+ if x == v {
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go
new file mode 100644
index 0000000..1265c45
--- /dev/null
+++ b/src/cmd/link/internal/ld/xcoff.go
@@ -0,0 +1,1815 @@
+// 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.
+
+package ld
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "math/bits"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+)
+
+// This file handles all algorithms related to XCOFF files generation.
+// Most of them are adaptations of the ones in cmd/link/internal/pe.go
+// as PE and XCOFF are based on COFF files.
+// XCOFF files generated are 64 bits.
+
+const (
+ // Total amount of space to reserve at the start of the file
+ // for File Header, Auxiliary Header, and Section Headers.
+ // May waste some.
+ XCOFFHDRRESERVE = FILHSZ_64 + AOUTHSZ_EXEC64 + SCNHSZ_64*23
+
+ // base on dump -o, then rounded from 32B to 64B to
+ // match worst case elf text section alignment on ppc64.
+ XCOFFSECTALIGN int64 = 64
+
+ // XCOFF binaries should normally have all its sections position-independent.
+ // However, this is not yet possible for .text because of some R_ADDR relocations
+ // inside RODATA symbols.
+ // .data and .bss are position-independent so their address start inside a unreachable
+ // segment during execution to force segfault if something is wrong.
+ XCOFFTEXTBASE = 0x100000000 // Start of text address
+ XCOFFDATABASE = 0x200000000 // Start of data address
+)
+
+// File Header
+type XcoffFileHdr64 struct {
+ Fmagic uint16 // Target machine
+ Fnscns uint16 // Number of sections
+ Ftimedat int32 // Time and date of file creation
+ Fsymptr uint64 // Byte offset to symbol table start
+ Fopthdr uint16 // Number of bytes in optional header
+ Fflags uint16 // Flags
+ Fnsyms int32 // Number of entries in symbol table
+}
+
+const (
+ U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF
+)
+
+// Flags that describe the type of the object file.
+const (
+ F_RELFLG = 0x0001
+ F_EXEC = 0x0002
+ F_LNNO = 0x0004
+ F_FDPR_PROF = 0x0010
+ F_FDPR_OPTI = 0x0020
+ F_DSA = 0x0040
+ F_VARPG = 0x0100
+ F_DYNLOAD = 0x1000
+ F_SHROBJ = 0x2000
+ F_LOADONLY = 0x4000
+)
+
+// Auxiliary Header
+type XcoffAoutHdr64 struct {
+ Omagic int16 // Flags - Ignored If Vstamp Is 1
+ Ovstamp int16 // Version
+ Odebugger uint32 // Reserved For Debugger
+ Otextstart uint64 // Virtual Address Of Text
+ Odatastart uint64 // Virtual Address Of Data
+ Otoc uint64 // Toc Address
+ Osnentry int16 // Section Number For Entry Point
+ Osntext int16 // Section Number For Text
+ Osndata int16 // Section Number For Data
+ Osntoc int16 // Section Number For Toc
+ Osnloader int16 // Section Number For Loader
+ Osnbss int16 // Section Number For Bss
+ Oalgntext int16 // Max Text Alignment
+ Oalgndata int16 // Max Data Alignment
+ Omodtype [2]byte // Module Type Field
+ Ocpuflag uint8 // Bit Flags - Cputypes Of Objects
+ Ocputype uint8 // Reserved for CPU type
+ Otextpsize uint8 // Requested text page size
+ Odatapsize uint8 // Requested data page size
+ Ostackpsize uint8 // Requested stack page size
+ Oflags uint8 // Flags And TLS Alignment
+ Otsize uint64 // Text Size In Bytes
+ Odsize uint64 // Data Size In Bytes
+ Obsize uint64 // Bss Size In Bytes
+ Oentry uint64 // Entry Point Address
+ Omaxstack uint64 // Max Stack Size Allowed
+ Omaxdata uint64 // Max Data Size Allowed
+ Osntdata int16 // Section Number For Tdata Section
+ Osntbss int16 // Section Number For Tbss Section
+ Ox64flags uint16 // Additional Flags For 64-Bit Objects
+ Oresv3a int16 // Reserved
+ Oresv3 [2]int32 // Reserved
+}
+
+// Section Header
+type XcoffScnHdr64 struct {
+ Sname [8]byte // Section Name
+ Spaddr uint64 // Physical Address
+ Svaddr uint64 // Virtual Address
+ Ssize uint64 // Section Size
+ Sscnptr uint64 // File Offset To Raw Data
+ Srelptr uint64 // File Offset To Relocation
+ Slnnoptr uint64 // File Offset To Line Numbers
+ Snreloc uint32 // Number Of Relocation Entries
+ Snlnno uint32 // Number Of Line Number Entries
+ Sflags uint32 // flags
+}
+
+// Flags defining the section type.
+const (
+ STYP_DWARF = 0x0010
+ STYP_TEXT = 0x0020
+ STYP_DATA = 0x0040
+ STYP_BSS = 0x0080
+ STYP_EXCEPT = 0x0100
+ STYP_INFO = 0x0200
+ STYP_TDATA = 0x0400
+ STYP_TBSS = 0x0800
+ STYP_LOADER = 0x1000
+ STYP_DEBUG = 0x2000
+ STYP_TYPCHK = 0x4000
+ STYP_OVRFLO = 0x8000
+)
+const (
+ SSUBTYP_DWINFO = 0x10000 // DWARF info section
+ SSUBTYP_DWLINE = 0x20000 // DWARF line-number section
+ SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section
+ SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section
+ SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section
+ SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section
+ SSUBTYP_DWSTR = 0x70000 // DWARF strings section
+ SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section
+ SSUBTYP_DWLOC = 0x90000 // DWARF location lists section
+ SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section
+ SSUBTYP_DWMAC = 0xB0000 // DWARF macros section
+)
+
+// Headers size
+const (
+ FILHSZ_32 = 20
+ FILHSZ_64 = 24
+ AOUTHSZ_EXEC32 = 72
+ AOUTHSZ_EXEC64 = 120
+ SCNHSZ_32 = 40
+ SCNHSZ_64 = 72
+ LDHDRSZ_32 = 32
+ LDHDRSZ_64 = 56
+ LDSYMSZ_64 = 24
+ RELSZ_64 = 14
+)
+
+// Type representing all XCOFF symbols.
+type xcoffSym interface {
+}
+
+// Symbol Table Entry
+type XcoffSymEnt64 struct {
+ Nvalue uint64 // Symbol value
+ Noffset uint32 // Offset of the name in string table or .debug section
+ Nscnum int16 // Section number of symbol
+ Ntype uint16 // Basic and derived type specification
+ Nsclass uint8 // Storage class of symbol
+ Nnumaux int8 // Number of auxiliary entries
+}
+
+const SYMESZ = 18
+
+const (
+ // Nscnum
+ N_DEBUG = -2
+ N_ABS = -1
+ N_UNDEF = 0
+
+ //Ntype
+ SYM_V_INTERNAL = 0x1000
+ SYM_V_HIDDEN = 0x2000
+ SYM_V_PROTECTED = 0x3000
+ SYM_V_EXPORTED = 0x4000
+ SYM_TYPE_FUNC = 0x0020 // is function
+)
+
+// Storage Class.
+const (
+ C_NULL = 0 // Symbol table entry marked for deletion
+ C_EXT = 2 // External symbol
+ C_STAT = 3 // Static symbol
+ C_BLOCK = 100 // Beginning or end of inner block
+ C_FCN = 101 // Beginning or end of function
+ C_FILE = 103 // Source file name and compiler information
+ C_HIDEXT = 107 // Unnamed external symbol
+ C_BINCL = 108 // Beginning of include file
+ C_EINCL = 109 // End of include file
+ C_WEAKEXT = 111 // Weak external symbol
+ C_DWARF = 112 // DWARF symbol
+ C_GSYM = 128 // Global variable
+ C_LSYM = 129 // Automatic variable allocated on stack
+ C_PSYM = 130 // Argument to subroutine allocated on stack
+ C_RSYM = 131 // Register variable
+ C_RPSYM = 132 // Argument to function or procedure stored in register
+ C_STSYM = 133 // Statically allocated symbol
+ C_BCOMM = 135 // Beginning of common block
+ C_ECOML = 136 // Local member of common block
+ C_ECOMM = 137 // End of common block
+ C_DECL = 140 // Declaration of object
+ C_ENTRY = 141 // Alternate entry
+ C_FUN = 142 // Function or procedure
+ C_BSTAT = 143 // Beginning of static block
+ C_ESTAT = 144 // End of static block
+ C_GTLS = 145 // Global thread-local variable
+ C_STTLS = 146 // Static thread-local variable
+)
+
+// File Auxiliary Entry
+type XcoffAuxFile64 struct {
+ Xzeroes uint32 // The name is always in the string table
+ Xoffset uint32 // Offset in the string table
+ X_pad1 [6]byte
+ Xftype uint8 // Source file string type
+ X_pad2 [2]byte
+ Xauxtype uint8 // Type of auxiliary entry
+}
+
+// Function Auxiliary Entry
+type XcoffAuxFcn64 struct {
+ Xlnnoptr uint64 // File pointer to line number
+ Xfsize uint32 // Size of function in bytes
+ Xendndx uint32 // Symbol table index of next entry
+ Xpad uint8 // Unused
+ Xauxtype uint8 // Type of auxiliary entry
+}
+
+// csect Auxiliary Entry.
+type XcoffAuxCSect64 struct {
+ Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index
+ Xparmhash uint32 // Offset of parameter type-check string
+ Xsnhash uint16 // .typchk section number
+ Xsmtyp uint8 // Symbol alignment and type
+ Xsmclas uint8 // Storage-mapping class
+ Xscnlenhi uint32 // Upper 4 bytes of length or symbol table index
+ Xpad uint8 // Unused
+ Xauxtype uint8 // Type of auxiliary entry
+}
+
+// DWARF Auxiliary Entry
+type XcoffAuxDWARF64 struct {
+ Xscnlen uint64 // Length of this symbol section
+ X_pad [9]byte
+ Xauxtype uint8 // Type of auxiliary entry
+}
+
+// Auxiliary type
+const (
+ _AUX_EXCEPT = 255
+ _AUX_FCN = 254
+ _AUX_SYM = 253
+ _AUX_FILE = 252
+ _AUX_CSECT = 251
+ _AUX_SECT = 250
+)
+
+// Xftype field
+const (
+ XFT_FN = 0 // Source File Name
+ XFT_CT = 1 // Compile Time Stamp
+ XFT_CV = 2 // Compiler Version Number
+ XFT_CD = 128 // Compiler Defined Information/
+
+)
+
+// Symbol type field.
+const (
+ XTY_ER = 0 // External reference
+ XTY_SD = 1 // Section definition
+ XTY_LD = 2 // Label definition
+ XTY_CM = 3 // Common csect definition
+ XTY_WK = 0x8 // Weak symbol
+ XTY_EXP = 0x10 // Exported symbol
+ XTY_ENT = 0x20 // Entry point symbol
+ XTY_IMP = 0x40 // Imported symbol
+)
+
+// Storage-mapping class.
+const (
+ XMC_PR = 0 // Program code
+ XMC_RO = 1 // Read-only constant
+ XMC_DB = 2 // Debug dictionary table
+ XMC_TC = 3 // TOC entry
+ XMC_UA = 4 // Unclassified
+ XMC_RW = 5 // Read/Write data
+ XMC_GL = 6 // Global linkage
+ XMC_XO = 7 // Extended operation
+ XMC_SV = 8 // 32-bit supervisor call descriptor
+ XMC_BS = 9 // BSS class
+ XMC_DS = 10 // Function descriptor
+ XMC_UC = 11 // Unnamed FORTRAN common
+ XMC_TC0 = 15 // TOC anchor
+ XMC_TD = 16 // Scalar data entry in the TOC
+ XMC_SV64 = 17 // 64-bit supervisor call descriptor
+ XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit
+ XMC_TL = 20 // Read/Write thread-local data
+ XMC_UL = 21 // Read/Write thread-local data (.tbss)
+ XMC_TE = 22 // TOC entry
+)
+
+// Loader Header
+type XcoffLdHdr64 struct {
+ Lversion int32 // Loader section version number
+ Lnsyms int32 // Number of symbol table entries
+ Lnreloc int32 // Number of relocation table entries
+ Listlen uint32 // Length of import file ID string table
+ Lnimpid int32 // Number of import file IDs
+ Lstlen uint32 // Length of string table
+ Limpoff uint64 // Offset to start of import file IDs
+ Lstoff uint64 // Offset to start of string table
+ Lsymoff uint64 // Offset to start of symbol table
+ Lrldoff uint64 // Offset to start of relocation entries
+}
+
+// Loader Symbol
+type XcoffLdSym64 struct {
+ Lvalue uint64 // Address field
+ Loffset uint32 // Byte offset into string table of symbol name
+ Lscnum int16 // Section number containing symbol
+ Lsmtype int8 // Symbol type, export, import flags
+ Lsmclas int8 // Symbol storage class
+ Lifile int32 // Import file ID; ordinal of import file IDs
+ Lparm uint32 // Parameter type-check field
+}
+
+type xcoffLoaderSymbol struct {
+ sym loader.Sym
+ smtype int8
+ smclas int8
+}
+
+type XcoffLdImportFile64 struct {
+ Limpidpath string
+ Limpidbase string
+ Limpidmem string
+}
+
+type XcoffLdRel64 struct {
+ Lvaddr uint64 // Address Field
+ Lrtype uint16 // Relocation Size and Type
+ Lrsecnm int16 // Section Number being relocated
+ Lsymndx int32 // Loader-Section symbol table index
+}
+
+// xcoffLoaderReloc holds information about a relocation made by the loader.
+type xcoffLoaderReloc struct {
+ sym loader.Sym
+ roff int32
+ rtype uint16
+ symndx int32
+}
+
+const (
+ XCOFF_R_POS = 0x00 // A(sym) Positive Relocation
+ XCOFF_R_NEG = 0x01 // -A(sym) Negative Relocation
+ XCOFF_R_REL = 0x02 // A(sym-*) Relative to self
+ XCOFF_R_TOC = 0x03 // A(sym-TOC) Relative to TOC
+ XCOFF_R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load.
+
+ XCOFF_R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst
+ XCOFF_R_GL = 0x05 // A(external TOC of sym) Global Linkage
+ XCOFF_R_TCL = 0x06 // A(local TOC of sym) Local object TOC address
+ XCOFF_R_RL = 0x0C // A(sym) Pos indirect load. modifiable instruction
+ XCOFF_R_RLA = 0x0D // A(sym) Pos Load Address. modifiable instruction
+ XCOFF_R_REF = 0x0F // AL0(sym) Non relocating ref. No garbage collect
+ XCOFF_R_BA = 0x08 // A(sym) Branch absolute. Cannot modify instruction
+ XCOFF_R_RBA = 0x18 // A(sym) Branch absolute. modifiable instruction
+ XCOFF_R_BR = 0x0A // A(sym-*) Branch rel to self. non modifiable
+ XCOFF_R_RBR = 0x1A // A(sym-*) Branch rel to self. modifiable instr
+
+ XCOFF_R_TLS = 0x20 // General-dynamic reference to TLS symbol
+ XCOFF_R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol
+ XCOFF_R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol
+ XCOFF_R_TLS_LE = 0x23 // Local-exec reference to TLS symbol
+ XCOFF_R_TLSM = 0x24 // Module reference to TLS symbol
+ XCOFF_R_TLSML = 0x25 // Module reference to local (own) module
+
+ XCOFF_R_TOCU = 0x30 // Relative to TOC - high order bits
+ XCOFF_R_TOCL = 0x31 // Relative to TOC - low order bits
+)
+
+type XcoffLdStr64 struct {
+ size uint16
+ name string
+}
+
+// xcoffFile is used to build XCOFF file.
+type xcoffFile struct {
+ xfhdr XcoffFileHdr64
+ xahdr XcoffAoutHdr64
+ sections []*XcoffScnHdr64
+ sectText *XcoffScnHdr64
+ sectData *XcoffScnHdr64
+ sectBss *XcoffScnHdr64
+ stringTable xcoffStringTable
+ sectNameToScnum map[string]int16
+ loaderSize uint64
+ symtabOffset int64 // offset to the start of symbol table
+ symbolCount uint32 // number of symbol table records written
+ symtabSym []xcoffSym // XCOFF symbols for the symbol table
+ dynLibraries map[string]int // Dynamic libraries in .loader section. The integer represents its import file number (- 1)
+ loaderSymbols []*xcoffLoaderSymbol // symbols inside .loader symbol table
+ loaderReloc []*xcoffLoaderReloc // Reloc that must be made inside loader
+ sync.Mutex // currently protect loaderReloc
+}
+
+// Var used by XCOFF Generation algorithms
+var (
+ xfile xcoffFile
+)
+
+// xcoffStringTable is a XCOFF string table.
+type xcoffStringTable struct {
+ strings []string
+ stringsLen int
+}
+
+// size returns size of string table t.
+func (t *xcoffStringTable) size() int {
+ // string table starts with 4-byte length at the beginning
+ return t.stringsLen + 4
+}
+
+// add adds string str to string table t.
+func (t *xcoffStringTable) add(str string) int {
+ off := t.size()
+ t.strings = append(t.strings, str)
+ t.stringsLen += len(str) + 1 // each string will have 0 appended to it
+ return off
+}
+
+// write writes string table t into the output file.
+func (t *xcoffStringTable) write(out *OutBuf) {
+ out.Write32(uint32(t.size()))
+ for _, s := range t.strings {
+ out.WriteString(s)
+ out.Write8(0)
+ }
+}
+
+// write writes XCOFF section sect into the output file.
+func (sect *XcoffScnHdr64) write(ctxt *Link) {
+ binary.Write(ctxt.Out, binary.BigEndian, sect)
+ ctxt.Out.Write32(0) // Add 4 empty bytes at the end to match alignment
+}
+
+// addSection adds section to the XCOFF file f.
+func (f *xcoffFile) addSection(name string, addr uint64, size uint64, fileoff uint64, flags uint32) *XcoffScnHdr64 {
+ sect := &XcoffScnHdr64{
+ Spaddr: addr,
+ Svaddr: addr,
+ Ssize: size,
+ Sscnptr: fileoff,
+ Sflags: flags,
+ }
+ copy(sect.Sname[:], name) // copy string to [8]byte
+ f.sections = append(f.sections, sect)
+ f.sectNameToScnum[name] = int16(len(f.sections))
+ return sect
+}
+
+// addDwarfSection adds a dwarf section to the XCOFF file f.
+// This function is similar to addSection, but Dwarf section names
+// must be modified to conventional names and they are various subtypes.
+func (f *xcoffFile) addDwarfSection(s *sym.Section) *XcoffScnHdr64 {
+ newName, subtype := xcoffGetDwarfSubtype(s.Name)
+ return f.addSection(newName, 0, s.Length, s.Seg.Fileoff+s.Vaddr-s.Seg.Vaddr, STYP_DWARF|subtype)
+}
+
+// xcoffGetDwarfSubtype returns the XCOFF name of the DWARF section str
+// and its subtype constant.
+func xcoffGetDwarfSubtype(str string) (string, uint32) {
+ switch str {
+ default:
+ Exitf("unknown DWARF section name for XCOFF: %s", str)
+ case ".debug_abbrev":
+ return ".dwabrev", SSUBTYP_DWABREV
+ case ".debug_info":
+ return ".dwinfo", SSUBTYP_DWINFO
+ case ".debug_frame":
+ return ".dwframe", SSUBTYP_DWFRAME
+ case ".debug_line":
+ return ".dwline", SSUBTYP_DWLINE
+ case ".debug_loc":
+ return ".dwloc", SSUBTYP_DWLOC
+ case ".debug_pubnames":
+ return ".dwpbnms", SSUBTYP_DWPBNMS
+ case ".debug_pubtypes":
+ return ".dwpbtyp", SSUBTYP_DWPBTYP
+ case ".debug_ranges":
+ return ".dwrnges", SSUBTYP_DWRNGES
+ }
+ // never used
+ return "", 0
+}
+
+// getXCOFFscnum returns the XCOFF section number of a Go section.
+func (f *xcoffFile) getXCOFFscnum(sect *sym.Section) int16 {
+ switch sect.Seg {
+ case &Segtext:
+ return f.sectNameToScnum[".text"]
+ case &Segdata:
+ if sect.Name == ".noptrbss" || sect.Name == ".bss" {
+ return f.sectNameToScnum[".bss"]
+ }
+ if sect.Name == ".tbss" {
+ return f.sectNameToScnum[".tbss"]
+ }
+ return f.sectNameToScnum[".data"]
+ case &Segdwarf:
+ name, _ := xcoffGetDwarfSubtype(sect.Name)
+ return f.sectNameToScnum[name]
+ case &Segrelrodata:
+ return f.sectNameToScnum[".data"]
+ }
+ Errorf(nil, "getXCOFFscnum not implemented for section %s", sect.Name)
+ return -1
+}
+
+// Xcoffinit initialised some internal value and setups
+// already known header information.
+func Xcoffinit(ctxt *Link) {
+ xfile.dynLibraries = make(map[string]int)
+
+ HEADR = int32(Rnd(XCOFFHDRRESERVE, XCOFFSECTALIGN))
+ if *FlagTextAddr != -1 {
+ Errorf(nil, "-T not available on AIX")
+ }
+ *FlagTextAddr = XCOFFTEXTBASE + int64(HEADR)
+ if *FlagRound != -1 {
+ Errorf(nil, "-R not available on AIX")
+ }
+ *FlagRound = int(XCOFFSECTALIGN)
+
+}
+
+// SYMBOL TABLE
+
+// type records C_FILE information needed for genasmsym in XCOFF.
+type xcoffSymSrcFile struct {
+ name string
+ file *XcoffSymEnt64 // Symbol of this C_FILE
+ csectAux *XcoffAuxCSect64 // Symbol for the current .csect
+ csectSymNb uint64 // Symbol number for the current .csect
+ csectVAStart int64
+ csectVAEnd int64
+}
+
+var (
+ currDwscnoff = make(map[string]uint64) // Needed to create C_DWARF symbols
+ currSymSrcFile xcoffSymSrcFile
+ outerSymSize = make(map[string]int64)
+)
+
+// xcoffUpdateOuterSize stores the size of outer symbols in order to have it
+// in the symbol table.
+func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
+ if size == 0 {
+ return
+ }
+ // TODO: use CarrierSymByType
+
+ ldr := ctxt.loader
+ switch stype {
+ default:
+ Errorf(nil, "unknown XCOFF outer symbol for type %s", stype.String())
+ case sym.SRODATA, sym.SRODATARELRO, sym.SFUNCTAB, sym.SSTRING:
+ // Nothing to do
+ case sym.STYPERELRO:
+ if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
+ // runtime.types size must be removed, as it's a real symbol.
+ tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
+ outerSymSize["typerel.*"] = size - tsize
+ return
+ }
+ fallthrough
+ case sym.STYPE:
+ if !ctxt.DynlinkingGo() {
+ // runtime.types size must be removed, as it's a real symbol.
+ tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
+ outerSymSize["type:*"] = size - tsize
+ }
+ case sym.SGOSTRING:
+ outerSymSize["go:string.*"] = size
+ case sym.SGOFUNC:
+ if !ctxt.DynlinkingGo() {
+ outerSymSize["go:func.*"] = size
+ }
+ case sym.SGOFUNCRELRO:
+ outerSymSize["go:funcrel.*"] = size
+ case sym.SGCBITS:
+ outerSymSize["runtime.gcbits.*"] = size
+ case sym.SPCLNTAB:
+ outerSymSize["runtime.pclntab"] = size
+ }
+}
+
+// addSymbol writes a symbol or an auxiliary symbol entry on ctxt.out.
+func (f *xcoffFile) addSymbol(sym xcoffSym) {
+ f.symtabSym = append(f.symtabSym, sym)
+ f.symbolCount++
+}
+
+// xcoffAlign returns the log base 2 of the symbol's alignment.
+func xcoffAlign(ldr *loader.Loader, x loader.Sym, t SymbolType) uint8 {
+ align := ldr.SymAlign(x)
+ if align == 0 {
+ if t == TextSym {
+ align = int32(Funcalign)
+ } else {
+ align = symalign(ldr, x)
+ }
+ }
+ return logBase2(int(align))
+}
+
+// logBase2 returns the log in base 2 of a.
+func logBase2(a int) uint8 {
+ return uint8(bits.Len(uint(a)) - 1)
+}
+
+// Write symbols needed when a new file appeared:
+// - a C_FILE with one auxiliary entry for its name
+// - C_DWARF symbols to provide debug information
+// - a C_HIDEXT which will be a csect containing all of its functions
+// It needs several parameters to create .csect symbols such as its entry point and its section number.
+//
+// Currently, a new file is in fact a new package. It seems to be OK, but it might change
+// in the future.
+func (f *xcoffFile) writeSymbolNewFile(ctxt *Link, name string, firstEntry uint64, extnum int16) {
+ ldr := ctxt.loader
+ /* C_FILE */
+ s := &XcoffSymEnt64{
+ Noffset: uint32(f.stringTable.add(".file")),
+ Nsclass: C_FILE,
+ Nscnum: N_DEBUG,
+ Ntype: 0, // Go isn't inside predefined language.
+ Nnumaux: 1,
+ }
+ f.addSymbol(s)
+ currSymSrcFile.file = s
+
+ // Auxiliary entry for file name.
+ auxf := &XcoffAuxFile64{
+ Xoffset: uint32(f.stringTable.add(name)),
+ Xftype: XFT_FN,
+ Xauxtype: _AUX_FILE,
+ }
+ f.addSymbol(auxf)
+
+ /* Dwarf */
+ for _, sect := range Segdwarf.Sections {
+ var dwsize uint64
+ if ctxt.LinkMode == LinkInternal {
+ // Find the size of this corresponding package DWARF compilation unit.
+ // This size is set during DWARF generation (see dwarf.go).
+ dwsize = getDwsectCUSize(sect.Name, name)
+ // .debug_abbrev is common to all packages and not found with the previous function
+ if sect.Name == ".debug_abbrev" {
+ dwsize = uint64(ldr.SymSize(loader.Sym(sect.Sym)))
+
+ }
+ } else {
+ // There is only one .FILE with external linking.
+ dwsize = sect.Length
+ }
+
+ // get XCOFF name
+ name, _ := xcoffGetDwarfSubtype(sect.Name)
+ s := &XcoffSymEnt64{
+ Nvalue: currDwscnoff[sect.Name],
+ Noffset: uint32(f.stringTable.add(name)),
+ Nsclass: C_DWARF,
+ Nscnum: f.getXCOFFscnum(sect),
+ Nnumaux: 1,
+ }
+
+ if currSymSrcFile.csectAux == nil {
+ // Dwarf relocations need the symbol number of .dw* symbols.
+ // It doesn't need to know it for each package, one is enough.
+ // currSymSrcFile.csectAux == nil means first package.
+ ldr.SetSymDynid(loader.Sym(sect.Sym), int32(f.symbolCount))
+
+ if sect.Name == ".debug_frame" && ctxt.LinkMode != LinkExternal {
+ // CIE size must be added to the first package.
+ dwsize += 48
+ }
+ }
+
+ f.addSymbol(s)
+
+ // update the DWARF section offset in this file
+ if sect.Name != ".debug_abbrev" {
+ currDwscnoff[sect.Name] += dwsize
+ }
+
+ // Auxiliary dwarf section
+ auxd := &XcoffAuxDWARF64{
+ Xscnlen: dwsize,
+ Xauxtype: _AUX_SECT,
+ }
+
+ f.addSymbol(auxd)
+ }
+
+ /* .csect */
+ // Check if extnum is in text.
+ // This is temporary and only here to check if this algorithm is correct.
+ if extnum != 1 {
+ Exitf("XCOFF symtab: A new file was detected with its first symbol not in .text")
+ }
+
+ currSymSrcFile.csectSymNb = uint64(f.symbolCount)
+
+ // No offset because no name
+ s = &XcoffSymEnt64{
+ Nvalue: firstEntry,
+ Nscnum: extnum,
+ Nsclass: C_HIDEXT,
+ Ntype: 0, // check visibility ?
+ Nnumaux: 1,
+ }
+ f.addSymbol(s)
+
+ aux := &XcoffAuxCSect64{
+ Xsmclas: XMC_PR,
+ Xsmtyp: XTY_SD | logBase2(Funcalign)<<3,
+ Xauxtype: _AUX_CSECT,
+ }
+ f.addSymbol(aux)
+
+ currSymSrcFile.csectAux = aux
+ currSymSrcFile.csectVAStart = int64(firstEntry)
+ currSymSrcFile.csectVAEnd = int64(firstEntry)
+}
+
+// Update values for the previous package.
+// - Svalue of the C_FILE symbol: if it is the last one, this Svalue must be -1
+// - Xsclen of the csect symbol.
+func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) {
+ // first file
+ if currSymSrcFile.file == nil {
+ return
+ }
+
+ // Update C_FILE
+ cfile := currSymSrcFile.file
+ if last {
+ cfile.Nvalue = 0xFFFFFFFFFFFFFFFF
+ } else {
+ cfile.Nvalue = uint64(f.symbolCount)
+ }
+
+ // update csect scnlen in this auxiliary entry
+ aux := currSymSrcFile.csectAux
+ csectSize := currSymSrcFile.csectVAEnd - currSymSrcFile.csectVAStart
+ aux.Xscnlenlo = uint32(csectSize & 0xFFFFFFFF)
+ aux.Xscnlenhi = uint32(csectSize >> 32)
+}
+
+// Write symbol representing a .text function.
+// The symbol table is split with C_FILE corresponding to each package
+// and not to each source file as it should be.
+func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x loader.Sym) []xcoffSym {
+ // New XCOFF symbols which will be written.
+ syms := []xcoffSym{}
+
+ // Check if a new file is detected.
+ ldr := ctxt.loader
+ name := ldr.SymName(x)
+ if strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") {
+ // Trampoline don't have a FILE so there are considered
+ // in the current file.
+ // Same goes for runtime.text.X symbols.
+ } else if ldr.SymPkg(x) == "" { // Undefined global symbol
+ // If this happens, the algorithm must be redone.
+ if currSymSrcFile.name != "" {
+ Exitf("undefined global symbol found inside another file")
+ }
+ } else {
+ // Current file has changed. New C_FILE, C_DWARF, etc must be generated.
+ if currSymSrcFile.name != ldr.SymPkg(x) {
+ if ctxt.LinkMode == LinkInternal {
+ // update previous file values
+ xfile.updatePreviousFile(ctxt, false)
+ currSymSrcFile.name = ldr.SymPkg(x)
+ f.writeSymbolNewFile(ctxt, ldr.SymPkg(x), uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x)))
+ } else {
+ // With external linking, ld will crash if there is several
+ // .FILE and DWARF debugging enable, somewhere during
+ // the relocation phase.
+ // Therefore, all packages are merged under a fake .FILE
+ // "go_functions".
+ // TODO(aix); remove once ld has been fixed or the triggering
+ // relocation has been found and fixed.
+ if currSymSrcFile.name == "" {
+ currSymSrcFile.name = ldr.SymPkg(x)
+ f.writeSymbolNewFile(ctxt, "go_functions", uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x)))
+ }
+ }
+
+ }
+ }
+
+ name = ldr.SymExtname(x)
+ name = mangleABIName(ctxt, ldr, x, name)
+
+ s := &XcoffSymEnt64{
+ Nsclass: C_EXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nvalue: uint64(ldr.SymValue(x)),
+ Nscnum: f.getXCOFFscnum(ldr.SymSect(x)),
+ Ntype: SYM_TYPE_FUNC,
+ Nnumaux: 2,
+ }
+
+ if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
+ s.Nsclass = C_HIDEXT
+ }
+
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ // Keep track of the section size by tracking the VA range. Individual
+ // alignment differences may introduce a few extra bytes of padding
+ // which are not fully accounted for by ldr.SymSize(x).
+ sv := ldr.SymValue(x) + ldr.SymSize(x)
+ if currSymSrcFile.csectVAEnd < sv {
+ currSymSrcFile.csectVAEnd = sv
+ }
+
+ // create auxiliary entries
+ a2 := &XcoffAuxFcn64{
+ Xfsize: uint32(ldr.SymSize(x)),
+ Xlnnoptr: 0, // TODO
+ Xendndx: xfile.symbolCount + 3, // this symbol + 2 aux entries
+ Xauxtype: _AUX_FCN,
+ }
+ syms = append(syms, a2)
+
+ a4 := &XcoffAuxCSect64{
+ Xscnlenlo: uint32(currSymSrcFile.csectSymNb & 0xFFFFFFFF),
+ Xscnlenhi: uint32(currSymSrcFile.csectSymNb >> 32),
+ Xsmclas: XMC_PR, // Program Code
+ Xsmtyp: XTY_LD, // label definition (based on C)
+ Xauxtype: _AUX_CSECT,
+ }
+ a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3)
+
+ syms = append(syms, a4)
+ return syms
+}
+
+// put function used by genasmsym to write symbol table.
+func putaixsym(ctxt *Link, x loader.Sym, t SymbolType) {
+ // All XCOFF symbols generated by this GO symbols
+ // Can be a symbol entry or a auxiliary entry
+ syms := []xcoffSym{}
+
+ ldr := ctxt.loader
+ name := ldr.SymName(x)
+ if t == UndefinedSym {
+ name = ldr.SymExtname(x)
+ }
+
+ switch t {
+ default:
+ return
+
+ case TextSym:
+ if ldr.SymPkg(x) != "" || strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") {
+ // Function within a file
+ syms = xfile.writeSymbolFunc(ctxt, x)
+ } else {
+ // Only runtime.text and runtime.etext come through this way
+ if name != "runtime.text" && name != "runtime.etext" && name != "go:buildid" {
+ Exitf("putaixsym: unknown text symbol %s", name)
+ }
+ s := &XcoffSymEnt64{
+ Nsclass: C_HIDEXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nvalue: uint64(ldr.SymValue(x)),
+ Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)),
+ Ntype: SYM_TYPE_FUNC,
+ Nnumaux: 1,
+ }
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ size := uint64(ldr.SymSize(x))
+ a4 := &XcoffAuxCSect64{
+ Xauxtype: _AUX_CSECT,
+ Xscnlenlo: uint32(size & 0xFFFFFFFF),
+ Xscnlenhi: uint32(size >> 32),
+ Xsmclas: XMC_PR,
+ Xsmtyp: XTY_SD,
+ }
+ a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3)
+ syms = append(syms, a4)
+ }
+
+ case DataSym, BSSSym:
+ s := &XcoffSymEnt64{
+ Nsclass: C_EXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nvalue: uint64(ldr.SymValue(x)),
+ Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)),
+ Nnumaux: 1,
+ }
+
+ if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
+ // There is more symbols in the case of a global data
+ // which are related to the assembly generated
+ // to access such symbols.
+ // But as Golang as its own way to check if a symbol is
+ // global or local (the capital letter), we don't need to
+ // implement them yet.
+ s.Nsclass = C_HIDEXT
+ }
+
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ // Create auxiliary entry
+
+ // Normally, size should be the size of csect containing all
+ // the data and bss symbols of one file/package.
+ // However, it's easier to just have a csect for each symbol.
+ // It might change
+ size := uint64(ldr.SymSize(x))
+ a4 := &XcoffAuxCSect64{
+ Xauxtype: _AUX_CSECT,
+ Xscnlenlo: uint32(size & 0xFFFFFFFF),
+ Xscnlenhi: uint32(size >> 32),
+ }
+
+ if ty := ldr.SymType(x); ty >= sym.STYPE && ty <= sym.SPCLNTAB {
+ if ctxt.IsExternal() && strings.HasPrefix(ldr.SymSect(x).Name, ".data.rel.ro") {
+ // During external linking, read-only datas with relocation
+ // must be in .data.
+ a4.Xsmclas = XMC_RW
+ } else {
+ // Read only data
+ a4.Xsmclas = XMC_RO
+ }
+ } else if /*ty == sym.SDATA &&*/ strings.HasPrefix(ldr.SymName(x), "TOC.") && ctxt.IsExternal() {
+ a4.Xsmclas = XMC_TC
+ } else if ldr.SymName(x) == "TOC" {
+ a4.Xsmclas = XMC_TC0
+ } else {
+ a4.Xsmclas = XMC_RW
+ }
+ if t == DataSym {
+ a4.Xsmtyp |= XTY_SD
+ } else {
+ a4.Xsmtyp |= XTY_CM
+ }
+
+ a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, t) << 3)
+
+ syms = append(syms, a4)
+
+ case UndefinedSym:
+ if ty := ldr.SymType(x); ty != sym.SDYNIMPORT && ty != sym.SHOSTOBJ && ty != sym.SUNDEFEXT {
+ return
+ }
+ s := &XcoffSymEnt64{
+ Nsclass: C_EXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nnumaux: 1,
+ }
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ a4 := &XcoffAuxCSect64{
+ Xauxtype: _AUX_CSECT,
+ Xsmclas: XMC_DS,
+ Xsmtyp: XTY_ER | XTY_IMP,
+ }
+
+ if ldr.SymName(x) == "__n_pthreads" {
+ // Currently, all imported symbols made by cgo_import_dynamic are
+ // syscall functions, except __n_pthreads which is a variable.
+ // TODO(aix): Find a way to detect variables imported by cgo.
+ a4.Xsmclas = XMC_RW
+ }
+
+ syms = append(syms, a4)
+
+ case TLSSym:
+ s := &XcoffSymEnt64{
+ Nsclass: C_EXT,
+ Noffset: uint32(xfile.stringTable.add(name)),
+ Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)),
+ Nvalue: uint64(ldr.SymValue(x)),
+ Nnumaux: 1,
+ }
+
+ ldr.SetSymDynid(x, int32(xfile.symbolCount))
+ syms = append(syms, s)
+
+ size := uint64(ldr.SymSize(x))
+ a4 := &XcoffAuxCSect64{
+ Xauxtype: _AUX_CSECT,
+ Xsmclas: XMC_UL,
+ Xsmtyp: XTY_CM,
+ Xscnlenlo: uint32(size & 0xFFFFFFFF),
+ Xscnlenhi: uint32(size >> 32),
+ }
+
+ syms = append(syms, a4)
+ }
+
+ for _, s := range syms {
+ xfile.addSymbol(s)
+ }
+}
+
+// Generate XCOFF Symbol table.
+// It will be written in out file in Asmbxcoff, because it must be
+// at the very end, especially after relocation sections which needs symbols' index.
+func (f *xcoffFile) asmaixsym(ctxt *Link) {
+ ldr := ctxt.loader
+ // Get correct size for symbols wrapping others symbols like go.string.*
+ // sym.Size can be used directly as the symbols have already been written.
+ for name, size := range outerSymSize {
+ sym := ldr.Lookup(name, 0)
+ if sym == 0 {
+ Errorf(nil, "unknown outer symbol with name %s", name)
+ } else {
+ s := ldr.MakeSymbolUpdater(sym)
+ s.SetSize(size)
+ }
+ }
+
+ // These symbols won't show up in the first loop below because we
+ // skip sym.STEXT symbols. Normal sym.STEXT symbols are emitted by walking textp.
+ s := ldr.Lookup("runtime.text", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ // We've already included this symbol in ctxt.Textp on AIX with external linker.
+ // See data.go:/textaddress
+ if !ctxt.IsExternal() {
+ putaixsym(ctxt, s, TextSym)
+ }
+ }
+
+ n := 1
+ // Generate base addresses for all text sections if there are multiple
+ for _, sect := range Segtext.Sections[1:] {
+ if sect.Name != ".text" || ctxt.IsExternal() {
+ // On AIX, runtime.text.X are symbols already in the symtab.
+ break
+ }
+ s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
+ if s == 0 {
+ break
+ }
+ if ldr.SymType(s) == sym.STEXT {
+ putaixsym(ctxt, s, TextSym)
+ }
+ n++
+ }
+
+ s = ldr.Lookup("runtime.etext", 0)
+ if ldr.SymType(s) == sym.STEXT {
+ // We've already included this symbol in ctxt.Textp
+ // on AIX with external linker.
+ // See data.go:/textaddress
+ if !ctxt.IsExternal() {
+ putaixsym(ctxt, s, TextSym)
+ }
+ }
+
+ shouldBeInSymbolTable := func(s loader.Sym, name string) bool {
+ if name == ".go.buildinfo" {
+ // On AIX, .go.buildinfo must be in the symbol table as
+ // it has relocations.
+ return true
+ }
+ if ldr.AttrNotInSymbolTable(s) {
+ return false
+ }
+ if (name == "" || name[0] == '.') && !ldr.IsFileLocal(s) && name != ".TOC." {
+ return false
+ }
+ return true
+ }
+
+ for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ {
+ if !shouldBeInSymbolTable(s, ldr.SymName(s)) {
+ continue
+ }
+ st := ldr.SymType(s)
+ switch {
+ case st == sym.STLSBSS:
+ if ctxt.IsExternal() {
+ putaixsym(ctxt, s, TLSSym)
+ }
+
+ case st == sym.SBSS, st == sym.SNOPTRBSS, st == sym.SLIBFUZZER_8BIT_COUNTER, st == sym.SCOVERAGE_COUNTER:
+ if ldr.AttrReachable(s) {
+ data := ldr.Data(s)
+ if len(data) > 0 {
+ ldr.Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(data), ldr.SymType(s), ldr.AttrSpecial(s))
+ }
+ putaixsym(ctxt, s, BSSSym)
+ }
+
+ case st >= sym.SELFRXSECT && st < sym.SXREF: // data sections handled in dodata
+ if ldr.AttrReachable(s) {
+ putaixsym(ctxt, s, DataSym)
+ }
+
+ case st == sym.SUNDEFEXT:
+ putaixsym(ctxt, s, UndefinedSym)
+
+ case st == sym.SDYNIMPORT:
+ if ldr.AttrReachable(s) {
+ putaixsym(ctxt, s, UndefinedSym)
+ }
+ }
+ }
+
+ for _, s := range ctxt.Textp {
+ putaixsym(ctxt, s, TextSym)
+ }
+
+ if ctxt.Debugvlog != 0 || *flagN {
+ ctxt.Logf("symsize = %d\n", uint32(symSize))
+ }
+ xfile.updatePreviousFile(ctxt, true)
+}
+
+func (f *xcoffFile) genDynSym(ctxt *Link) {
+ ldr := ctxt.loader
+ var dynsyms []loader.Sym
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if t := ldr.SymType(s); t != sym.SHOSTOBJ && t != sym.SDYNIMPORT {
+ continue
+ }
+ dynsyms = append(dynsyms, s)
+ }
+
+ for _, s := range dynsyms {
+ f.adddynimpsym(ctxt, s)
+
+ if _, ok := f.dynLibraries[ldr.SymDynimplib(s)]; !ok {
+ f.dynLibraries[ldr.SymDynimplib(s)] = len(f.dynLibraries)
+ }
+ }
+}
+
+// (*xcoffFile)adddynimpsym adds the dynamic symbol "s" to a XCOFF file.
+// A new symbol named s.Extname() is created to be the actual dynamic symbol
+// in the .loader section and in the symbol table as an External Reference.
+// The symbol "s" is transformed to SXCOFFTOC to end up in .data section.
+// However, there is no writing protection on those symbols and
+// it might need to be added.
+// TODO(aix): Handles dynamic symbols without library.
+func (f *xcoffFile) adddynimpsym(ctxt *Link, s loader.Sym) {
+ // Check that library name is given.
+ // Pattern is already checked when compiling.
+ ldr := ctxt.loader
+ if ctxt.IsInternal() && ldr.SymDynimplib(s) == "" {
+ ctxt.Errorf(s, "imported symbol must have a given library")
+ }
+
+ sb := ldr.MakeSymbolUpdater(s)
+ sb.SetReachable(true)
+ sb.SetType(sym.SXCOFFTOC)
+
+ // Create new dynamic symbol
+ extsym := ldr.CreateSymForUpdate(ldr.SymExtname(s), 0)
+ extsym.SetType(sym.SDYNIMPORT)
+ extsym.SetDynimplib(ldr.SymDynimplib(s))
+ extsym.SetExtname(ldr.SymExtname(s))
+ extsym.SetDynimpvers(ldr.SymDynimpvers(s))
+
+ // Add loader symbol
+ lds := &xcoffLoaderSymbol{
+ sym: extsym.Sym(),
+ smtype: XTY_IMP,
+ smclas: XMC_DS,
+ }
+ if ldr.SymName(s) == "__n_pthreads" {
+ // Currently, all imported symbols made by cgo_import_dynamic are
+ // syscall functions, except __n_pthreads which is a variable.
+ // TODO(aix): Find a way to detect variables imported by cgo.
+ lds.smclas = XMC_RW
+ }
+ f.loaderSymbols = append(f.loaderSymbols, lds)
+
+ // Relocation to retrieve the external address
+ sb.AddBytes(make([]byte, 8))
+ r, _ := sb.AddRel(objabi.R_ADDR)
+ r.SetSym(extsym.Sym())
+ r.SetSiz(uint8(ctxt.Arch.PtrSize))
+ // TODO: maybe this could be
+ // sb.SetSize(0)
+ // sb.SetData(nil)
+ // sb.AddAddr(ctxt.Arch, extsym.Sym())
+ // If the size is not 0 to begin with, I don't think the added 8 bytes
+ // of zeros are necessary.
+}
+
+// Xcoffadddynrel adds a dynamic relocation in a XCOFF file.
+// This relocation will be made by the loader.
+func Xcoffadddynrel(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ if target.IsExternal() {
+ return true
+ }
+ if ldr.SymType(s) <= sym.SPCLNTAB {
+ ldr.Errorf(s, "cannot have a relocation to %s in a text section symbol", ldr.SymName(r.Sym()))
+ return false
+ }
+
+ xldr := &xcoffLoaderReloc{
+ sym: s,
+ roff: r.Off(),
+ }
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ ldr.Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", ldr.SymName(targ), r.Type().String())
+ return false
+ case objabi.R_ADDR:
+ if ldr.SymType(s) == sym.SXCOFFTOC && targType == sym.SDYNIMPORT {
+ // Imported symbol relocation
+ for i, dynsym := range xfile.loaderSymbols {
+ if ldr.SymName(dynsym.sym) == ldr.SymName(targ) {
+ xldr.symndx = int32(i + 3) // +3 because of 3 section symbols
+ break
+ }
+ }
+ } else if t := ldr.SymType(s); t == sym.SDATA || t == sym.SNOPTRDATA || t == sym.SBUILDINFO || t == sym.SXCOFFTOC {
+ switch ldr.SymSect(targ).Seg {
+ default:
+ ldr.Errorf(s, "unknown segment for .loader relocation with symbol %s", ldr.SymName(targ))
+ case &Segtext:
+ case &Segrodata:
+ xldr.symndx = 0 // .text
+ case &Segdata:
+ if targType == sym.SBSS || targType == sym.SNOPTRBSS {
+ xldr.symndx = 2 // .bss
+ } else {
+ xldr.symndx = 1 // .data
+ }
+ }
+
+ } else {
+ ldr.Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", ldr.SymName(targ), ldr.SymType(s), ldr.SymType(targ))
+ return false
+ }
+
+ xldr.rtype = 0x3F<<8 + XCOFF_R_POS
+ }
+
+ xfile.Lock()
+ xfile.loaderReloc = append(xfile.loaderReloc, xldr)
+ xfile.Unlock()
+ return true
+}
+
+func (ctxt *Link) doxcoff() {
+ ldr := ctxt.loader
+
+ // TOC
+ toc := ldr.CreateSymForUpdate("TOC", 0)
+ toc.SetType(sym.SXCOFFTOC)
+ toc.SetVisibilityHidden(true)
+
+ // Add entry point to .loader symbols.
+ ep := ldr.Lookup(*flagEntrySymbol, 0)
+ if ep == 0 || !ldr.AttrReachable(ep) {
+ Exitf("wrong entry point")
+ }
+
+ xfile.loaderSymbols = append(xfile.loaderSymbols, &xcoffLoaderSymbol{
+ sym: ep,
+ smtype: XTY_ENT | XTY_SD,
+ smclas: XMC_DS,
+ })
+
+ xfile.genDynSym(ctxt)
+
+ for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
+ if strings.HasPrefix(ldr.SymName(s), "TOC.") {
+ sb := ldr.MakeSymbolUpdater(s)
+ sb.SetType(sym.SXCOFFTOC)
+ }
+ }
+
+ if ctxt.IsExternal() {
+ // Change rt0_go name to match name in runtime/cgo:main().
+ rt0 := ldr.Lookup("runtime.rt0_go", 0)
+ ldr.SetSymExtname(rt0, "runtime_rt0_go")
+
+ nsym := loader.Sym(ldr.NSym())
+ for s := loader.Sym(1); s < nsym; s++ {
+ if !ldr.AttrCgoExport(s) {
+ continue
+ }
+ if ldr.IsFileLocal(s) {
+ panic("cgo_export on static symbol")
+ }
+
+ if ldr.SymType(s) == sym.STEXT {
+ // On AIX, a exported function must have two symbols:
+ // - a .text symbol which must start with a ".".
+ // - a .data symbol which is a function descriptor.
+ name := ldr.SymExtname(s)
+ ldr.SetSymExtname(s, "."+name)
+
+ desc := ldr.MakeSymbolUpdater(ldr.CreateExtSym(name, 0))
+ desc.SetReachable(true)
+ desc.SetType(sym.SNOPTRDATA)
+ desc.AddAddr(ctxt.Arch, s)
+ desc.AddAddr(ctxt.Arch, toc.Sym())
+ desc.AddUint64(ctxt.Arch, 0)
+ }
+ }
+ }
+}
+
+// Loader section
+// Currently, this section is created from scratch when assembling the XCOFF file
+// according to information retrieved in xfile object.
+
+// Create loader section and returns its size.
+func Loaderblk(ctxt *Link, off uint64) {
+ xfile.writeLdrScn(ctxt, off)
+}
+
+func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) {
+ var symtab []*XcoffLdSym64
+ var strtab []*XcoffLdStr64
+ var importtab []*XcoffLdImportFile64
+ var reloctab []*XcoffLdRel64
+ var dynimpreloc []*XcoffLdRel64
+
+ // As the string table is updated in any loader subsection,
+ // its length must be computed at the same time.
+ stlen := uint32(0)
+
+ // Loader Header
+ hdr := &XcoffLdHdr64{
+ Lversion: 2,
+ Lsymoff: LDHDRSZ_64,
+ }
+
+ ldr := ctxt.loader
+ /* Symbol table */
+ for _, s := range f.loaderSymbols {
+ lds := &XcoffLdSym64{
+ Loffset: uint32(stlen + 2),
+ Lsmtype: s.smtype,
+ Lsmclas: s.smclas,
+ }
+ sym := s.sym
+ switch s.smtype {
+ default:
+ ldr.Errorf(sym, "unexpected loader symbol type: 0x%x", s.smtype)
+ case XTY_ENT | XTY_SD:
+ lds.Lvalue = uint64(ldr.SymValue(sym))
+ lds.Lscnum = f.getXCOFFscnum(ldr.SymSect(sym))
+ case XTY_IMP:
+ lds.Lifile = int32(f.dynLibraries[ldr.SymDynimplib(sym)] + 1)
+ }
+ ldstr := &XcoffLdStr64{
+ size: uint16(len(ldr.SymName(sym)) + 1), // + null terminator
+ name: ldr.SymName(sym),
+ }
+ stlen += uint32(2 + ldstr.size) // 2 = sizeof ldstr.size
+ symtab = append(symtab, lds)
+ strtab = append(strtab, ldstr)
+
+ }
+
+ hdr.Lnsyms = int32(len(symtab))
+ hdr.Lrldoff = hdr.Lsymoff + uint64(24*hdr.Lnsyms) // 24 = sizeof one symbol
+ off := hdr.Lrldoff // current offset is the same of reloc offset
+
+ /* Reloc */
+ // Ensure deterministic order
+ sort.Slice(f.loaderReloc, func(i, j int) bool {
+ r1, r2 := f.loaderReloc[i], f.loaderReloc[j]
+ if r1.sym != r2.sym {
+ return r1.sym < r2.sym
+ }
+ if r1.roff != r2.roff {
+ return r1.roff < r2.roff
+ }
+ if r1.rtype != r2.rtype {
+ return r1.rtype < r2.rtype
+ }
+ return r1.symndx < r2.symndx
+ })
+
+ ep := ldr.Lookup(*flagEntrySymbol, 0)
+ xldr := &XcoffLdRel64{
+ Lvaddr: uint64(ldr.SymValue(ep)),
+ Lrtype: 0x3F00,
+ Lrsecnm: f.getXCOFFscnum(ldr.SymSect(ep)),
+ Lsymndx: 0,
+ }
+ off += 16
+ reloctab = append(reloctab, xldr)
+
+ off += uint64(16 * len(f.loaderReloc))
+ for _, r := range f.loaderReloc {
+ symp := r.sym
+ if symp == 0 {
+ panic("unexpected 0 sym value")
+ }
+ xldr = &XcoffLdRel64{
+ Lvaddr: uint64(ldr.SymValue(symp) + int64(r.roff)),
+ Lrtype: r.rtype,
+ Lsymndx: r.symndx,
+ }
+
+ if ldr.SymSect(symp) != nil {
+ xldr.Lrsecnm = f.getXCOFFscnum(ldr.SymSect(symp))
+ }
+
+ reloctab = append(reloctab, xldr)
+ }
+
+ off += uint64(16 * len(dynimpreloc))
+ reloctab = append(reloctab, dynimpreloc...)
+
+ hdr.Lnreloc = int32(len(reloctab))
+ hdr.Limpoff = off
+
+ /* Import */
+ // Default import: /usr/lib:/lib
+ ldimpf := &XcoffLdImportFile64{
+ Limpidpath: "/usr/lib:/lib",
+ }
+ off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter
+ importtab = append(importtab, ldimpf)
+
+ // The map created by adddynimpsym associates the name to a number
+ // This number represents the librairie index (- 1) in this import files section
+ // Therefore, they must be sorted before being put inside the section
+ libsOrdered := make([]string, len(f.dynLibraries))
+ for key, val := range f.dynLibraries {
+ if libsOrdered[val] != "" {
+ continue
+ }
+ libsOrdered[val] = key
+ }
+
+ for _, lib := range libsOrdered {
+ // lib string is defined as base.a/mem.o or path/base.a/mem.o
+ n := strings.Split(lib, "/")
+ path := ""
+ base := n[len(n)-2]
+ mem := n[len(n)-1]
+ if len(n) > 2 {
+ path = lib[:len(lib)-len(base)-len(mem)-2]
+
+ }
+ ldimpf = &XcoffLdImportFile64{
+ Limpidpath: path,
+ Limpidbase: base,
+ Limpidmem: mem,
+ }
+ off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter
+ importtab = append(importtab, ldimpf)
+ }
+
+ hdr.Lnimpid = int32(len(importtab))
+ hdr.Listlen = uint32(off - hdr.Limpoff)
+ hdr.Lstoff = off
+ hdr.Lstlen = stlen
+
+ /* Writing */
+ ctxt.Out.SeekSet(int64(globalOff))
+ binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, hdr)
+
+ for _, s := range symtab {
+ binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)
+
+ }
+ for _, r := range reloctab {
+ binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, r)
+ }
+ for _, f := range importtab {
+ ctxt.Out.WriteString(f.Limpidpath)
+ ctxt.Out.Write8(0)
+ ctxt.Out.WriteString(f.Limpidbase)
+ ctxt.Out.Write8(0)
+ ctxt.Out.WriteString(f.Limpidmem)
+ ctxt.Out.Write8(0)
+ }
+ for _, s := range strtab {
+ ctxt.Out.Write16(s.size)
+ ctxt.Out.WriteString(s.name)
+ ctxt.Out.Write8(0) // null terminator
+ }
+
+ f.loaderSize = off + uint64(stlen)
+}
+
+// XCOFF assembling and writing file
+
+func (f *xcoffFile) writeFileHeader(ctxt *Link) {
+ // File header
+ f.xfhdr.Fmagic = U64_TOCMAGIC
+ f.xfhdr.Fnscns = uint16(len(f.sections))
+ f.xfhdr.Ftimedat = 0
+
+ if !*FlagS {
+ f.xfhdr.Fsymptr = uint64(f.symtabOffset)
+ f.xfhdr.Fnsyms = int32(f.symbolCount)
+ }
+
+ if ctxt.BuildMode == BuildModeExe && ctxt.LinkMode == LinkInternal {
+ ldr := ctxt.loader
+ f.xfhdr.Fopthdr = AOUTHSZ_EXEC64
+ f.xfhdr.Fflags = F_EXEC
+
+ // auxiliary header
+ f.xahdr.Ovstamp = 1 // based on dump -o
+ f.xahdr.Omagic = 0x10b
+ copy(f.xahdr.Omodtype[:], "1L")
+ entry := ldr.Lookup(*flagEntrySymbol, 0)
+ f.xahdr.Oentry = uint64(ldr.SymValue(entry))
+ f.xahdr.Osnentry = f.getXCOFFscnum(ldr.SymSect(entry))
+ toc := ldr.Lookup("TOC", 0)
+ f.xahdr.Otoc = uint64(ldr.SymValue(toc))
+ f.xahdr.Osntoc = f.getXCOFFscnum(ldr.SymSect(toc))
+
+ f.xahdr.Oalgntext = int16(logBase2(int(XCOFFSECTALIGN)))
+ f.xahdr.Oalgndata = 0x5
+
+ binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
+ binary.Write(ctxt.Out, binary.BigEndian, &f.xahdr)
+ } else {
+ f.xfhdr.Fopthdr = 0
+ binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
+ }
+
+}
+
+func xcoffwrite(ctxt *Link) {
+ ctxt.Out.SeekSet(0)
+
+ xfile.writeFileHeader(ctxt)
+
+ for _, sect := range xfile.sections {
+ sect.write(ctxt)
+ }
+}
+
+// Generate XCOFF assembly file.
+func asmbXcoff(ctxt *Link) {
+ ctxt.Out.SeekSet(0)
+ fileoff := int64(Segdwarf.Fileoff + Segdwarf.Filelen)
+ fileoff = int64(Rnd(int64(fileoff), int64(*FlagRound)))
+
+ xfile.sectNameToScnum = make(map[string]int16)
+
+ // Add sections
+ s := xfile.addSection(".text", Segtext.Vaddr, Segtext.Length, Segtext.Fileoff, STYP_TEXT)
+ xfile.xahdr.Otextstart = s.Svaddr
+ xfile.xahdr.Osntext = xfile.sectNameToScnum[".text"]
+ xfile.xahdr.Otsize = s.Ssize
+ xfile.sectText = s
+
+ segdataVaddr := Segdata.Vaddr
+ segdataFilelen := Segdata.Filelen
+ segdataFileoff := Segdata.Fileoff
+ segbssFilelen := Segdata.Length - Segdata.Filelen
+ if len(Segrelrodata.Sections) > 0 {
+ // Merge relro segment to data segment as
+ // relro data are inside data segment on AIX.
+ segdataVaddr = Segrelrodata.Vaddr
+ segdataFileoff = Segrelrodata.Fileoff
+ segdataFilelen = Segdata.Vaddr + Segdata.Filelen - Segrelrodata.Vaddr
+ }
+
+ s = xfile.addSection(".data", segdataVaddr, segdataFilelen, segdataFileoff, STYP_DATA)
+ xfile.xahdr.Odatastart = s.Svaddr
+ xfile.xahdr.Osndata = xfile.sectNameToScnum[".data"]
+ xfile.xahdr.Odsize = s.Ssize
+ xfile.sectData = s
+
+ s = xfile.addSection(".bss", segdataVaddr+segdataFilelen, segbssFilelen, 0, STYP_BSS)
+ xfile.xahdr.Osnbss = xfile.sectNameToScnum[".bss"]
+ xfile.xahdr.Obsize = s.Ssize
+ xfile.sectBss = s
+
+ if ctxt.LinkMode == LinkExternal {
+ var tbss *sym.Section
+ for _, s := range Segdata.Sections {
+ if s.Name == ".tbss" {
+ tbss = s
+ break
+ }
+ }
+ s = xfile.addSection(".tbss", tbss.Vaddr, tbss.Length, 0, STYP_TBSS)
+ }
+
+ // add dwarf sections
+ for _, sect := range Segdwarf.Sections {
+ xfile.addDwarfSection(sect)
+ }
+
+ // add and write remaining sections
+ if ctxt.LinkMode == LinkInternal {
+ // Loader section
+ if ctxt.BuildMode == BuildModeExe {
+ Loaderblk(ctxt, uint64(fileoff))
+ s = xfile.addSection(".loader", 0, xfile.loaderSize, uint64(fileoff), STYP_LOADER)
+ xfile.xahdr.Osnloader = xfile.sectNameToScnum[".loader"]
+
+ // Update fileoff for symbol table
+ fileoff += int64(xfile.loaderSize)
+ }
+ }
+
+ // Create Symbol table
+ xfile.asmaixsym(ctxt)
+
+ if ctxt.LinkMode == LinkExternal {
+ xfile.emitRelocations(ctxt, fileoff)
+ }
+
+ // Write Symbol table
+ xfile.symtabOffset = ctxt.Out.Offset()
+ for _, s := range xfile.symtabSym {
+ binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)
+ }
+ // write string table
+ xfile.stringTable.write(ctxt.Out)
+
+ // write headers
+ xcoffwrite(ctxt)
+}
+
+// emitRelocations emits relocation entries for go.o in external linking.
+func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) {
+ ctxt.Out.SeekSet(fileoff)
+ for ctxt.Out.Offset()&7 != 0 {
+ ctxt.Out.Write8(0)
+ }
+
+ ldr := ctxt.loader
+ // relocsect relocates symbols from first in section sect, and returns
+ // the total number of relocations emitted.
+ relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) uint32 {
+ // ctxt.Logf("%s 0x%x\n", sect.Name, sect.Vaddr)
+ // If main section has no bits, nothing to relocate.
+ if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
+ return 0
+ }
+ sect.Reloff = uint64(ctxt.Out.Offset())
+ for i, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if uint64(ldr.SymValue(s)) >= sect.Vaddr {
+ syms = syms[i:]
+ break
+ }
+ }
+ eaddr := int64(sect.Vaddr + sect.Length)
+ for _, s := range syms {
+ if !ldr.AttrReachable(s) {
+ continue
+ }
+ if ldr.SymValue(s) >= int64(eaddr) {
+ break
+ }
+
+ // Compute external relocations on the go, and pass to Xcoffreloc1 to stream out.
+ // Relocation must be ordered by address, so create a list of sorted indices.
+ relocs := ldr.Relocs(s)
+ sorted := make([]int, relocs.Count())
+ for i := 0; i < relocs.Count(); i++ {
+ sorted[i] = i
+ }
+ sort.Slice(sorted, func(i, j int) bool {
+ return relocs.At(sorted[i]).Off() < relocs.At(sorted[j]).Off()
+ })
+
+ for _, ri := range sorted {
+ r := relocs.At(ri)
+ rr, ok := extreloc(ctxt, ldr, s, r)
+ if !ok {
+ continue
+ }
+ if rr.Xsym == 0 {
+ ldr.Errorf(s, "missing xsym in relocation")
+ continue
+ }
+ if ldr.SymDynid(rr.Xsym) < 0 {
+ ldr.Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymDynid(rr.Xsym))
+ }
+ if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) {
+ ldr.Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type(), r.Type(), r.Siz(), ldr.SymName(r.Sym()))
+ }
+ }
+ }
+ sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
+ return uint32(sect.Rellen) / RELSZ_64
+ }
+ sects := []struct {
+ xcoffSect *XcoffScnHdr64
+ segs []*sym.Segment
+ }{
+ {f.sectText, []*sym.Segment{&Segtext}},
+ {f.sectData, []*sym.Segment{&Segrelrodata, &Segdata}},
+ }
+ for _, s := range sects {
+ s.xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
+ n := uint32(0)
+ for _, seg := range s.segs {
+ for _, sect := range seg.Sections {
+ if sect.Name == ".text" {
+ n += relocsect(sect, ctxt.Textp, 0)
+ } else {
+ n += relocsect(sect, ctxt.datap, 0)
+ }
+ }
+ }
+ s.xcoffSect.Snreloc += n
+ }
+
+dwarfLoop:
+ for i := 0; i < len(Segdwarf.Sections); i++ {
+ sect := Segdwarf.Sections[i]
+ si := dwarfp[i]
+ if si.secSym() != loader.Sym(sect.Sym) ||
+ ldr.SymSect(si.secSym()) != sect {
+ panic("inconsistency between dwarfp and Segdwarf")
+ }
+ for _, xcoffSect := range f.sections {
+ _, subtyp := xcoffGetDwarfSubtype(sect.Name)
+ if xcoffSect.Sflags&0xF0000 == subtyp {
+ xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
+ xcoffSect.Snreloc = relocsect(sect, si.syms, sect.Vaddr)
+ continue dwarfLoop
+ }
+ }
+ Errorf(nil, "emitRelocations: could not find %q section", sect.Name)
+ }
+}
+
+// xcoffCreateExportFile creates a file with exported symbols for
+// -Wl,-bE option.
+// ld won't export symbols unless they are listed in an export file.
+func xcoffCreateExportFile(ctxt *Link) (fname string) {
+ fname = filepath.Join(*flagTmpdir, "export_file.exp")
+ var buf bytes.Buffer
+
+ ldr := ctxt.loader
+ for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ {
+ if !ldr.AttrCgoExport(s) {
+ continue
+ }
+ extname := ldr.SymExtname(s)
+ if !strings.HasPrefix(extname, "._cgoexp_") {
+ continue
+ }
+ if ldr.IsFileLocal(s) {
+ continue // Only export non-static symbols
+ }
+
+ // Retrieve the name of the initial symbol
+ // exported by cgo.
+ // The corresponding Go symbol is:
+ // _cgoexp_hashcode_symname.
+ name := strings.SplitN(extname, "_", 4)[3]
+
+ buf.Write([]byte(name + "\n"))
+ }
+
+ err := os.WriteFile(fname, buf.Bytes(), 0666)
+ if err != nil {
+ Errorf(nil, "WriteFile %s failed: %v", fname, err)
+ }
+
+ return fname
+}
diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go
new file mode 100644
index 0000000..7ac7699
--- /dev/null
+++ b/src/cmd/link/internal/loadelf/ldelf.go
@@ -0,0 +1,1151 @@
+// Copyright 2019 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.
+
+// Package loadelf implements an ELF file reader.
+package loadelf
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "log"
+ "strings"
+)
+
+/*
+Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
+https://github.com/9fans/plan9port/tree/master/src/libmach/
+
+ Copyright © 2004 Russ Cox.
+ Portions Copyright © 2008-2010 Google Inc.
+ Portions Copyright © 2010 The Go Authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+const (
+ SHT_ARM_ATTRIBUTES = 0x70000003
+)
+
+type ElfSect struct {
+ name string
+ nameoff uint32
+ type_ elf.SectionType
+ flags elf.SectionFlag
+ addr uint64
+ off uint64
+ size uint64
+ link uint32
+ info uint32
+ align uint64
+ entsize uint64
+ base []byte
+ readOnlyMem bool // Is this section in readonly memory?
+ sym loader.Sym
+}
+
+type ElfObj struct {
+ f *bio.Reader
+ base int64 // offset in f where ELF begins
+ length int64 // length of ELF
+ is64 int
+ name string
+ e binary.ByteOrder
+ sect []ElfSect
+ nsect uint
+ nsymtab int
+ symtab *ElfSect
+ symstr *ElfSect
+ type_ uint32
+ machine uint32
+ version uint32
+ entry uint64
+ phoff uint64
+ shoff uint64
+ flags uint32
+ ehsize uint32
+ phentsize uint32
+ phnum uint32
+ shentsize uint32
+ shnum uint32
+ shstrndx uint32
+}
+
+type ElfSym struct {
+ name string
+ value uint64
+ size uint64
+ bind elf.SymBind
+ type_ elf.SymType
+ other uint8
+ shndx elf.SectionIndex
+ sym loader.Sym
+}
+
+const (
+ TagFile = 1
+ TagCPUName = 4
+ TagCPURawName = 5
+ TagCompatibility = 32
+ TagNoDefaults = 64
+ TagAlsoCompatibleWith = 65
+ TagABIVFPArgs = 28
+)
+
+type elfAttribute struct {
+ tag uint64
+ sval string
+ ival uint64
+}
+
+type elfAttributeList struct {
+ data []byte
+ err error
+}
+
+func (a *elfAttributeList) string() string {
+ if a.err != nil {
+ return ""
+ }
+ nul := bytes.IndexByte(a.data, 0)
+ if nul < 0 {
+ a.err = io.EOF
+ return ""
+ }
+ s := string(a.data[:nul])
+ a.data = a.data[nul+1:]
+ return s
+}
+
+func (a *elfAttributeList) uleb128() uint64 {
+ if a.err != nil {
+ return 0
+ }
+ v, size := binary.Uvarint(a.data)
+ a.data = a.data[size:]
+ return v
+}
+
+// Read an elfAttribute from the list following the rules used on ARM systems.
+func (a *elfAttributeList) armAttr() elfAttribute {
+ attr := elfAttribute{tag: a.uleb128()}
+ switch {
+ case attr.tag == TagCompatibility:
+ attr.ival = a.uleb128()
+ attr.sval = a.string()
+
+ case attr.tag == TagNoDefaults: // Tag_nodefaults has no argument
+
+ case attr.tag == TagAlsoCompatibleWith:
+ // Not really, but we don't actually care about this tag.
+ attr.sval = a.string()
+
+ // Tag with string argument
+ case attr.tag == TagCPUName || attr.tag == TagCPURawName || (attr.tag >= 32 && attr.tag&1 != 0):
+ attr.sval = a.string()
+
+ default: // Tag with integer argument
+ attr.ival = a.uleb128()
+ }
+ return attr
+}
+
+func (a *elfAttributeList) done() bool {
+ if a.err != nil || len(a.data) == 0 {
+ return true
+ }
+ return false
+}
+
+// Look for the attribute that indicates the object uses the hard-float ABI (a
+// file-level attribute with tag Tag_VFP_arch and value 1). Unfortunately the
+// format used means that we have to parse all of the file-level attributes to
+// find the one we are looking for. This format is slightly documented in "ELF
+// for the ARM Architecture" but mostly this is derived from reading the source
+// to gold and readelf.
+func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags uint32, err error) {
+ found = false
+ if data[0] != 'A' {
+ return false, 0, fmt.Errorf(".ARM.attributes has unexpected format %c\n", data[0])
+ }
+ data = data[1:]
+ for len(data) != 0 {
+ sectionlength := e.Uint32(data)
+ sectiondata := data[4:sectionlength]
+ data = data[sectionlength:]
+
+ nulIndex := bytes.IndexByte(sectiondata, 0)
+ if nulIndex < 0 {
+ return false, 0, fmt.Errorf("corrupt .ARM.attributes (section name not NUL-terminated)\n")
+ }
+ name := string(sectiondata[:nulIndex])
+ sectiondata = sectiondata[nulIndex+1:]
+
+ if name != "aeabi" {
+ continue
+ }
+ for len(sectiondata) != 0 {
+ subsectiontag, sz := binary.Uvarint(sectiondata)
+ subsectionsize := e.Uint32(sectiondata[sz:])
+ subsectiondata := sectiondata[sz+4 : subsectionsize]
+ sectiondata = sectiondata[subsectionsize:]
+
+ if subsectiontag != TagFile {
+ continue
+ }
+ attrList := elfAttributeList{data: subsectiondata}
+ for !attrList.done() {
+ attr := attrList.armAttr()
+ if attr.tag == TagABIVFPArgs && attr.ival == 1 {
+ found = true
+ ehdrFlags = 0x5000402 // has entry point, Version5 EABI, hard-float ABI
+ }
+ }
+ if attrList.err != nil {
+ return false, 0, fmt.Errorf("could not parse .ARM.attributes\n")
+ }
+ }
+ }
+ return found, ehdrFlags, nil
+}
+
+// Load loads the ELF file pn from f.
+// Symbols are installed into the loader, and a slice of the text symbols is returned.
+//
+// On ARM systems, Load will attempt to determine what ELF header flags to
+// emit by scanning the attributes in the ELF file being loaded. The
+// parameter initEhdrFlags contains the current header flags for the output
+// object, and the returned ehdrFlags contains what this Load function computes.
+// TODO: find a better place for this logic.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []loader.Sym, ehdrFlags uint32, err error) {
+ newSym := func(name string, version int) loader.Sym {
+ return l.CreateStaticSym(name)
+ }
+ lookup := l.LookupOrCreateCgoExport
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) {
+ return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
+ }
+
+ ehdrFlags = initEhdrFlags
+
+ base := f.Offset()
+
+ var hdrbuf [64]byte
+ if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+
+ var e binary.ByteOrder
+ switch elf.Data(hdrbuf[elf.EI_DATA]) {
+ case elf.ELFDATA2LSB:
+ e = binary.LittleEndian
+
+ case elf.ELFDATA2MSB:
+ e = binary.BigEndian
+
+ default:
+ return errorf("malformed elf file, unknown header")
+ }
+
+ hdr := new(elf.Header32)
+ binary.Read(bytes.NewReader(hdrbuf[:]), e, hdr)
+
+ if string(hdr.Ident[:elf.EI_CLASS]) != elf.ELFMAG {
+ return errorf("malformed elf file, bad header")
+ }
+
+ // read header
+ elfobj := new(ElfObj)
+
+ elfobj.e = e
+ elfobj.f = f
+ elfobj.base = base
+ elfobj.length = length
+ elfobj.name = pn
+
+ is64 := 0
+ class := elf.Class(hdrbuf[elf.EI_CLASS])
+ if class == elf.ELFCLASS64 {
+ is64 = 1
+ hdr := new(elf.Header64)
+ binary.Read(bytes.NewReader(hdrbuf[:]), e, hdr)
+ elfobj.type_ = uint32(hdr.Type)
+ elfobj.machine = uint32(hdr.Machine)
+ elfobj.version = hdr.Version
+ elfobj.entry = hdr.Entry
+ elfobj.phoff = hdr.Phoff
+ elfobj.shoff = hdr.Shoff
+ elfobj.flags = hdr.Flags
+ elfobj.ehsize = uint32(hdr.Ehsize)
+ elfobj.phentsize = uint32(hdr.Phentsize)
+ elfobj.phnum = uint32(hdr.Phnum)
+ elfobj.shentsize = uint32(hdr.Shentsize)
+ elfobj.shnum = uint32(hdr.Shnum)
+ elfobj.shstrndx = uint32(hdr.Shstrndx)
+ } else {
+ elfobj.type_ = uint32(hdr.Type)
+ elfobj.machine = uint32(hdr.Machine)
+ elfobj.version = hdr.Version
+ elfobj.entry = uint64(hdr.Entry)
+ elfobj.phoff = uint64(hdr.Phoff)
+ elfobj.shoff = uint64(hdr.Shoff)
+ elfobj.flags = hdr.Flags
+ elfobj.ehsize = uint32(hdr.Ehsize)
+ elfobj.phentsize = uint32(hdr.Phentsize)
+ elfobj.phnum = uint32(hdr.Phnum)
+ elfobj.shentsize = uint32(hdr.Shentsize)
+ elfobj.shnum = uint32(hdr.Shnum)
+ elfobj.shstrndx = uint32(hdr.Shstrndx)
+ }
+
+ elfobj.is64 = is64
+
+ if v := uint32(hdrbuf[elf.EI_VERSION]); v != elfobj.version {
+ return errorf("malformed elf version: got %d, want %d", v, elfobj.version)
+ }
+
+ if elf.Type(elfobj.type_) != elf.ET_REL {
+ return errorf("elf but not elf relocatable object")
+ }
+
+ mach := elf.Machine(elfobj.machine)
+ switch arch.Family {
+ default:
+ return errorf("elf %s unimplemented", arch.Name)
+
+ case sys.MIPS:
+ if mach != elf.EM_MIPS || class != elf.ELFCLASS32 {
+ return errorf("elf object but not mips")
+ }
+
+ case sys.MIPS64:
+ if mach != elf.EM_MIPS || class != elf.ELFCLASS64 {
+ return errorf("elf object but not mips64")
+ }
+ case sys.Loong64:
+ if mach != elf.EM_LOONGARCH || class != elf.ELFCLASS64 {
+ return errorf("elf object but not loong64")
+ }
+
+ case sys.ARM:
+ if e != binary.LittleEndian || mach != elf.EM_ARM || class != elf.ELFCLASS32 {
+ return errorf("elf object but not arm")
+ }
+
+ case sys.AMD64:
+ if e != binary.LittleEndian || mach != elf.EM_X86_64 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not amd64")
+ }
+
+ case sys.ARM64:
+ if e != binary.LittleEndian || mach != elf.EM_AARCH64 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not arm64")
+ }
+
+ case sys.I386:
+ if e != binary.LittleEndian || mach != elf.EM_386 || class != elf.ELFCLASS32 {
+ return errorf("elf object but not 386")
+ }
+
+ case sys.PPC64:
+ if mach != elf.EM_PPC64 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not ppc64")
+ }
+
+ case sys.RISCV64:
+ if mach != elf.EM_RISCV || class != elf.ELFCLASS64 {
+ return errorf("elf object but not riscv64")
+ }
+
+ case sys.S390X:
+ if mach != elf.EM_S390 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not s390x")
+ }
+ }
+
+ // load section list into memory.
+ elfobj.sect = make([]ElfSect, elfobj.shnum)
+
+ elfobj.nsect = uint(elfobj.shnum)
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ f.MustSeek(int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0)
+ sect := &elfobj.sect[i]
+ if is64 != 0 {
+ var b elf.Section64
+ if err := binary.Read(f, e, &b); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+
+ sect.nameoff = b.Name
+ sect.type_ = elf.SectionType(b.Type)
+ sect.flags = elf.SectionFlag(b.Flags)
+ sect.addr = b.Addr
+ sect.off = b.Off
+ sect.size = b.Size
+ sect.link = b.Link
+ sect.info = b.Info
+ sect.align = b.Addralign
+ sect.entsize = b.Entsize
+ } else {
+ var b elf.Section32
+
+ if err := binary.Read(f, e, &b); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ sect.nameoff = b.Name
+ sect.type_ = elf.SectionType(b.Type)
+ sect.flags = elf.SectionFlag(b.Flags)
+ sect.addr = uint64(b.Addr)
+ sect.off = uint64(b.Off)
+ sect.size = uint64(b.Size)
+ sect.link = b.Link
+ sect.info = b.Info
+ sect.align = uint64(b.Addralign)
+ sect.entsize = uint64(b.Entsize)
+ }
+ }
+
+ // read section string table and translate names
+ if elfobj.shstrndx >= uint32(elfobj.nsect) {
+ return errorf("malformed elf file: shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect)
+ }
+
+ sect := &elfobj.sect[elfobj.shstrndx]
+ if err := elfmap(elfobj, sect); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ if elfobj.sect[i].nameoff != 0 {
+ elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:])
+ }
+ }
+
+ // load string table for symbols into memory.
+ elfobj.symtab = section(elfobj, ".symtab")
+
+ if elfobj.symtab == nil {
+ // our work is done here - no symbols means nothing can refer to this file
+ return
+ }
+
+ if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) {
+ return errorf("elf object has symbol table with invalid string table link")
+ }
+
+ elfobj.symstr = &elfobj.sect[elfobj.symtab.link]
+ if is64 != 0 {
+ elfobj.nsymtab = int(elfobj.symtab.size / elf.Sym64Size)
+ } else {
+ elfobj.nsymtab = int(elfobj.symtab.size / elf.Sym32Size)
+ }
+
+ if err := elfmap(elfobj, elfobj.symtab); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ if err := elfmap(elfobj, elfobj.symstr); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+
+ // load text and data segments into memory.
+ // they are not as small as the section lists, but we'll need
+ // the memory anyway for the symbol images, so we might
+ // as well use one large chunk.
+
+ // create symbols for elfmapped sections
+ sectsymNames := make(map[string]bool)
+ counter := 0
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ sect = &elfobj.sect[i]
+ if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" {
+ if err := elfmap(elfobj, sect); err != nil {
+ return errorf("%s: malformed elf file: %v", pn, err)
+ }
+ // We assume the soft-float ABI unless we see a tag indicating otherwise.
+ if initEhdrFlags == 0x5000002 {
+ ehdrFlags = 0x5000202
+ } else {
+ ehdrFlags = initEhdrFlags
+ }
+ found, newEhdrFlags, err := parseArmAttributes(e, sect.base[:sect.size])
+ if err != nil {
+ // TODO(dfc) should this return an error?
+ log.Printf("%s: %v", pn, err)
+ }
+ if found {
+ ehdrFlags = newEhdrFlags
+ }
+ }
+ if (sect.type_ != elf.SHT_PROGBITS && sect.type_ != elf.SHT_NOBITS) || sect.flags&elf.SHF_ALLOC == 0 {
+ continue
+ }
+ if sect.type_ != elf.SHT_NOBITS {
+ if err := elfmap(elfobj, sect); err != nil {
+ return errorf("%s: malformed elf file: %v", pn, err)
+ }
+ }
+
+ name := fmt.Sprintf("%s(%s)", pkg, sect.name)
+ for sectsymNames[name] {
+ counter++
+ name = fmt.Sprintf("%s(%s%d)", pkg, sect.name, counter)
+ }
+ sectsymNames[name] = true
+
+ sb := l.MakeSymbolUpdater(lookup(name, localSymVersion))
+
+ switch sect.flags & (elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_EXECINSTR) {
+ default:
+ return errorf("%s: unexpected flags for ELF section %s", pn, sect.name)
+
+ case elf.SHF_ALLOC:
+ sb.SetType(sym.SRODATA)
+
+ case elf.SHF_ALLOC + elf.SHF_WRITE:
+ if sect.type_ == elf.SHT_NOBITS {
+ sb.SetType(sym.SNOPTRBSS)
+ } else {
+ sb.SetType(sym.SNOPTRDATA)
+ }
+
+ case elf.SHF_ALLOC + elf.SHF_EXECINSTR:
+ sb.SetType(sym.STEXT)
+ }
+
+ if sect.name == ".got" || sect.name == ".toc" {
+ sb.SetType(sym.SELFGOT)
+ }
+ if sect.type_ == elf.SHT_PROGBITS {
+ sb.SetData(sect.base[:sect.size])
+ }
+
+ sb.SetSize(int64(sect.size))
+ sb.SetAlign(int32(sect.align))
+ sb.SetReadOnly(sect.readOnlyMem)
+
+ sect.sym = sb.Sym()
+ }
+
+ // enter sub-symbols into symbol table.
+ // symbol 0 is the null symbol.
+ symbols := make([]loader.Sym, elfobj.nsymtab)
+
+ for i := 1; i < elfobj.nsymtab; i++ {
+ var elfsym ElfSym
+ if err := readelfsym(newSym, lookup, l, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
+ return errorf("%s: malformed elf file: %v", pn, err)
+ }
+ symbols[i] = elfsym.sym
+ if elfsym.type_ != elf.STT_FUNC && elfsym.type_ != elf.STT_OBJECT && elfsym.type_ != elf.STT_NOTYPE && elfsym.type_ != elf.STT_COMMON {
+ continue
+ }
+ if elfsym.shndx == elf.SHN_COMMON || elfsym.type_ == elf.STT_COMMON {
+ sb := l.MakeSymbolUpdater(elfsym.sym)
+ if uint64(sb.Size()) < elfsym.size {
+ sb.SetSize(int64(elfsym.size))
+ }
+ if sb.Type() == 0 || sb.Type() == sym.SXREF {
+ sb.SetType(sym.SNOPTRBSS)
+ }
+ continue
+ }
+
+ if uint(elfsym.shndx) >= elfobj.nsect || elfsym.shndx == 0 {
+ continue
+ }
+
+ // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols
+ if elfsym.sym == 0 {
+ continue
+ }
+ sect = &elfobj.sect[elfsym.shndx]
+ if sect.sym == 0 {
+ if strings.HasPrefix(elfsym.name, ".Linfo_string") { // clang does this
+ continue
+ }
+
+ if elfsym.name == "" && elfsym.type_ == 0 && sect.name == ".debug_str" {
+ // This reportedly happens with clang 3.7 on ARM.
+ // See issue 13139.
+ continue
+ }
+
+ if strings.HasPrefix(elfsym.name, "$d") && elfsym.type_ == 0 && sect.name == ".debug_frame" {
+ // "$d" is a marker, not a real symbol.
+ // This happens with gcc on ARM64.
+ // See https://sourceware.org/bugzilla/show_bug.cgi?id=21809
+ continue
+ }
+
+ if strings.HasPrefix(elfsym.name, ".LASF") { // gcc on s390x does this
+ continue
+ }
+ return errorf("%v: sym#%d (%s): ignoring symbol in section %d (type %d)", elfsym.sym, i, elfsym.name, elfsym.shndx, elfsym.type_)
+ }
+
+ s := elfsym.sym
+ if l.OuterSym(s) != 0 {
+ if l.AttrDuplicateOK(s) {
+ continue
+ }
+ return errorf("duplicate symbol reference: %s in both %s and %s",
+ l.SymName(s), l.SymName(l.OuterSym(s)), l.SymName(sect.sym))
+ }
+
+ sectsb := l.MakeSymbolUpdater(sect.sym)
+ sb := l.MakeSymbolUpdater(s)
+
+ sb.SetType(sectsb.Type())
+ sectsb.AddInteriorSym(s)
+ if !l.AttrCgoExportDynamic(s) {
+ sb.SetDynimplib("") // satisfy dynimport
+ }
+ sb.SetValue(int64(elfsym.value))
+ sb.SetSize(int64(elfsym.size))
+ if sectsb.Type() == sym.STEXT {
+ if l.AttrExternal(s) && !l.AttrDuplicateOK(s) {
+ return errorf("%s: duplicate symbol definition", sb.Name())
+ }
+ l.SetAttrExternal(s, true)
+ }
+
+ if elf.Machine(elfobj.machine) == elf.EM_PPC64 {
+ flag := int(elfsym.other) >> 5
+ switch flag {
+ case 0:
+ // No local entry. R2 is preserved.
+ case 1:
+ // These require R2 be saved and restored by the caller. This isn't supported today.
+ return errorf("%s: unable to handle local entry type 1", sb.Name())
+ case 7:
+ return errorf("%s: invalid sym.other 0x%x", sb.Name(), elfsym.other)
+ default:
+ l.SetSymLocalentry(s, 4<<uint(flag-2))
+ }
+ }
+ }
+
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for i := uint(0); i < elfobj.nsect; i++ {
+ s := elfobj.sect[i].sym
+ if s == 0 {
+ continue
+ }
+ sb := l.MakeSymbolUpdater(s)
+ if l.SubSym(s) != 0 {
+ sb.SortSub()
+ }
+ if sb.Type() == sym.STEXT {
+ if l.AttrOnList(s) {
+ return errorf("symbol %s listed multiple times",
+ l.SymName(s))
+ }
+ l.SetAttrOnList(s, true)
+ textp = append(textp, s)
+ for ss := l.SubSym(s); ss != 0; ss = l.SubSym(ss) {
+ if l.AttrOnList(ss) {
+ return errorf("symbol %s listed multiple times",
+ l.SymName(ss))
+ }
+ l.SetAttrOnList(ss, true)
+ textp = append(textp, ss)
+ }
+ }
+ }
+
+ // load relocations
+ for i := uint(0); i < elfobj.nsect; i++ {
+ rsect := &elfobj.sect[i]
+ if rsect.type_ != elf.SHT_RELA && rsect.type_ != elf.SHT_REL {
+ continue
+ }
+ if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil {
+ continue
+ }
+ sect = &elfobj.sect[rsect.info]
+ if err := elfmap(elfobj, rsect); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ rela := 0
+ if rsect.type_ == elf.SHT_RELA {
+ rela = 1
+ }
+ n := int(rsect.size / uint64(4+4*is64) / uint64(2+rela))
+ p := rsect.base
+ sb := l.MakeSymbolUpdater(sect.sym)
+ for j := 0; j < n; j++ {
+ var add uint64
+ var symIdx int
+ var relocType uint64
+ var rOff int32
+ var rAdd int64
+ var rSym loader.Sym
+
+ if is64 != 0 {
+ // 64-bit rel/rela
+ rOff = int32(e.Uint64(p))
+
+ p = p[8:]
+ switch arch.Family {
+ case sys.MIPS64:
+ // https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf
+ // The doc shows it's different with general Linux ELF
+ symIdx = int(e.Uint32(p))
+ relocType = uint64(p[7])
+ default:
+ info := e.Uint64(p)
+ relocType = info & 0xffffffff
+ symIdx = int(info >> 32)
+ }
+ p = p[8:]
+ if rela != 0 {
+ add = e.Uint64(p)
+ p = p[8:]
+ }
+ } else {
+ // 32-bit rel/rela
+ rOff = int32(e.Uint32(p))
+
+ p = p[4:]
+ info := e.Uint32(p)
+ relocType = uint64(info & 0xff)
+ symIdx = int(info >> 8)
+ p = p[4:]
+ if rela != 0 {
+ add = uint64(e.Uint32(p))
+ p = p[4:]
+ }
+ }
+
+ if relocType == 0 { // skip R_*_NONE relocation
+ j--
+ n--
+ continue
+ }
+
+ if symIdx == 0 { // absolute relocation, don't bother reading the null symbol
+ rSym = 0
+ } else {
+ var elfsym ElfSym
+ if err := readelfsym(newSym, lookup, l, arch, elfobj, int(symIdx), &elfsym, 0, 0); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ elfsym.sym = symbols[symIdx]
+ if elfsym.sym == 0 {
+ return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", l.SymName(sect.sym), j, int(symIdx), elfsym.name, elfsym.shndx, elfsym.type_)
+ }
+
+ rSym = elfsym.sym
+ }
+
+ rType := objabi.ElfRelocOffset + objabi.RelocType(relocType)
+ rSize, addendSize, err := relSize(arch, pn, uint32(relocType))
+ if err != nil {
+ return nil, 0, err
+ }
+ if rela != 0 {
+ rAdd = int64(add)
+ } else {
+ // load addend from image
+ if rSize == 4 {
+ rAdd = int64(e.Uint32(sect.base[rOff:]))
+ } else if rSize == 8 {
+ rAdd = int64(e.Uint64(sect.base[rOff:]))
+ } else {
+ return errorf("invalid rela size %d", rSize)
+ }
+ }
+
+ if addendSize == 2 {
+ rAdd = int64(int16(rAdd))
+ }
+ if addendSize == 4 {
+ rAdd = int64(int32(rAdd))
+ }
+
+ r, _ := sb.AddRel(rType)
+ r.SetOff(rOff)
+ r.SetSiz(rSize)
+ r.SetSym(rSym)
+ r.SetAdd(rAdd)
+ }
+
+ sb.SortRelocs() // just in case
+ }
+
+ return textp, ehdrFlags, nil
+}
+
+func section(elfobj *ElfObj, name string) *ElfSect {
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ if elfobj.sect[i].name != "" && name != "" && elfobj.sect[i].name == name {
+ return &elfobj.sect[i]
+ }
+ }
+ return nil
+}
+
+func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
+ if sect.base != nil {
+ return nil
+ }
+
+ if sect.off+sect.size > uint64(elfobj.length) {
+ err = fmt.Errorf("elf section past end of file")
+ return err
+ }
+
+ elfobj.f.MustSeek(int64(uint64(elfobj.base)+sect.off), 0)
+ sect.base, sect.readOnlyMem, err = elfobj.f.Slice(uint64(sect.size))
+ if err != nil {
+ return fmt.Errorf("short read: %v", err)
+ }
+
+ return nil
+}
+
+func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
+ if i >= elfobj.nsymtab || i < 0 {
+ err = fmt.Errorf("invalid elf symbol index")
+ return err
+ }
+
+ if i == 0 {
+ return fmt.Errorf("readym: read null symbol!")
+ }
+
+ if elfobj.is64 != 0 {
+ b := new(elf.Sym64)
+ binary.Read(bytes.NewReader(elfobj.symtab.base[i*elf.Sym64Size:(i+1)*elf.Sym64Size]), elfobj.e, b)
+ elfsym.name = cstring(elfobj.symstr.base[b.Name:])
+ elfsym.value = b.Value
+ elfsym.size = b.Size
+ elfsym.shndx = elf.SectionIndex(b.Shndx)
+ elfsym.bind = elf.ST_BIND(b.Info)
+ elfsym.type_ = elf.ST_TYPE(b.Info)
+ elfsym.other = b.Other
+ } else {
+ b := new(elf.Sym32)
+ binary.Read(bytes.NewReader(elfobj.symtab.base[i*elf.Sym32Size:(i+1)*elf.Sym32Size]), elfobj.e, b)
+ elfsym.name = cstring(elfobj.symstr.base[b.Name:])
+ elfsym.value = uint64(b.Value)
+ elfsym.size = uint64(b.Size)
+ elfsym.shndx = elf.SectionIndex(b.Shndx)
+ elfsym.bind = elf.ST_BIND(b.Info)
+ elfsym.type_ = elf.ST_TYPE(b.Info)
+ elfsym.other = b.Other
+ }
+
+ var s loader.Sym
+
+ if elfsym.name == "_GLOBAL_OFFSET_TABLE_" {
+ elfsym.name = ".got"
+ }
+ if elfsym.name == ".TOC." {
+ // Magic symbol on ppc64. Will be set to this object
+ // file's .got+0x8000.
+ elfsym.bind = elf.STB_LOCAL
+ }
+
+ switch elfsym.type_ {
+ case elf.STT_SECTION:
+ s = elfobj.sect[elfsym.shndx].sym
+
+ case elf.STT_OBJECT, elf.STT_FUNC, elf.STT_NOTYPE, elf.STT_COMMON:
+ switch elfsym.bind {
+ case elf.STB_GLOBAL:
+ if needSym != 0 {
+ s = lookup(elfsym.name, 0)
+
+ // for global scoped hidden symbols we should insert it into
+ // symbol hash table, but mark them as hidden.
+ // __i686.get_pc_thunk.bx is allowed to be duplicated, to
+ // workaround that we set dupok.
+ // TODO(minux): correctly handle __i686.get_pc_thunk.bx without
+ // set dupok generally. See https://golang.org/cl/5823055
+ // comment #5 for details.
+ if s != 0 && elfsym.other == 2 {
+ if !l.IsExternal(s) {
+ l.MakeSymbolUpdater(s)
+ }
+ l.SetAttrDuplicateOK(s, true)
+ l.SetAttrVisibilityHidden(s, true)
+ }
+ }
+
+ case elf.STB_LOCAL:
+ if (arch.Family == sys.ARM || arch.Family == sys.ARM64) && (strings.HasPrefix(elfsym.name, "$a") || strings.HasPrefix(elfsym.name, "$d") || strings.HasPrefix(elfsym.name, "$x")) {
+ // binutils for arm and arm64 generate these mapping
+ // symbols, ignore these
+ break
+ }
+
+ if elfsym.name == ".TOC." {
+ // We need to be able to look this up,
+ // so put it in the hash table.
+ if needSym != 0 {
+ s = lookup(elfsym.name, localSymVersion)
+ l.SetAttrVisibilityHidden(s, true)
+ }
+ break
+ }
+
+ if needSym != 0 {
+ // local names and hidden global names are unique
+ // and should only be referenced by their index, not name, so we
+ // don't bother to add them into the hash table
+ // FIXME: pass empty string here for name? This would
+ // reduce mem use, but also (possibly) make it harder
+ // to debug problems.
+ s = newSym(elfsym.name, localSymVersion)
+ l.SetAttrVisibilityHidden(s, true)
+ }
+
+ case elf.STB_WEAK:
+ if needSym != 0 {
+ s = lookup(elfsym.name, 0)
+ if elfsym.other == 2 {
+ l.SetAttrVisibilityHidden(s, true)
+ }
+
+ // Allow weak symbols to be duplicated when already defined.
+ if l.OuterSym(s) != 0 {
+ l.SetAttrDuplicateOK(s, true)
+ }
+ }
+
+ default:
+ err = fmt.Errorf("%s: invalid symbol binding %d", elfsym.name, elfsym.bind)
+ return err
+ }
+ }
+
+ if s != 0 && l.SymType(s) == 0 && elfsym.type_ != elf.STT_SECTION {
+ sb := l.MakeSymbolUpdater(s)
+ sb.SetType(sym.SXREF)
+ }
+ elfsym.sym = s
+
+ return nil
+}
+
+// Return the size of the relocated field, and the size of the addend as the first
+// and second values. Note, the addend may be larger than the relocation field in
+// some cases when a relocated value is split across multiple relocations.
+func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, uint8, error) {
+ // TODO(mdempsky): Replace this with a struct-valued switch statement
+ // once golang.org/issue/15164 is fixed or found to not impair cmd/link
+ // performance.
+
+ const (
+ AMD64 = uint32(sys.AMD64)
+ ARM = uint32(sys.ARM)
+ ARM64 = uint32(sys.ARM64)
+ I386 = uint32(sys.I386)
+ LOONG64 = uint32(sys.Loong64)
+ MIPS = uint32(sys.MIPS)
+ MIPS64 = uint32(sys.MIPS64)
+ PPC64 = uint32(sys.PPC64)
+ RISCV64 = uint32(sys.RISCV64)
+ S390X = uint32(sys.S390X)
+ )
+
+ switch uint32(arch.Family) | elftype<<16 {
+ default:
+ return 0, 0, fmt.Errorf("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype)
+
+ case MIPS | uint32(elf.R_MIPS_HI16)<<16,
+ MIPS | uint32(elf.R_MIPS_LO16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_HI16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_LO16)<<16,
+ MIPS | uint32(elf.R_MIPS_GPREL16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_PAGE)<<16,
+ MIPS | uint32(elf.R_MIPS_JALR)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_OFST)<<16,
+ MIPS64 | uint32(elf.R_MIPS_HI16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_LO16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_HI16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_LO16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GPREL16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_PAGE)<<16,
+ MIPS64 | uint32(elf.R_MIPS_JALR)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_OFST)<<16,
+ MIPS64 | uint32(elf.R_MIPS_CALL16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GPREL32)<<16,
+ MIPS64 | uint32(elf.R_MIPS_64)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_DISP)<<16:
+ return 4, 4, nil
+
+ case LOONG64 | uint32(elf.R_LARCH_SOP_PUSH_PCREL)<<16,
+ LOONG64 | uint32(elf.R_LARCH_SOP_PUSH_GPREL)<<16,
+ LOONG64 | uint32(elf.R_LARCH_SOP_PUSH_ABSOLUTE)<<16,
+ LOONG64 | uint32(elf.R_LARCH_MARK_LA)<<16,
+ LOONG64 | uint32(elf.R_LARCH_SOP_POP_32_S_0_10_10_16_S2)<<16,
+ LOONG64 | uint32(elf.R_LARCH_64)<<16,
+ LOONG64 | uint32(elf.R_LARCH_MARK_PCREL)<<16,
+ LOONG64 | uint32(elf.R_LARCH_32_PCREL)<<16:
+ return 4, 4, nil
+
+ case S390X | uint32(elf.R_390_8)<<16:
+ return 1, 1, nil
+
+ case PPC64 | uint32(elf.R_PPC64_TOC16)<<16,
+ S390X | uint32(elf.R_390_16)<<16,
+ S390X | uint32(elf.R_390_GOT16)<<16,
+ S390X | uint32(elf.R_390_PC16)<<16,
+ S390X | uint32(elf.R_390_PC16DBL)<<16,
+ S390X | uint32(elf.R_390_PLT16DBL)<<16:
+ return 2, 2, nil
+
+ case ARM | uint32(elf.R_ARM_ABS32)<<16,
+ ARM | uint32(elf.R_ARM_GOT32)<<16,
+ ARM | uint32(elf.R_ARM_PLT32)<<16,
+ ARM | uint32(elf.R_ARM_GOTOFF)<<16,
+ ARM | uint32(elf.R_ARM_GOTPC)<<16,
+ ARM | uint32(elf.R_ARM_THM_PC22)<<16,
+ ARM | uint32(elf.R_ARM_REL32)<<16,
+ ARM | uint32(elf.R_ARM_CALL)<<16,
+ ARM | uint32(elf.R_ARM_V4BX)<<16,
+ ARM | uint32(elf.R_ARM_GOT_PREL)<<16,
+ ARM | uint32(elf.R_ARM_PC24)<<16,
+ ARM | uint32(elf.R_ARM_JUMP24)<<16,
+ ARM64 | uint32(elf.R_AARCH64_CALL26)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ADR_GOT_PAGE)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LD64_GOT_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ADR_PREL_PG_HI21)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ADD_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST8_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST16_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST32_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST64_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST128_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_PREL32)<<16,
+ ARM64 | uint32(elf.R_AARCH64_JUMP26)<<16,
+ AMD64 | uint32(elf.R_X86_64_PC32)<<16,
+ AMD64 | uint32(elf.R_X86_64_PLT32)<<16,
+ AMD64 | uint32(elf.R_X86_64_GOTPCREL)<<16,
+ AMD64 | uint32(elf.R_X86_64_GOTPCRELX)<<16,
+ AMD64 | uint32(elf.R_X86_64_REX_GOTPCRELX)<<16,
+ I386 | uint32(elf.R_386_32)<<16,
+ I386 | uint32(elf.R_386_PC32)<<16,
+ I386 | uint32(elf.R_386_GOT32)<<16,
+ I386 | uint32(elf.R_386_PLT32)<<16,
+ I386 | uint32(elf.R_386_GOTOFF)<<16,
+ I386 | uint32(elf.R_386_GOTPC)<<16,
+ I386 | uint32(elf.R_386_GOT32X)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL24)<<16,
+ PPC64 | uint32(elf.R_PPC_REL32)<<16,
+ S390X | uint32(elf.R_390_32)<<16,
+ S390X | uint32(elf.R_390_PC32)<<16,
+ S390X | uint32(elf.R_390_GOT32)<<16,
+ S390X | uint32(elf.R_390_PLT32)<<16,
+ S390X | uint32(elf.R_390_PC32DBL)<<16,
+ S390X | uint32(elf.R_390_PLT32DBL)<<16,
+ S390X | uint32(elf.R_390_GOTPCDBL)<<16,
+ S390X | uint32(elf.R_390_GOTENT)<<16:
+ return 4, 4, nil
+
+ case AMD64 | uint32(elf.R_X86_64_64)<<16,
+ AMD64 | uint32(elf.R_X86_64_PC64)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ABS64)<<16,
+ ARM64 | uint32(elf.R_AARCH64_PREL64)<<16,
+ PPC64 | uint32(elf.R_PPC64_ADDR64)<<16,
+ S390X | uint32(elf.R_390_GLOB_DAT)<<16,
+ S390X | uint32(elf.R_390_RELATIVE)<<16,
+ S390X | uint32(elf.R_390_GOTOFF)<<16,
+ S390X | uint32(elf.R_390_GOTPC)<<16,
+ S390X | uint32(elf.R_390_64)<<16,
+ S390X | uint32(elf.R_390_PC64)<<16,
+ S390X | uint32(elf.R_390_GOT64)<<16,
+ S390X | uint32(elf.R_390_PLT64)<<16:
+ return 8, 8, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_SET6)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SUB6)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SET8)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SUB8)<<16:
+ return 1, 1, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_RVC_BRANCH)<<16,
+ RISCV64 | uint32(elf.R_RISCV_RVC_JUMP)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SET16)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SUB16)<<16:
+ return 2, 2, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_32)<<16,
+ RISCV64 | uint32(elf.R_RISCV_BRANCH)<<16,
+ RISCV64 | uint32(elf.R_RISCV_HI20)<<16,
+ RISCV64 | uint32(elf.R_RISCV_LO12_I)<<16,
+ RISCV64 | uint32(elf.R_RISCV_LO12_S)<<16,
+ RISCV64 | uint32(elf.R_RISCV_GOT_HI20)<<16,
+ RISCV64 | uint32(elf.R_RISCV_PCREL_HI20)<<16,
+ RISCV64 | uint32(elf.R_RISCV_PCREL_LO12_I)<<16,
+ RISCV64 | uint32(elf.R_RISCV_PCREL_LO12_S)<<16,
+ RISCV64 | uint32(elf.R_RISCV_ADD32)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SET32)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SUB32)<<16,
+ RISCV64 | uint32(elf.R_RISCV_32_PCREL)<<16,
+ RISCV64 | uint32(elf.R_RISCV_RELAX)<<16:
+ return 4, 4, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_64)<<16,
+ RISCV64 | uint32(elf.R_RISCV_CALL)<<16,
+ RISCV64 | uint32(elf.R_RISCV_CALL_PLT)<<16:
+ return 8, 8, nil
+
+ case PPC64 | uint32(elf.R_PPC64_TOC16_LO)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_HI)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_HA)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_DS)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_LO_DS)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL16_LO)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL16_HI)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL16_HA)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLT16_HA)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLT16_LO_DS)<<16:
+ return 2, 4, nil
+
+ // PPC64 inline PLT sequence hint relocations (-fno-plt)
+ // These are informational annotations to assist linker optimizations.
+ case PPC64 | uint32(elf.R_PPC64_PLTSEQ)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLTCALL)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLTCALL_NOTOC)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLTSEQ_NOTOC)<<16:
+ return 0, 0, nil
+
+ }
+}
+
+func cstring(x []byte) string {
+ i := bytes.IndexByte(x, '\x00')
+ if i >= 0 {
+ x = x[:i]
+ }
+ return string(x)
+}
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
new file mode 100644
index 0000000..8e1575a
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader.go
@@ -0,0 +1,2601 @@
+// Copyright 2019 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.
+
+package loader
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "io"
+ "log"
+ "math/bits"
+ "os"
+ "sort"
+ "strings"
+)
+
+var _ = fmt.Print
+
+// Sym encapsulates a global symbol index, used to identify a specific
+// Go symbol. The 0-valued Sym is corresponds to an invalid symbol.
+type Sym int
+
+// Relocs encapsulates the set of relocations on a given symbol; an
+// instance of this type is returned by the Loader Relocs() method.
+type Relocs struct {
+ rs []goobj.Reloc
+
+ li uint32 // local index of symbol whose relocs we're examining
+ r *oReader // object reader for containing package
+ l *Loader // loader
+}
+
+// ExtReloc contains the payload for an external relocation.
+type ExtReloc struct {
+ Xsym Sym
+ Xadd int64
+ Type objabi.RelocType
+ Size uint8
+}
+
+// Reloc holds a "handle" to access a relocation record from an
+// object file.
+type Reloc struct {
+ *goobj.Reloc
+ r *oReader
+ l *Loader
+}
+
+func (rel Reloc) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc.Type()) &^ objabi.R_WEAK }
+func (rel Reloc) Weak() bool { return objabi.RelocType(rel.Reloc.Type())&objabi.R_WEAK != 0 }
+func (rel Reloc) SetType(t objabi.RelocType) { rel.Reloc.SetType(uint16(t)) }
+func (rel Reloc) Sym() Sym { return rel.l.resolve(rel.r, rel.Reloc.Sym()) }
+func (rel Reloc) SetSym(s Sym) { rel.Reloc.SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(s)}) }
+func (rel Reloc) IsMarker() bool { return rel.Siz() == 0 }
+
+// Aux holds a "handle" to access an aux symbol record from an
+// object file.
+type Aux struct {
+ *goobj.Aux
+ r *oReader
+ l *Loader
+}
+
+func (a Aux) Sym() Sym { return a.l.resolve(a.r, a.Aux.Sym()) }
+
+// oReader is a wrapper type of obj.Reader, along with some
+// extra information.
+type oReader struct {
+ *goobj.Reader
+ unit *sym.CompilationUnit
+ version int // version of static symbol
+ pkgprefix string
+ syms []Sym // Sym's global index, indexed by local index
+ pkg []uint32 // indices of referenced package by PkgIdx (index into loader.objs array)
+ ndef int // cache goobj.Reader.NSym()
+ nhashed64def int // cache goobj.Reader.NHashed64Def()
+ nhasheddef int // cache goobj.Reader.NHashedDef()
+ objidx uint32 // index of this reader in the objs slice
+}
+
+// Total number of defined symbols (package symbols, hashed symbols, and
+// non-package symbols).
+func (r *oReader) NAlldef() int { return r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef() }
+
+type objIdx struct {
+ r *oReader
+ i Sym // start index
+}
+
+// objSym represents a symbol in an object file. It is a tuple of
+// the object and the symbol's local index.
+// For external symbols, objidx is the index of l.extReader (extObj),
+// s is its index into the payload array.
+// {0, 0} represents the nil symbol.
+type objSym struct {
+ objidx uint32 // index of the object (in l.objs array)
+ s uint32 // local index
+}
+
+type nameVer struct {
+ name string
+ v int
+}
+
+type Bitmap []uint32
+
+// set the i-th bit.
+func (bm Bitmap) Set(i Sym) {
+ n, r := uint(i)/32, uint(i)%32
+ bm[n] |= 1 << r
+}
+
+// unset the i-th bit.
+func (bm Bitmap) Unset(i Sym) {
+ n, r := uint(i)/32, uint(i)%32
+ bm[n] &^= (1 << r)
+}
+
+// whether the i-th bit is set.
+func (bm Bitmap) Has(i Sym) bool {
+ n, r := uint(i)/32, uint(i)%32
+ return bm[n]&(1<<r) != 0
+}
+
+// return current length of bitmap in bits.
+func (bm Bitmap) Len() int {
+ return len(bm) * 32
+}
+
+// return the number of bits set.
+func (bm Bitmap) Count() int {
+ s := 0
+ for _, x := range bm {
+ s += bits.OnesCount32(x)
+ }
+ return s
+}
+
+func MakeBitmap(n int) Bitmap {
+ return make(Bitmap, (n+31)/32)
+}
+
+// growBitmap insures that the specified bitmap has enough capacity,
+// reallocating (doubling the size) if needed.
+func growBitmap(reqLen int, b Bitmap) Bitmap {
+ curLen := b.Len()
+ if reqLen > curLen {
+ b = append(b, MakeBitmap(reqLen+1-curLen)...)
+ }
+ return b
+}
+
+type symAndSize struct {
+ sym Sym
+ size uint32
+}
+
+// A Loader loads new object files and resolves indexed symbol references.
+//
+// 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.
+//
+// - 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.
+//
+// - 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)
+ extStart Sym // from this index on, the symbols are externally defined
+ builtinSyms []Sym // global index of builtin symbols
+
+ objSyms []objSym // global index mapping to local index
+
+ symsByName [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal
+ extStaticSyms map[nameVer]Sym // externally defined static symbols, keyed by name
+
+ extReader *oReader // a dummy oReader, for external symbols
+ payloadBatch []extSymPayload
+ payloads []*extSymPayload // contents of linker-materialized external syms
+ values []int64 // symbol values, indexed by global sym index
+
+ sects []*sym.Section // sections
+ symSects []uint16 // symbol's section, index to sects array
+
+ align []uint8 // symbol 2^N alignment, indexed by global index
+
+ deferReturnTramp map[Sym]bool // whether the symbol is a trampoline of a deferreturn call
+
+ objByPkg map[string]uint32 // map package path to the index of its Go object reader
+
+ anonVersion int // most recently assigned ext static sym pseudo-version
+
+ // Bitmaps and other side structures used to store data used to store
+ // symbol flags/attributes; these are to be accessed via the
+ // corresponding loader "AttrXXX" and "SetAttrXXX" methods. Please
+ // visit the comments on these methods for more details on the
+ // semantics / interpretation of the specific flags or attribute.
+ attrReachable Bitmap // reachable symbols, indexed by global index
+ attrOnList Bitmap // "on list" symbols, indexed by global index
+ attrLocal Bitmap // "local" symbols, indexed by global index
+ attrNotInSymbolTable Bitmap // "not in symtab" symbols, indexed by global idx
+ attrUsedInIface Bitmap // "used in interface" symbols, indexed by global idx
+ attrVisibilityHidden Bitmap // hidden symbols, indexed by ext sym index
+ attrDuplicateOK Bitmap // dupOK symbols, indexed by ext sym index
+ attrShared Bitmap // shared symbols, indexed by ext sym index
+ attrExternal Bitmap // external symbols, indexed by ext sym index
+
+ attrReadOnly map[Sym]bool // readonly data for this sym
+ attrSpecial map[Sym]struct{} // "special" frame symbols
+ attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols
+ attrCgoExportStatic map[Sym]struct{} // "cgo_export_static" symbols
+ generatedSyms map[Sym]struct{} // symbols that generate their content
+
+ // Outer and Sub relations for symbols.
+ // TODO: figure out whether it's more efficient to just have these
+ // as fields on extSymPayload (note that this won't be a viable
+ // strategy if somewhere in the linker we set sub/outer for a
+ // non-external sym).
+ outer map[Sym]Sym
+ sub map[Sym]Sym
+
+ dynimplib map[Sym]string // stores Dynimplib symbol attribute
+ dynimpvers map[Sym]string // stores Dynimpvers symbol attribute
+ localentry map[Sym]uint8 // stores Localentry symbol attribute
+ extname map[Sym]string // stores Extname symbol attribute
+ elfType map[Sym]elf.SymType // stores elf type symbol property
+ elfSym map[Sym]int32 // stores elf sym symbol property
+ localElfSym map[Sym]int32 // stores "local" elf sym symbol property
+ symPkg map[Sym]string // stores package for symbol, or library for shlib-derived syms
+ plt map[Sym]int32 // stores dynimport for pe objects
+ got map[Sym]int32 // stores got for pe objects
+ dynid map[Sym]int32 // stores Dynid for symbol
+
+ relocVariant map[relocId]sym.RelocVariant // stores variant relocs
+
+ // Used to implement field tracking; created during deadcode if
+ // field tracking is enabled. Reachparent[K] contains the index of
+ // the symbol that triggered the marking of symbol K as live.
+ Reachparent []Sym
+
+ // CgoExports records cgo-exported symbols by SymName.
+ CgoExports map[string]Sym
+
+ flags uint32
+
+ strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
+
+ elfsetstring elfsetstringFunc
+
+ errorReporter *ErrorReporter
+
+ npkgsyms int // number of package symbols, for accounting
+ nhashedsyms int // number of hashed symbols, for accounting
+}
+
+const (
+ pkgDef = iota
+ hashed64Def
+ hashedDef
+ nonPkgDef
+ nonPkgRef
+)
+
+// objidx
+const (
+ nilObj = iota
+ extObj
+ goObjStart
+)
+
+type elfsetstringFunc func(str string, off int)
+
+// extSymPayload holds the payload (data + relocations) for linker-synthesized
+// external symbols (note that symbol value is stored in a separate slice).
+type extSymPayload struct {
+ name string // TODO: would this be better as offset into str table?
+ size int64
+ ver int
+ kind sym.SymKind
+ objidx uint32 // index of original object if sym made by cloneToExternal
+ relocs []goobj.Reloc
+ data []byte
+ auxs []goobj.Aux
+}
+
+const (
+ // Loader.flags
+ FlagStrictDups = 1 << iota
+)
+
+func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorReporter) *Loader {
+ nbuiltin := goobj.NBuiltin()
+ extReader := &oReader{objidx: extObj}
+ ldr := &Loader{
+ start: make(map[*oReader]Sym),
+ objs: []objIdx{{}, {extReader, 0}}, // reserve index 0 for nil symbol, 1 for external symbols
+ objSyms: make([]objSym, 1, 1), // This will get overwritten later.
+ extReader: extReader,
+ symsByName: [2]map[string]Sym{make(map[string]Sym, 80000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols
+ objByPkg: make(map[string]uint32),
+ outer: make(map[Sym]Sym),
+ sub: make(map[Sym]Sym),
+ dynimplib: make(map[Sym]string),
+ dynimpvers: make(map[Sym]string),
+ localentry: make(map[Sym]uint8),
+ extname: make(map[Sym]string),
+ attrReadOnly: make(map[Sym]bool),
+ elfType: make(map[Sym]elf.SymType),
+ elfSym: make(map[Sym]int32),
+ localElfSym: make(map[Sym]int32),
+ symPkg: make(map[Sym]string),
+ plt: make(map[Sym]int32),
+ got: make(map[Sym]int32),
+ dynid: make(map[Sym]int32),
+ attrSpecial: make(map[Sym]struct{}),
+ attrCgoExportDynamic: make(map[Sym]struct{}),
+ attrCgoExportStatic: make(map[Sym]struct{}),
+ generatedSyms: make(map[Sym]struct{}),
+ deferReturnTramp: make(map[Sym]bool),
+ extStaticSyms: make(map[nameVer]Sym),
+ builtinSyms: make([]Sym, nbuiltin),
+ flags: flags,
+ elfsetstring: elfsetstring,
+ errorReporter: reporter,
+ sects: []*sym.Section{nil}, // reserve index 0 for nil section
+ }
+ reporter.ldr = ldr
+ return ldr
+}
+
+// Add object file r, return the start index.
+func (l *Loader) addObj(pkg string, r *oReader) Sym {
+ if _, ok := l.start[r]; ok {
+ panic("already added")
+ }
+ pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path
+ if _, ok := l.objByPkg[pkg]; !ok {
+ l.objByPkg[pkg] = r.objidx
+ }
+ i := Sym(len(l.objSyms))
+ l.start[r] = i
+ l.objs = append(l.objs, objIdx{r, i})
+ return i
+}
+
+// Add a symbol from an object file, return the global index.
+// If the symbol already exist, it returns the index of that symbol.
+func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind int, osym *goobj.Sym) Sym {
+ l := st.l
+ if l.extStart != 0 {
+ panic("addSym called after external symbol is created")
+ }
+ i := Sym(len(l.objSyms))
+ addToGlobal := func() {
+ l.objSyms = append(l.objSyms, objSym{r.objidx, li})
+ }
+ if name == "" && kind != hashed64Def && kind != hashedDef {
+ addToGlobal()
+ return i // unnamed aux symbol
+ }
+ if ver == r.version {
+ // Static symbol. Add its global index but don't
+ // add to name lookup table, as it cannot be
+ // referenced by name.
+ addToGlobal()
+ return i
+ }
+ switch kind {
+ case pkgDef:
+ // Defined package symbols cannot be dup to each other.
+ // We load all the package symbols first, so we don't need
+ // to check dup here.
+ // We still add it to the lookup table, as it may still be
+ // referenced by name (e.g. through linkname).
+ l.symsByName[ver][name] = i
+ addToGlobal()
+ return i
+ case hashed64Def, hashedDef:
+ // Hashed (content-addressable) symbol. Check the hash
+ // but don't add to name lookup table, as they are not
+ // referenced by name. Also no need to do overwriting
+ // check, as same hash indicates same content.
+ var checkHash func() (symAndSize, bool)
+ var addToHashMap func(symAndSize)
+ var h64 uint64 // only used for hashed64Def
+ var h *goobj.HashType // only used for hashedDef
+ if kind == hashed64Def {
+ checkHash = func() (symAndSize, bool) {
+ h64 = r.Hash64(li - uint32(r.ndef))
+ s, existed := st.hashed64Syms[h64]
+ return s, existed
+ }
+ addToHashMap = func(ss symAndSize) { st.hashed64Syms[h64] = ss }
+ } else {
+ checkHash = func() (symAndSize, bool) {
+ h = r.Hash(li - uint32(r.ndef+r.nhashed64def))
+ s, existed := st.hashedSyms[*h]
+ return s, existed
+ }
+ addToHashMap = func(ss symAndSize) { st.hashedSyms[*h] = ss }
+ }
+ siz := osym.Siz()
+ if s, existed := checkHash(); existed {
+ // The content hash is built from symbol data and relocations. In the
+ // object file, the symbol data may not always contain trailing zeros,
+ // e.g. for [5]int{1,2,3} and [100]int{1,2,3}, the data is same
+ // (although the size is different).
+ // Also, for short symbols, the content hash is the identity function of
+ // the 8 bytes, and trailing zeros doesn't change the hash value, e.g.
+ // hash("A") == hash("A\0\0\0").
+ // So when two symbols have the same hash, we need to use the one with
+ // larger size.
+ if siz > s.size {
+ // New symbol has larger size, use the new one. Rewrite the index mapping.
+ l.objSyms[s.sym] = objSym{r.objidx, li}
+ addToHashMap(symAndSize{s.sym, siz})
+ }
+ return s.sym
+ }
+ addToHashMap(symAndSize{i, siz})
+ addToGlobal()
+ return i
+ }
+
+ // Non-package (named) symbol. Check if it already exists.
+ oldi, existed := l.symsByName[ver][name]
+ if !existed {
+ l.symsByName[ver][name] = i
+ addToGlobal()
+ return i
+ }
+ // symbol already exists
+ if osym.Dupok() {
+ if l.flags&FlagStrictDups != 0 {
+ l.checkdup(name, r, li, oldi)
+ }
+ // Fix for issue #47185 -- given two dupok symbols with
+ // different sizes, favor symbol with larger size. See
+ // also issue #46653.
+ szdup := l.SymSize(oldi)
+ sz := int64(r.Sym(li).Siz())
+ if szdup < sz {
+ // new symbol overwrites old symbol.
+ l.objSyms[oldi] = objSym{r.objidx, li}
+ }
+ return oldi
+ }
+ oldr, oldli := l.toLocal(oldi)
+ oldsym := oldr.Sym(oldli)
+ if oldsym.Dupok() {
+ return oldi
+ }
+ overwrite := r.DataSize(li) != 0
+ if overwrite {
+ // new symbol overwrites old symbol.
+ oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
+ if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) {
+ log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
+ }
+ l.objSyms[oldi] = objSym{r.objidx, li}
+ } else {
+ // old symbol overwrites new symbol.
+ typ := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
+ if !typ.IsData() { // only allow overwriting data symbol
+ log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
+ }
+ }
+ return oldi
+}
+
+// newExtSym creates a new external sym with the specified
+// name/version.
+func (l *Loader) newExtSym(name string, ver int) Sym {
+ i := Sym(len(l.objSyms))
+ if l.extStart == 0 {
+ l.extStart = i
+ }
+ l.growValues(int(i) + 1)
+ l.growAttrBitmaps(int(i) + 1)
+ pi := l.newPayload(name, ver)
+ l.objSyms = append(l.objSyms, objSym{l.extReader.objidx, uint32(pi)})
+ l.extReader.syms = append(l.extReader.syms, i)
+ return i
+}
+
+// LookupOrCreateSym looks up the symbol with the specified name/version,
+// returning its Sym index if found. If the lookup fails, a new external
+// Sym will be created, entered into the lookup tables, and returned.
+func (l *Loader) LookupOrCreateSym(name string, ver int) Sym {
+ i := l.Lookup(name, ver)
+ if i != 0 {
+ return i
+ }
+ i = l.newExtSym(name, ver)
+ static := ver >= sym.SymVerStatic || ver < 0
+ if static {
+ l.extStaticSyms[nameVer{name, ver}] = i
+ } else {
+ l.symsByName[ver][name] = i
+ }
+ return i
+}
+
+// AddCgoExport records a cgo-exported symbol in l.CgoExports.
+// This table is used to identify the correct Go symbol ABI to use
+// to resolve references from host objects (which don't have ABIs).
+func (l *Loader) AddCgoExport(s Sym) {
+ if l.CgoExports == nil {
+ l.CgoExports = make(map[string]Sym)
+ }
+ l.CgoExports[l.SymName(s)] = s
+}
+
+// LookupOrCreateCgoExport is like LookupOrCreateSym, but if ver
+// indicates a global symbol, it uses the CgoExport table to determine
+// the appropriate symbol version (ABI) to use. ver must be either 0
+// or a static symbol version.
+func (l *Loader) LookupOrCreateCgoExport(name string, ver int) Sym {
+ if ver >= sym.SymVerStatic {
+ return l.LookupOrCreateSym(name, ver)
+ }
+ if ver != 0 {
+ panic("ver must be 0 or a static version")
+ }
+ // Look for a cgo-exported symbol from Go.
+ if s, ok := l.CgoExports[name]; ok {
+ return s
+ }
+ // Otherwise, this must just be a symbol in the host object.
+ // Create a version 0 symbol for it.
+ return l.LookupOrCreateSym(name, 0)
+}
+
+func (l *Loader) IsExternal(i Sym) bool {
+ r, _ := l.toLocal(i)
+ return l.isExtReader(r)
+}
+
+func (l *Loader) isExtReader(r *oReader) bool {
+ return r == l.extReader
+}
+
+// For external symbol, return its index in the payloads array.
+// XXX result is actually not a global index. We (ab)use the Sym type
+// so we don't need conversion for accessing bitmaps.
+func (l *Loader) extIndex(i Sym) Sym {
+ _, li := l.toLocal(i)
+ return Sym(li)
+}
+
+// Get a new payload for external symbol, return its index in
+// the payloads array.
+func (l *Loader) newPayload(name string, ver int) int {
+ pi := len(l.payloads)
+ pp := l.allocPayload()
+ pp.name = name
+ pp.ver = ver
+ l.payloads = append(l.payloads, pp)
+ l.growExtAttrBitmaps()
+ return pi
+}
+
+// getPayload returns a pointer to the extSymPayload struct for an
+// external symbol if the symbol has a payload. Will panic if the
+// symbol in question is bogus (zero or not an external sym).
+func (l *Loader) getPayload(i Sym) *extSymPayload {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("bogus symbol index %d in getPayload", i))
+ }
+ pi := l.extIndex(i)
+ return l.payloads[pi]
+}
+
+// allocPayload allocates a new payload.
+func (l *Loader) allocPayload() *extSymPayload {
+ batch := l.payloadBatch
+ if len(batch) == 0 {
+ batch = make([]extSymPayload, 1000)
+ }
+ p := &batch[0]
+ l.payloadBatch = batch[1:]
+ return p
+}
+
+func (ms *extSymPayload) Grow(siz int64) {
+ if int64(int(siz)) != siz {
+ log.Fatalf("symgrow size %d too long", siz)
+ }
+ if int64(len(ms.data)) >= siz {
+ return
+ }
+ if cap(ms.data) < int(siz) {
+ cl := len(ms.data)
+ ms.data = append(ms.data, make([]byte, int(siz)+1-cl)...)
+ ms.data = ms.data[0:cl]
+ }
+ ms.data = ms.data[:siz]
+}
+
+// Convert a local index to a global index.
+func (l *Loader) toGlobal(r *oReader, i uint32) Sym {
+ return r.syms[i]
+}
+
+// Convert a global index to a local index.
+func (l *Loader) toLocal(i Sym) (*oReader, uint32) {
+ return l.objs[l.objSyms[i].objidx].r, l.objSyms[i].s
+}
+
+// Resolve a local symbol reference. Return global index.
+func (l *Loader) resolve(r *oReader, s goobj.SymRef) Sym {
+ var rr *oReader
+ switch p := s.PkgIdx; p {
+ case goobj.PkgIdxInvalid:
+ // {0, X} with non-zero X is never a valid sym reference from a Go object.
+ // We steal this space for symbol references from external objects.
+ // In this case, X is just the global index.
+ if l.isExtReader(r) {
+ return Sym(s.SymIdx)
+ }
+ if s.SymIdx != 0 {
+ panic("bad sym ref")
+ }
+ return 0
+ case goobj.PkgIdxHashed64:
+ i := int(s.SymIdx) + r.ndef
+ return r.syms[i]
+ case goobj.PkgIdxHashed:
+ i := int(s.SymIdx) + r.ndef + r.nhashed64def
+ return r.syms[i]
+ case goobj.PkgIdxNone:
+ i := int(s.SymIdx) + r.ndef + r.nhashed64def + r.nhasheddef
+ return r.syms[i]
+ case goobj.PkgIdxBuiltin:
+ if bi := l.builtinSyms[s.SymIdx]; bi != 0 {
+ return bi
+ }
+ l.reportMissingBuiltin(int(s.SymIdx), r.unit.Lib.Pkg)
+ return 0
+ case goobj.PkgIdxSelf:
+ rr = r
+ default:
+ rr = l.objs[r.pkg[p]].r
+ }
+ return l.toGlobal(rr, s.SymIdx)
+}
+
+// reportMissingBuiltin issues an error in the case where we have a
+// relocation against a runtime builtin whose definition is not found
+// when the runtime package is built. The canonical example is
+// "runtime.racefuncenter" -- currently if you do something like
+//
+// go build -gcflags=-race myprogram.go
+//
+// the compiler will insert calls to the builtin runtime.racefuncenter,
+// but the version of the runtime used for linkage won't actually contain
+// definitions of that symbol. See issue #42396 for details.
+//
+// As currently implemented, this is a fatal error. This has drawbacks
+// in that if there are multiple missing builtins, the error will only
+// cite the first one. On the plus side, terminating the link here has
+// advantages in that we won't run the risk of panics or crashes later
+// on in the linker due to R_CALL relocations with 0-valued target
+// symbols.
+func (l *Loader) reportMissingBuiltin(bsym int, reflib string) {
+ bname, _ := goobj.BuiltinName(bsym)
+ log.Fatalf("reference to undefined builtin %q from package %q",
+ bname, reflib)
+}
+
+// Look up a symbol by name, return global index, or 0 if not found.
+// This is more like Syms.ROLookup than Lookup -- it doesn't create
+// new symbol.
+func (l *Loader) Lookup(name string, ver int) Sym {
+ if ver >= sym.SymVerStatic || ver < 0 {
+ return l.extStaticSyms[nameVer{name, ver}]
+ }
+ return l.symsByName[ver][name]
+}
+
+// Check that duplicate symbols have same contents.
+func (l *Loader) checkdup(name string, r *oReader, li uint32, dup Sym) {
+ p := r.Data(li)
+ rdup, ldup := l.toLocal(dup)
+ pdup := rdup.Data(ldup)
+ reason := "same length but different contents"
+ if len(p) != len(pdup) {
+ reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
+ } else if bytes.Equal(p, pdup) {
+ // For BSS symbols, we need to check size as well, see issue 46653.
+ szdup := l.SymSize(dup)
+ sz := int64(r.Sym(li).Siz())
+ if szdup == sz {
+ return
+ }
+ reason = fmt.Sprintf("different sizes: new size %d != old size %d",
+ sz, szdup)
+ }
+ fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason)
+
+ // For the moment, allow DWARF subprogram DIEs for
+ // auto-generated wrapper functions. What seems to happen
+ // here is that we get different line numbers on formal
+ // params; I am guessing that the pos is being inherited
+ // from the spot where the wrapper is needed.
+ allowed := strings.HasPrefix(name, "go:info.go.interface") ||
+ strings.HasPrefix(name, "go:info.go.builtin") ||
+ strings.HasPrefix(name, "go:debuglines")
+ if !allowed {
+ l.strictDupMsgs++
+ }
+}
+
+func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
+
+// Number of total symbols.
+func (l *Loader) NSym() int {
+ return len(l.objSyms)
+}
+
+// Number of defined Go symbols.
+func (l *Loader) NDef() int {
+ return int(l.extStart)
+}
+
+// Number of reachable symbols.
+func (l *Loader) NReachableSym() int {
+ return l.attrReachable.Count()
+}
+
+// Returns the name of the i-th symbol.
+func (l *Loader) SymName(i Sym) string {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.name
+ }
+ r, li := l.toLocal(i)
+ if r == nil {
+ return "?"
+ }
+ return r.Sym(li).Name(r.Reader)
+}
+
+// Returns the version of the i-th symbol.
+func (l *Loader) SymVersion(i Sym) int {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.ver
+ }
+ r, li := l.toLocal(i)
+ return int(abiToVer(r.Sym(li).ABI(), r.version))
+}
+
+func (l *Loader) IsFileLocal(i Sym) bool {
+ return l.SymVersion(i) >= sym.SymVerStatic
+}
+
+// IsFromAssembly returns true if this symbol is derived from an
+// object file generated by the Go assembler.
+func (l *Loader) IsFromAssembly(i Sym) bool {
+ if l.IsExternal(i) {
+ return false
+ }
+ r, _ := l.toLocal(i)
+ return r.FromAssembly()
+}
+
+// Returns the type of the i-th symbol.
+func (l *Loader) SymType(i Sym) sym.SymKind {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp != nil {
+ return pp.kind
+ }
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return sym.AbiSymKindToSymKind[objabi.SymKind(r.Sym(li).Type())]
+}
+
+// Returns the attributes of the i-th symbol.
+func (l *Loader) SymAttr(i Sym) uint8 {
+ if l.IsExternal(i) {
+ // TODO: do something? External symbols have different representation of attributes.
+ // For now, ReflectMethod, NoSplit, GoType, and Typelink are used and they cannot be
+ // set by external symbol.
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).Flag()
+}
+
+// Returns the size of the i-th symbol.
+func (l *Loader) SymSize(i Sym) int64 {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return pp.size
+ }
+ r, li := l.toLocal(i)
+ return int64(r.Sym(li).Siz())
+}
+
+// AttrReachable returns true for symbols that are transitively
+// referenced from the entry points. Unreachable symbols are not
+// written to the output.
+func (l *Loader) AttrReachable(i Sym) bool {
+ return l.attrReachable.Has(i)
+}
+
+// SetAttrReachable sets the reachability property for a symbol (see
+// AttrReachable).
+func (l *Loader) SetAttrReachable(i Sym, v bool) {
+ if v {
+ l.attrReachable.Set(i)
+ } else {
+ l.attrReachable.Unset(i)
+ }
+}
+
+// AttrOnList returns true for symbols that are on some list (such as
+// the list of all text symbols, or one of the lists of data symbols)
+// and is consulted to avoid bugs where a symbol is put on a list
+// twice.
+func (l *Loader) AttrOnList(i Sym) bool {
+ return l.attrOnList.Has(i)
+}
+
+// SetAttrOnList sets the "on list" property for a symbol (see
+// AttrOnList).
+func (l *Loader) SetAttrOnList(i Sym, v bool) {
+ if v {
+ l.attrOnList.Set(i)
+ } else {
+ l.attrOnList.Unset(i)
+ }
+}
+
+// AttrLocal returns true for symbols that are only visible within the
+// module (executable or shared library) being linked. This attribute
+// is applied to thunks and certain other linker-generated symbols.
+func (l *Loader) AttrLocal(i Sym) bool {
+ return l.attrLocal.Has(i)
+}
+
+// SetAttrLocal the "local" property for a symbol (see AttrLocal above).
+func (l *Loader) SetAttrLocal(i Sym, v bool) {
+ if v {
+ l.attrLocal.Set(i)
+ } else {
+ l.attrLocal.Unset(i)
+ }
+}
+
+// AttrUsedInIface returns true for a type symbol that is used in
+// an interface.
+func (l *Loader) AttrUsedInIface(i Sym) bool {
+ return l.attrUsedInIface.Has(i)
+}
+
+func (l *Loader) SetAttrUsedInIface(i Sym, v bool) {
+ if v {
+ l.attrUsedInIface.Set(i)
+ } else {
+ l.attrUsedInIface.Unset(i)
+ }
+}
+
+// SymAddr checks that a symbol is reachable, and returns its value.
+func (l *Loader) SymAddr(i Sym) int64 {
+ if !l.AttrReachable(i) {
+ panic("unreachable symbol in symaddr")
+ }
+ return l.values[i]
+}
+
+// AttrNotInSymbolTable returns true for symbols that should not be
+// added to the symbol table of the final generated load module.
+func (l *Loader) AttrNotInSymbolTable(i Sym) bool {
+ return l.attrNotInSymbolTable.Has(i)
+}
+
+// SetAttrNotInSymbolTable the "not in symtab" property for a symbol
+// (see AttrNotInSymbolTable above).
+func (l *Loader) SetAttrNotInSymbolTable(i Sym, v bool) {
+ if v {
+ l.attrNotInSymbolTable.Set(i)
+ } else {
+ l.attrNotInSymbolTable.Unset(i)
+ }
+}
+
+// AttrVisibilityHidden symbols returns true for ELF symbols with
+// visibility set to STV_HIDDEN. They become local symbols in
+// the final executable. Only relevant when internally linking
+// on an ELF platform.
+func (l *Loader) AttrVisibilityHidden(i Sym) bool {
+ if !l.IsExternal(i) {
+ return false
+ }
+ return l.attrVisibilityHidden.Has(l.extIndex(i))
+}
+
+// SetAttrVisibilityHidden sets the "hidden visibility" property for a
+// symbol (see AttrVisibilityHidden).
+func (l *Loader) SetAttrVisibilityHidden(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("tried to set visibility attr on non-external symbol")
+ }
+ if v {
+ l.attrVisibilityHidden.Set(l.extIndex(i))
+ } else {
+ l.attrVisibilityHidden.Unset(l.extIndex(i))
+ }
+}
+
+// AttrDuplicateOK returns true for a symbol that can be present in
+// multiple object files.
+func (l *Loader) AttrDuplicateOK(i Sym) bool {
+ if !l.IsExternal(i) {
+ // TODO: if this path winds up being taken frequently, it
+ // might make more sense to copy the flag value out of the object
+ // into a larger bitmap during preload.
+ r, li := l.toLocal(i)
+ return r.Sym(li).Dupok()
+ }
+ return l.attrDuplicateOK.Has(l.extIndex(i))
+}
+
+// SetAttrDuplicateOK sets the "duplicate OK" property for an external
+// symbol (see AttrDuplicateOK).
+func (l *Loader) SetAttrDuplicateOK(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("tried to set dupok attr on non-external symbol")
+ }
+ if v {
+ l.attrDuplicateOK.Set(l.extIndex(i))
+ } else {
+ l.attrDuplicateOK.Unset(l.extIndex(i))
+ }
+}
+
+// AttrShared returns true for symbols compiled with the -shared option.
+func (l *Loader) AttrShared(i Sym) bool {
+ if !l.IsExternal(i) {
+ // TODO: if this path winds up being taken frequently, it
+ // might make more sense to copy the flag value out of the
+ // object into a larger bitmap during preload.
+ r, _ := l.toLocal(i)
+ return r.Shared()
+ }
+ return l.attrShared.Has(l.extIndex(i))
+}
+
+// SetAttrShared sets the "shared" property for an external
+// symbol (see AttrShared).
+func (l *Loader) SetAttrShared(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("tried to set shared attr on non-external symbol %d %s", i, l.SymName(i)))
+ }
+ if v {
+ l.attrShared.Set(l.extIndex(i))
+ } else {
+ l.attrShared.Unset(l.extIndex(i))
+ }
+}
+
+// AttrExternal returns true for function symbols loaded from host
+// object files.
+func (l *Loader) AttrExternal(i Sym) bool {
+ if !l.IsExternal(i) {
+ return false
+ }
+ return l.attrExternal.Has(l.extIndex(i))
+}
+
+// SetAttrExternal sets the "external" property for an host object
+// symbol (see AttrExternal).
+func (l *Loader) SetAttrExternal(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic(fmt.Sprintf("tried to set external attr on non-external symbol %q", l.SymName(i)))
+ }
+ if v {
+ l.attrExternal.Set(l.extIndex(i))
+ } else {
+ l.attrExternal.Unset(l.extIndex(i))
+ }
+}
+
+// AttrSpecial returns true for a symbols that do not have their
+// address (i.e. Value) computed by the usual mechanism of
+// data.go:dodata() & data.go:address().
+func (l *Loader) AttrSpecial(i Sym) bool {
+ _, ok := l.attrSpecial[i]
+ return ok
+}
+
+// SetAttrSpecial sets the "special" property for a symbol (see
+// AttrSpecial).
+func (l *Loader) SetAttrSpecial(i Sym, v bool) {
+ if v {
+ l.attrSpecial[i] = struct{}{}
+ } else {
+ delete(l.attrSpecial, i)
+ }
+}
+
+// AttrCgoExportDynamic returns true for a symbol that has been
+// specially marked via the "cgo_export_dynamic" compiler directive
+// written by cgo (in response to //export directives in the source).
+func (l *Loader) AttrCgoExportDynamic(i Sym) bool {
+ _, ok := l.attrCgoExportDynamic[i]
+ return ok
+}
+
+// SetAttrCgoExportDynamic sets the "cgo_export_dynamic" for a symbol
+// (see AttrCgoExportDynamic).
+func (l *Loader) SetAttrCgoExportDynamic(i Sym, v bool) {
+ if v {
+ l.attrCgoExportDynamic[i] = struct{}{}
+ } else {
+ delete(l.attrCgoExportDynamic, i)
+ }
+}
+
+// AttrCgoExportStatic returns true for a symbol that has been
+// specially marked via the "cgo_export_static" directive
+// written by cgo.
+func (l *Loader) AttrCgoExportStatic(i Sym) bool {
+ _, ok := l.attrCgoExportStatic[i]
+ return ok
+}
+
+// SetAttrCgoExportStatic sets the "cgo_export_static" for a symbol
+// (see AttrCgoExportStatic).
+func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) {
+ if v {
+ l.attrCgoExportStatic[i] = struct{}{}
+ } else {
+ delete(l.attrCgoExportStatic, i)
+ }
+}
+
+// IsGeneratedSym returns true if a symbol's been previously marked as a
+// generator symbol through the SetIsGeneratedSym. The functions for generator
+// symbols are kept in the Link context.
+func (l *Loader) IsGeneratedSym(i Sym) bool {
+ _, ok := l.generatedSyms[i]
+ return ok
+}
+
+// SetIsGeneratedSym marks symbols as generated symbols. Data shouldn't be
+// stored in generated symbols, and a function is registered and called for
+// each of these symbols.
+func (l *Loader) SetIsGeneratedSym(i Sym, v bool) {
+ if !l.IsExternal(i) {
+ panic("only external symbols can be generated")
+ }
+ if v {
+ l.generatedSyms[i] = struct{}{}
+ } else {
+ delete(l.generatedSyms, i)
+ }
+}
+
+func (l *Loader) AttrCgoExport(i Sym) bool {
+ return l.AttrCgoExportDynamic(i) || l.AttrCgoExportStatic(i)
+}
+
+// AttrReadOnly returns true for a symbol whose underlying data
+// is stored via a read-only mmap.
+func (l *Loader) AttrReadOnly(i Sym) bool {
+ if v, ok := l.attrReadOnly[i]; ok {
+ return v
+ }
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ return l.objs[pp.objidx].r.ReadOnly()
+ }
+ return false
+ }
+ r, _ := l.toLocal(i)
+ return r.ReadOnly()
+}
+
+// SetAttrReadOnly sets the "data is read only" property for a symbol
+// (see AttrReadOnly).
+func (l *Loader) SetAttrReadOnly(i Sym, v bool) {
+ l.attrReadOnly[i] = v
+}
+
+// AttrSubSymbol returns true for symbols that are listed as a
+// sub-symbol of some other outer symbol. The sub/outer mechanism is
+// used when loading host objects (sections from the host object
+// become regular linker symbols and symbols go on the Sub list of
+// their section) and for constructing the global offset table when
+// internally linking a dynamic executable.
+//
+// Note that in later stages of the linker, we set Outer(S) to some
+// container symbol C, but don't set Sub(C). Thus we have two
+// distinct scenarios:
+//
+// - Outer symbol covers the address ranges of its sub-symbols.
+// Outer.Sub is set in this case.
+// - Outer symbol doesn't conver the address ranges. It is zero-sized
+// and doesn't have sub-symbols. In the case, the inner symbol is
+// not actually a "SubSymbol". (Tricky!)
+//
+// This method returns TRUE only for sub-symbols in the first scenario.
+//
+// FIXME: would be better to do away with this and have a better way
+// to represent container symbols.
+
+func (l *Loader) AttrSubSymbol(i Sym) bool {
+ // we don't explicitly store this attribute any more -- return
+ // a value based on the sub-symbol setting.
+ o := l.OuterSym(i)
+ if o == 0 {
+ return false
+ }
+ return l.SubSym(o) != 0
+}
+
+// Note that we don't have a 'SetAttrSubSymbol' method in the loader;
+// clients should instead use the AddInteriorSym method to establish
+// containment relationships for host object symbols.
+
+// Returns whether the i-th symbol has ReflectMethod attribute set.
+func (l *Loader) IsReflectMethod(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagReflectMethod != 0
+}
+
+// Returns whether the i-th symbol is nosplit.
+func (l *Loader) IsNoSplit(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagNoSplit != 0
+}
+
+// Returns whether this is a Go type symbol.
+func (l *Loader) IsGoType(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagGoType != 0
+}
+
+// Returns whether this symbol should be included in typelink.
+func (l *Loader) IsTypelink(i Sym) bool {
+ return l.SymAttr(i)&goobj.SymFlagTypelink != 0
+}
+
+// Returns whether this symbol is an itab symbol.
+func (l *Loader) IsItab(i Sym) bool {
+ if l.IsExternal(i) {
+ return false
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).IsItab()
+}
+
+// Returns whether this symbol is a dictionary symbol.
+func (l *Loader) IsDict(i Sym) bool {
+ if l.IsExternal(i) {
+ return false
+ }
+ r, li := l.toLocal(i)
+ return r.Sym(li).IsDict()
+}
+
+// Return whether this is a trampoline of a deferreturn call.
+func (l *Loader) IsDeferReturnTramp(i Sym) bool {
+ return l.deferReturnTramp[i]
+}
+
+// Set that i is a trampoline of a deferreturn call.
+func (l *Loader) SetIsDeferReturnTramp(i Sym, v bool) {
+ l.deferReturnTramp[i] = v
+}
+
+// growValues grows the slice used to store symbol values.
+func (l *Loader) growValues(reqLen int) {
+ curLen := len(l.values)
+ if reqLen > curLen {
+ l.values = append(l.values, make([]int64, reqLen+1-curLen)...)
+ }
+}
+
+// SymValue returns the value of the i-th symbol. i is global index.
+func (l *Loader) SymValue(i Sym) int64 {
+ return l.values[i]
+}
+
+// SetSymValue sets the value of the i-th symbol. i is global index.
+func (l *Loader) SetSymValue(i Sym, val int64) {
+ l.values[i] = val
+}
+
+// AddToSymValue adds to the value of the i-th symbol. i is the global index.
+func (l *Loader) AddToSymValue(i Sym, val int64) {
+ l.values[i] += val
+}
+
+// Returns the symbol content of the i-th symbol. i is global index.
+func (l *Loader) Data(i Sym) []byte {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp != nil {
+ return pp.data
+ }
+ return nil
+ }
+ r, li := l.toLocal(i)
+ return r.Data(li)
+}
+
+// FreeData clears the symbol data of an external symbol, allowing the memory
+// to be freed earlier. No-op for non-external symbols.
+// i is global index.
+func (l *Loader) FreeData(i Sym) {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp != nil {
+ pp.data = nil
+ }
+ }
+}
+
+// SymAlign returns the alignment for a symbol.
+func (l *Loader) SymAlign(i Sym) int32 {
+ if int(i) >= len(l.align) {
+ // align is extended lazily -- it the sym in question is
+ // outside the range of the existing slice, then we assume its
+ // alignment has not yet been set.
+ return 0
+ }
+ // TODO: would it make sense to return an arch-specific
+ // alignment depending on section type? E.g. STEXT => 32,
+ // SDATA => 1, etc?
+ abits := l.align[i]
+ if abits == 0 {
+ return 0
+ }
+ return int32(1 << (abits - 1))
+}
+
+// SetSymAlign sets the alignment for a symbol.
+func (l *Loader) SetSymAlign(i Sym, align int32) {
+ // Reject nonsense alignments.
+ if align < 0 || align&(align-1) != 0 {
+ panic("bad alignment value")
+ }
+ if int(i) >= len(l.align) {
+ l.align = append(l.align, make([]uint8, l.NSym()-len(l.align))...)
+ }
+ if align == 0 {
+ l.align[i] = 0
+ }
+ l.align[i] = uint8(bits.Len32(uint32(align)))
+}
+
+// SymValue returns the section of the i-th symbol. i is global index.
+func (l *Loader) SymSect(i Sym) *sym.Section {
+ if int(i) >= len(l.symSects) {
+ // symSects is extended lazily -- it the sym in question is
+ // outside the range of the existing slice, then we assume its
+ // section has not yet been set.
+ return nil
+ }
+ return l.sects[l.symSects[i]]
+}
+
+// SetSymSect sets the section of the i-th symbol. i is global index.
+func (l *Loader) SetSymSect(i Sym, sect *sym.Section) {
+ if int(i) >= len(l.symSects) {
+ l.symSects = append(l.symSects, make([]uint16, l.NSym()-len(l.symSects))...)
+ }
+ l.symSects[i] = sect.Index
+}
+
+// growSects grows the slice used to store symbol sections.
+func (l *Loader) growSects(reqLen int) {
+ curLen := len(l.symSects)
+ if reqLen > curLen {
+ l.symSects = append(l.symSects, make([]uint16, reqLen+1-curLen)...)
+ }
+}
+
+// NewSection creates a new (output) section.
+func (l *Loader) NewSection() *sym.Section {
+ sect := new(sym.Section)
+ idx := len(l.sects)
+ if idx != int(uint16(idx)) {
+ panic("too many sections created")
+ }
+ sect.Index = uint16(idx)
+ l.sects = append(l.sects, sect)
+ return sect
+}
+
+// SymDynImplib returns the "dynimplib" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimplib(i Sym) string {
+ return l.dynimplib[i]
+}
+
+// SetSymDynimplib sets the "dynimplib" attribute for a symbol.
+func (l *Loader) SetSymDynimplib(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetDynimplib")
+ }
+ if value == "" {
+ delete(l.dynimplib, i)
+ } else {
+ l.dynimplib[i] = value
+ }
+}
+
+// SymDynimpvers returns the "dynimpvers" attribute for the specified
+// symbol, making up a portion of the info for a symbol specified
+// on a "cgo_import_dynamic" compiler directive.
+func (l *Loader) SymDynimpvers(i Sym) string {
+ return l.dynimpvers[i]
+}
+
+// SetSymDynimpvers sets the "dynimpvers" attribute for a symbol.
+func (l *Loader) SetSymDynimpvers(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetDynimpvers")
+ }
+ if value == "" {
+ delete(l.dynimpvers, i)
+ } else {
+ l.dynimpvers[i] = value
+ }
+}
+
+// SymExtname returns the "extname" value for the specified
+// symbol.
+func (l *Loader) SymExtname(i Sym) string {
+ if s, ok := l.extname[i]; ok {
+ return s
+ }
+ return l.SymName(i)
+}
+
+// SetSymExtname sets the "extname" attribute for a symbol.
+func (l *Loader) SetSymExtname(i Sym, value string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetExtname")
+ }
+ if value == "" {
+ delete(l.extname, i)
+ } else {
+ l.extname[i] = value
+ }
+}
+
+// SymElfType returns the previously recorded ELF type for a symbol
+// (used only for symbols read from shared libraries by ldshlibsyms).
+// It is not set for symbols defined by the packages being linked or
+// by symbols read by ldelf (and so is left as elf.STT_NOTYPE).
+func (l *Loader) SymElfType(i Sym) elf.SymType {
+ if et, ok := l.elfType[i]; ok {
+ return et
+ }
+ return elf.STT_NOTYPE
+}
+
+// SetSymElfType sets the elf type attribute for a symbol.
+func (l *Loader) SetSymElfType(i Sym, et elf.SymType) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymElfType")
+ }
+ if et == elf.STT_NOTYPE {
+ delete(l.elfType, i)
+ } else {
+ l.elfType[i] = et
+ }
+}
+
+// SymElfSym returns the ELF symbol index for a given loader
+// symbol, assigned during ELF symtab generation.
+func (l *Loader) SymElfSym(i Sym) int32 {
+ return l.elfSym[i]
+}
+
+// SetSymElfSym sets the elf symbol index for a symbol.
+func (l *Loader) SetSymElfSym(i Sym, es int32) {
+ if i == 0 {
+ panic("bad sym index")
+ }
+ if es == 0 {
+ delete(l.elfSym, i)
+ } else {
+ l.elfSym[i] = es
+ }
+}
+
+// SymLocalElfSym returns the "local" ELF symbol index for a given loader
+// symbol, assigned during ELF symtab generation.
+func (l *Loader) SymLocalElfSym(i Sym) int32 {
+ return l.localElfSym[i]
+}
+
+// SetSymLocalElfSym sets the "local" elf symbol index for a symbol.
+func (l *Loader) SetSymLocalElfSym(i Sym, es int32) {
+ if i == 0 {
+ panic("bad sym index")
+ }
+ if es == 0 {
+ delete(l.localElfSym, i)
+ } else {
+ l.localElfSym[i] = es
+ }
+}
+
+// SymPlt returns the PLT offset of symbol s.
+func (l *Loader) SymPlt(s Sym) int32 {
+ if v, ok := l.plt[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetPlt sets the PLT offset of symbol i.
+func (l *Loader) SetPlt(i Sym, v int32) {
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol for SetPlt")
+ }
+ if v == -1 {
+ delete(l.plt, i)
+ } else {
+ l.plt[i] = v
+ }
+}
+
+// SymGot returns the GOT offset of symbol s.
+func (l *Loader) SymGot(s Sym) int32 {
+ if v, ok := l.got[s]; ok {
+ return v
+ }
+ return -1
+}
+
+// SetGot sets the GOT offset of symbol i.
+func (l *Loader) SetGot(i Sym, v int32) {
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol for SetGot")
+ }
+ if v == -1 {
+ delete(l.got, i)
+ } else {
+ l.got[i] = v
+ }
+}
+
+// SymDynid returns the "dynid" property for the specified symbol.
+func (l *Loader) SymDynid(i Sym) int32 {
+ if s, ok := l.dynid[i]; ok {
+ return s
+ }
+ return -1
+}
+
+// SetSymDynid sets the "dynid" property for a symbol.
+func (l *Loader) SetSymDynid(i Sym, val int32) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymDynid")
+ }
+ if val == -1 {
+ delete(l.dynid, i)
+ } else {
+ l.dynid[i] = val
+ }
+}
+
+// DynIdSyms returns the set of symbols for which dynID is set to an
+// interesting (non-default) value. This is expected to be a fairly
+// small set.
+func (l *Loader) DynidSyms() []Sym {
+ sl := make([]Sym, 0, len(l.dynid))
+ for s := range l.dynid {
+ sl = append(sl, s)
+ }
+ sort.Slice(sl, func(i, j int) bool { return sl[i] < sl[j] })
+ return sl
+}
+
+// SymGoType returns the 'Gotype' property for a given symbol (set by
+// the Go compiler for variable symbols). This version relies on
+// reading aux symbols for the target sym -- it could be that a faster
+// approach would be to check for gotype during preload and copy the
+// results in to a map (might want to try this at some point and see
+// if it helps speed things up).
+func (l *Loader) SymGoType(i Sym) Sym { return l.aux1(i, goobj.AuxGotype) }
+
+// SymUnit returns the compilation unit for a given symbol (which will
+// typically be nil for external or linker-manufactured symbols).
+func (l *Loader) SymUnit(i Sym) *sym.CompilationUnit {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ r := l.objs[pp.objidx].r
+ return r.unit
+ }
+ return nil
+ }
+ r, _ := l.toLocal(i)
+ return r.unit
+}
+
+// SymPkg returns the package where the symbol came from (for
+// regular compiler-generated Go symbols), but in the case of
+// building with "-linkshared" (when a symbol is read from a
+// shared library), will hold the library name.
+// NOTE: this corresponds to sym.Symbol.File field.
+func (l *Loader) SymPkg(i Sym) string {
+ if f, ok := l.symPkg[i]; ok {
+ return f
+ }
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ if pp.objidx != 0 {
+ r := l.objs[pp.objidx].r
+ return r.unit.Lib.Pkg
+ }
+ return ""
+ }
+ r, _ := l.toLocal(i)
+ return r.unit.Lib.Pkg
+}
+
+// SetSymPkg sets the package/library for a symbol. This is
+// needed mainly for external symbols, specifically those imported
+// from shared libraries.
+func (l *Loader) SetSymPkg(i Sym, pkg string) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymPkg")
+ }
+ l.symPkg[i] = pkg
+}
+
+// SymLocalentry returns an offset in bytes of the "local entry" of a symbol.
+func (l *Loader) SymLocalentry(i Sym) uint8 {
+ return l.localentry[i]
+}
+
+// SetSymLocalentry sets the "local entry" offset attribute for a symbol.
+func (l *Loader) SetSymLocalentry(i Sym, value uint8) {
+ // reject bad symbols
+ if i >= Sym(len(l.objSyms)) || i == 0 {
+ panic("bad symbol index in SetSymLocalentry")
+ }
+ if value == 0 {
+ delete(l.localentry, i)
+ } else {
+ l.localentry[i] = value
+ }
+}
+
+// Returns the number of aux symbols given a global index.
+func (l *Loader) NAux(i Sym) int {
+ if l.IsExternal(i) {
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return r.NAux(li)
+}
+
+// Returns the "handle" to the j-th aux symbol of the i-th symbol.
+func (l *Loader) Aux(i Sym, j int) Aux {
+ if l.IsExternal(i) {
+ return Aux{}
+ }
+ r, li := l.toLocal(i)
+ if j >= r.NAux(li) {
+ return Aux{}
+ }
+ return Aux{r.Aux(li, j), r, l}
+}
+
+// GetFuncDwarfAuxSyms collects and returns the auxiliary DWARF
+// symbols associated with a given function symbol. Prior to the
+// introduction of the loader, this was done purely using name
+// lookups, e.f. for function with name XYZ we would then look up
+// go.info.XYZ, etc.
+func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, auxDwarfRanges, auxDwarfLines Sym) {
+ if l.SymType(fnSymIdx) != sym.STEXT {
+ log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String())
+ }
+ r, auxs := l.auxs(fnSymIdx)
+
+ for i := range auxs {
+ a := &auxs[i]
+ switch a.Type() {
+ case goobj.AuxDwarfInfo:
+ auxDwarfInfo = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfInfo) != sym.SDWARFFCN {
+ panic("aux dwarf info sym with wrong type")
+ }
+ case goobj.AuxDwarfLoc:
+ auxDwarfLoc = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfLoc) != sym.SDWARFLOC {
+ panic("aux dwarf loc sym with wrong type")
+ }
+ case goobj.AuxDwarfRanges:
+ auxDwarfRanges = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfRanges) != sym.SDWARFRANGE {
+ panic("aux dwarf ranges sym with wrong type")
+ }
+ case goobj.AuxDwarfLines:
+ auxDwarfLines = l.resolve(r, a.Sym())
+ if l.SymType(auxDwarfLines) != sym.SDWARFLINES {
+ panic("aux dwarf lines sym with wrong type")
+ }
+ }
+ }
+ return
+}
+
+// AddInteriorSym sets up 'interior' as an interior symbol of
+// container/payload symbol 'container'. An interior symbol does not
+// itself have data, but gives a name to a subrange of the data in its
+// container symbol. The container itself may or may not have a name.
+// This method is intended primarily for use in the host object
+// loaders, to capture the semantics of symbols and sections in an
+// object file. When reading a host object file, we'll typically
+// encounter a static section symbol (ex: ".text") containing content
+// for a collection of functions, then a series of ELF (or macho, etc)
+// symbol table entries each of which points into a sub-section
+// (offset and length) of its corresponding container symbol. Within
+// the go linker we create a loader.Sym for the container (which is
+// expected to have the actual content/payload) and then a set of
+// interior loader.Sym's that point into a portion of the container.
+func (l *Loader) AddInteriorSym(container Sym, interior Sym) {
+ // Container symbols are expected to have content/data.
+ // NB: this restriction may turn out to be too strict (it's possible
+ // to imagine a zero-sized container with an interior symbol pointing
+ // into it); it's ok to relax or remove it if we counter an
+ // oddball host object that triggers this.
+ if l.SymSize(container) == 0 && len(l.Data(container)) == 0 {
+ panic("unexpected empty container symbol")
+ }
+ // The interior symbols for a container are not expected to have
+ // content/data or relocations.
+ if len(l.Data(interior)) != 0 {
+ panic("unexpected non-empty interior symbol")
+ }
+ // Interior symbol is expected to be in the symbol table.
+ if l.AttrNotInSymbolTable(interior) {
+ panic("interior symbol must be in symtab")
+ }
+ // Only a single level of containment is allowed.
+ if l.OuterSym(container) != 0 {
+ panic("outer has outer itself")
+ }
+ // Interior sym should not already have a sibling.
+ if l.SubSym(interior) != 0 {
+ panic("sub set for subsym")
+ }
+ // Interior sym should not already point at a container.
+ if l.OuterSym(interior) != 0 {
+ panic("outer already set for subsym")
+ }
+ l.sub[interior] = l.sub[container]
+ l.sub[container] = interior
+ l.outer[interior] = container
+}
+
+// OuterSym gets the outer symbol for host object loaded symbols.
+func (l *Loader) OuterSym(i Sym) Sym {
+ // FIXME: add check for isExternal?
+ return l.outer[i]
+}
+
+// SubSym gets the subsymbol for host object loaded symbols.
+func (l *Loader) SubSym(i Sym) Sym {
+ // NB: note -- no check for l.isExternal(), since I am pretty sure
+ // that later phases in the linker set subsym for "type:" syms
+ return l.sub[i]
+}
+
+// SetCarrierSym declares that 'c' is the carrier or container symbol
+// for 's'. Carrier symbols are used in the linker to as a container
+// for a collection of sub-symbols where the content of the
+// sub-symbols is effectively concatenated to form the content of the
+// carrier. The carrier is given a name in the output symbol table
+// while the sub-symbol names are not. For example, the Go compiler
+// emits named string symbols (type SGOSTRING) when compiling a
+// package; after being deduplicated, these symbols are collected into
+// a single unit by assigning them a new carrier symbol named
+// "go:string.*" (which appears in the final symbol table for the
+// output load module).
+func (l *Loader) SetCarrierSym(s Sym, c Sym) {
+ if c == 0 {
+ panic("invalid carrier in SetCarrierSym")
+ }
+ if s == 0 {
+ panic("invalid sub-symbol in SetCarrierSym")
+ }
+ // Carrier symbols are not expected to have content/data. It is
+ // ok for them to have non-zero size (to allow for use of generator
+ // symbols).
+ if len(l.Data(c)) != 0 {
+ panic("unexpected non-empty carrier symbol")
+ }
+ l.outer[s] = c
+ // relocsym's foldSubSymbolOffset requires that we only
+ // have a single level of containment-- enforce here.
+ if l.outer[c] != 0 {
+ panic("invalid nested carrier sym")
+ }
+}
+
+// Initialize Reachable bitmap and its siblings for running deadcode pass.
+func (l *Loader) InitReachable() {
+ l.growAttrBitmaps(l.NSym() + 1)
+}
+
+type symWithVal struct {
+ s Sym
+ v int64
+}
+type bySymValue []symWithVal
+
+func (s bySymValue) Len() int { return len(s) }
+func (s bySymValue) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s bySymValue) Less(i, j int) bool { return s[i].v < s[j].v }
+
+// SortSub walks through the sub-symbols for 's' and sorts them
+// in place by increasing value. Return value is the new
+// sub symbol for the specified outer symbol.
+func (l *Loader) SortSub(s Sym) Sym {
+
+ if s == 0 || l.sub[s] == 0 {
+ return s
+ }
+
+ // Sort symbols using a slice first. Use a stable sort on the off
+ // chance that there's more than once symbol with the same value,
+ // so as to preserve reproducible builds.
+ sl := []symWithVal{}
+ for ss := l.sub[s]; ss != 0; ss = l.sub[ss] {
+ sl = append(sl, symWithVal{s: ss, v: l.SymValue(ss)})
+ }
+ sort.Stable(bySymValue(sl))
+
+ // Then apply any changes needed to the sub map.
+ ns := Sym(0)
+ for i := len(sl) - 1; i >= 0; i-- {
+ s := sl[i].s
+ l.sub[s] = ns
+ ns = s
+ }
+
+ // Update sub for outer symbol, then return
+ l.sub[s] = sl[0].s
+ return sl[0].s
+}
+
+// SortSyms sorts a list of symbols by their value.
+func (l *Loader) SortSyms(ss []Sym) {
+ sort.SliceStable(ss, func(i, j int) bool { return l.SymValue(ss[i]) < l.SymValue(ss[j]) })
+}
+
+// Insure that reachable bitmap and its siblings have enough size.
+func (l *Loader) growAttrBitmaps(reqLen int) {
+ if reqLen > l.attrReachable.Len() {
+ // These are indexed by global symbol
+ l.attrReachable = growBitmap(reqLen, l.attrReachable)
+ l.attrOnList = growBitmap(reqLen, l.attrOnList)
+ l.attrLocal = growBitmap(reqLen, l.attrLocal)
+ l.attrNotInSymbolTable = growBitmap(reqLen, l.attrNotInSymbolTable)
+ l.attrUsedInIface = growBitmap(reqLen, l.attrUsedInIface)
+ }
+ l.growExtAttrBitmaps()
+}
+
+func (l *Loader) growExtAttrBitmaps() {
+ // These are indexed by external symbol index (e.g. l.extIndex(i))
+ extReqLen := len(l.payloads)
+ if extReqLen > l.attrVisibilityHidden.Len() {
+ l.attrVisibilityHidden = growBitmap(extReqLen, l.attrVisibilityHidden)
+ l.attrDuplicateOK = growBitmap(extReqLen, l.attrDuplicateOK)
+ l.attrShared = growBitmap(extReqLen, l.attrShared)
+ l.attrExternal = growBitmap(extReqLen, l.attrExternal)
+ }
+}
+
+func (relocs *Relocs) Count() int { return len(relocs.rs) }
+
+// At returns the j-th reloc for a global symbol.
+func (relocs *Relocs) At(j int) Reloc {
+ if relocs.l.isExtReader(relocs.r) {
+ return Reloc{&relocs.rs[j], relocs.r, relocs.l}
+ }
+ return Reloc{&relocs.rs[j], relocs.r, relocs.l}
+}
+
+// Relocs returns a Relocs object for the given global sym.
+func (l *Loader) Relocs(i Sym) Relocs {
+ r, li := l.toLocal(i)
+ if r == nil {
+ panic(fmt.Sprintf("trying to get oreader for invalid sym %d\n\n", i))
+ }
+ return l.relocs(r, li)
+}
+
+// Relocs returns a Relocs object given a local sym index and reader.
+func (l *Loader) relocs(r *oReader, li uint32) Relocs {
+ var rs []goobj.Reloc
+ if l.isExtReader(r) {
+ pp := l.payloads[li]
+ rs = pp.relocs
+ } else {
+ rs = r.Relocs(li)
+ }
+ return Relocs{
+ rs: rs,
+ li: li,
+ r: r,
+ l: l,
+ }
+}
+
+func (l *Loader) auxs(i Sym) (*oReader, []goobj.Aux) {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ return l.objs[pp.objidx].r, pp.auxs
+ } else {
+ r, li := l.toLocal(i)
+ return r, r.Auxs(li)
+ }
+}
+
+// Returns a specific aux symbol of type t for symbol i.
+func (l *Loader) aux1(i Sym, t uint8) Sym {
+ r, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == t {
+ return l.resolve(r, a.Sym())
+ }
+ }
+ return 0
+}
+
+func (l *Loader) Pcsp(i Sym) Sym { return l.aux1(i, goobj.AuxPcsp) }
+
+// Returns all aux symbols of per-PC data for symbol i.
+// tmp is a scratch space for the pcdata slice.
+func (l *Loader) PcdataAuxs(i Sym, tmp []Sym) (pcsp, pcfile, pcline, pcinline Sym, pcdata []Sym) {
+ pcdata = tmp[:0]
+ r, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ switch a.Type() {
+ case goobj.AuxPcsp:
+ pcsp = l.resolve(r, a.Sym())
+ case goobj.AuxPcline:
+ pcline = l.resolve(r, a.Sym())
+ case goobj.AuxPcfile:
+ pcfile = l.resolve(r, a.Sym())
+ case goobj.AuxPcinline:
+ pcinline = l.resolve(r, a.Sym())
+ case goobj.AuxPcdata:
+ pcdata = append(pcdata, l.resolve(r, a.Sym()))
+ }
+ }
+ return
+}
+
+// Returns the number of pcdata for symbol i.
+func (l *Loader) NumPcdata(i Sym) int {
+ n := 0
+ _, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxPcdata {
+ n++
+ }
+ }
+ return n
+}
+
+// Returns all funcdata symbols of symbol i.
+// tmp is a scratch space.
+func (l *Loader) Funcdata(i Sym, tmp []Sym) []Sym {
+ fd := tmp[:0]
+ r, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxFuncdata {
+ fd = append(fd, l.resolve(r, a.Sym()))
+ }
+ }
+ return fd
+}
+
+// Returns the number of funcdata for symbol i.
+func (l *Loader) NumFuncdata(i Sym) int {
+ n := 0
+ _, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxFuncdata {
+ n++
+ }
+ }
+ return n
+}
+
+// FuncInfo provides hooks to access goobj.FuncInfo in the objects.
+type FuncInfo struct {
+ l *Loader
+ r *oReader
+ data []byte
+ lengths goobj.FuncInfoLengths
+}
+
+func (fi *FuncInfo) Valid() bool { return fi.r != nil }
+
+func (fi *FuncInfo) Args() int {
+ return int((*goobj.FuncInfo)(nil).ReadArgs(fi.data))
+}
+
+func (fi *FuncInfo) Locals() int {
+ return int((*goobj.FuncInfo)(nil).ReadLocals(fi.data))
+}
+
+func (fi *FuncInfo) FuncID() objabi.FuncID {
+ return (*goobj.FuncInfo)(nil).ReadFuncID(fi.data)
+}
+
+func (fi *FuncInfo) FuncFlag() objabi.FuncFlag {
+ return (*goobj.FuncInfo)(nil).ReadFuncFlag(fi.data)
+}
+
+func (fi *FuncInfo) StartLine() int32 {
+ return (*goobj.FuncInfo)(nil).ReadStartLine(fi.data)
+}
+
+// Preload has to be called prior to invoking the various methods
+// below related to pcdata, funcdataoff, files, and inltree nodes.
+func (fi *FuncInfo) Preload() {
+ fi.lengths = (*goobj.FuncInfo)(nil).ReadFuncInfoLengths(fi.data)
+}
+
+func (fi *FuncInfo) NumFile() uint32 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return fi.lengths.NumFile
+}
+
+func (fi *FuncInfo) File(k int) goobj.CUFileIndex {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return (*goobj.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k))
+}
+
+// TopFrame returns true if the function associated with this FuncInfo
+// is an entry point, meaning that unwinders should stop when they hit
+// this function.
+func (fi *FuncInfo) TopFrame() bool {
+ return (fi.FuncFlag() & objabi.FuncFlag_TOPFRAME) != 0
+}
+
+type InlTreeNode struct {
+ Parent int32
+ File goobj.CUFileIndex
+ Line int32
+ Func Sym
+ ParentPC int32
+}
+
+func (fi *FuncInfo) NumInlTree() uint32 {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ return fi.lengths.NumInlTree
+}
+
+func (fi *FuncInfo) InlTree(k int) InlTreeNode {
+ if !fi.lengths.Initialized {
+ panic("need to call Preload first")
+ }
+ node := (*goobj.FuncInfo)(nil).ReadInlTree(fi.data, fi.lengths.InlTreeOff, uint32(k))
+ return InlTreeNode{
+ Parent: node.Parent,
+ File: node.File,
+ Line: node.Line,
+ Func: fi.l.resolve(fi.r, node.Func),
+ ParentPC: node.ParentPC,
+ }
+}
+
+func (l *Loader) FuncInfo(i Sym) FuncInfo {
+ r, auxs := l.auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() == goobj.AuxFuncInfo {
+ b := r.Data(a.Sym().SymIdx)
+ return FuncInfo{l, r, b, goobj.FuncInfoLengths{}}
+ }
+ }
+ return FuncInfo{}
+}
+
+// Preload a package: adds autolib.
+// Does not add defined package or non-packaged symbols to the symbol table.
+// These are done in LoadSyms.
+// Does not read symbol data.
+// Returns the fingerprint of the object.
+func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64) goobj.FingerprintType {
+ roObject, readonly, err := f.Slice(uint64(length)) // TODO: no need to map blocks that are for tools only (e.g. RefName)
+ if err != nil {
+ log.Fatal("cannot read object file:", err)
+ }
+ r := goobj.NewReaderFromBytes(roObject, readonly)
+ if r == nil {
+ if len(roObject) >= 8 && bytes.Equal(roObject[:8], []byte("\x00go114ld")) {
+ log.Fatalf("found object file %s in old format", f.File().Name())
+ }
+ panic("cannot read object file")
+ }
+ pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
+ ndef := r.NSym()
+ nhashed64def := r.NHashed64def()
+ nhasheddef := r.NHasheddef()
+ or := &oReader{
+ Reader: r,
+ unit: unit,
+ version: localSymVersion,
+ pkgprefix: pkgprefix,
+ syms: make([]Sym, ndef+nhashed64def+nhasheddef+r.NNonpkgdef()+r.NNonpkgref()),
+ ndef: ndef,
+ nhasheddef: nhasheddef,
+ nhashed64def: nhashed64def,
+ objidx: uint32(len(l.objs)),
+ }
+
+ if r.Unlinkable() {
+ log.Fatalf("link: unlinkable object (from package %s) - compiler requires -p flag", lib.Pkg)
+ }
+
+ // Autolib
+ lib.Autolib = append(lib.Autolib, r.Autolib()...)
+
+ // DWARF file table
+ nfile := r.NFile()
+ unit.FileTable = make([]string, nfile)
+ for i := range unit.FileTable {
+ unit.FileTable[i] = r.File(i)
+ }
+
+ l.addObj(lib.Pkg, or)
+
+ // The caller expects us consuming all the data
+ f.MustSeek(length, io.SeekCurrent)
+
+ return r.Fingerprint()
+}
+
+// Holds the loader along with temporary states for loading symbols.
+type loadState struct {
+ l *Loader
+ hashed64Syms map[uint64]symAndSize // short hashed (content-addressable) symbols, keyed by content hash
+ hashedSyms map[goobj.HashType]symAndSize // hashed (content-addressable) symbols, keyed by content hash
+}
+
+// Preload symbols of given kind from an object.
+func (st *loadState) preloadSyms(r *oReader, kind int) {
+ l := st.l
+ var start, end uint32
+ switch kind {
+ case pkgDef:
+ start = 0
+ end = uint32(r.ndef)
+ case hashed64Def:
+ start = uint32(r.ndef)
+ end = uint32(r.ndef + r.nhashed64def)
+ case hashedDef:
+ start = uint32(r.ndef + r.nhashed64def)
+ end = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
+ case nonPkgDef:
+ start = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
+ end = uint32(r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef())
+ default:
+ panic("preloadSyms: bad kind")
+ }
+ l.growAttrBitmaps(len(l.objSyms) + int(end-start))
+ loadingRuntimePkg := r.unit.Lib.Pkg == "runtime"
+ for i := start; i < end; i++ {
+ osym := r.Sym(i)
+ var name string
+ var v int
+ if kind != hashed64Def && kind != hashedDef { // we don't need the name, etc. for hashed symbols
+ name = osym.Name(r.Reader)
+ v = abiToVer(osym.ABI(), r.version)
+ }
+ gi := st.addSym(name, v, r, i, kind, osym)
+ r.syms[i] = gi
+ if osym.Local() {
+ l.SetAttrLocal(gi, true)
+ }
+ if osym.UsedInIface() {
+ l.SetAttrUsedInIface(gi, true)
+ }
+ if strings.HasPrefix(name, "runtime.") ||
+ (loadingRuntimePkg && strings.HasPrefix(name, "type:")) {
+ if bi := goobj.BuiltinIdx(name, int(osym.ABI())); bi != -1 {
+ // This is a definition of a builtin symbol. Record where it is.
+ l.builtinSyms[bi] = gi
+ }
+ }
+ if a := int32(osym.Align()); a != 0 && a > l.SymAlign(gi) {
+ l.SetSymAlign(gi, a)
+ }
+ }
+}
+
+// Add syms, hashed (content-addressable) symbols, non-package symbols, and
+// references to external symbols (which are always named).
+func (l *Loader) LoadSyms(arch *sys.Arch) {
+ // Allocate space for symbols, making a guess as to how much space we need.
+ // This function was determined empirically by looking at the cmd/compile on
+ // Darwin, and picking factors for hashed and hashed64 syms.
+ var symSize, hashedSize, hashed64Size int
+ for _, o := range l.objs[goObjStart:] {
+ symSize += o.r.ndef + o.r.nhasheddef/2 + o.r.nhashed64def/2 + o.r.NNonpkgdef()
+ hashedSize += o.r.nhasheddef / 2
+ hashed64Size += o.r.nhashed64def / 2
+ }
+ // Index 0 is invalid for symbols.
+ l.objSyms = make([]objSym, 1, symSize)
+
+ st := loadState{
+ l: l,
+ hashed64Syms: make(map[uint64]symAndSize, hashed64Size),
+ hashedSyms: make(map[goobj.HashType]symAndSize, hashedSize),
+ }
+
+ for _, o := range l.objs[goObjStart:] {
+ st.preloadSyms(o.r, pkgDef)
+ }
+ l.npkgsyms = l.NSym()
+ for _, o := range l.objs[goObjStart:] {
+ st.preloadSyms(o.r, hashed64Def)
+ st.preloadSyms(o.r, hashedDef)
+ st.preloadSyms(o.r, nonPkgDef)
+ }
+ l.nhashedsyms = len(st.hashed64Syms) + len(st.hashedSyms)
+ for _, o := range l.objs[goObjStart:] {
+ loadObjRefs(l, o.r, arch)
+ }
+ l.values = make([]int64, l.NSym(), l.NSym()+1000) // +1000 make some room for external symbols
+}
+
+func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
+ // load non-package refs
+ ndef := uint32(r.NAlldef())
+ for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ {
+ osym := r.Sym(ndef + i)
+ name := osym.Name(r.Reader)
+ v := abiToVer(osym.ABI(), r.version)
+ r.syms[ndef+i] = l.LookupOrCreateSym(name, v)
+ gi := r.syms[ndef+i]
+ if osym.Local() {
+ l.SetAttrLocal(gi, true)
+ }
+ if osym.UsedInIface() {
+ l.SetAttrUsedInIface(gi, true)
+ }
+ }
+
+ // referenced packages
+ npkg := r.NPkg()
+ r.pkg = make([]uint32, npkg)
+ for i := 1; i < npkg; i++ { // PkgIdx 0 is a dummy invalid package
+ pkg := r.Pkg(i)
+ objidx, ok := l.objByPkg[pkg]
+ if !ok {
+ log.Fatalf("%v: reference to nonexistent package %s", r.unit.Lib, pkg)
+ }
+ r.pkg[i] = objidx
+ }
+
+ // load flags of package refs
+ for i, n := 0, r.NRefFlags(); i < n; i++ {
+ rf := r.RefFlags(i)
+ gi := l.resolve(r, rf.Sym())
+ if rf.Flag2()&goobj.SymFlagUsedInIface != 0 {
+ l.SetAttrUsedInIface(gi, true)
+ }
+ }
+}
+
+func abiToVer(abi uint16, localSymVersion int) int {
+ var v int
+ if abi == goobj.SymABIstatic {
+ // Static
+ v = localSymVersion
+ } else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
+ // Note that data symbols are "ABI0", which maps to version 0.
+ v = abiver
+ } else {
+ log.Fatalf("invalid symbol ABI: %d", abi)
+ }
+ return v
+}
+
+// TopLevelSym tests a symbol (by name and kind) to determine whether
+// the symbol first class sym (participating in the link) or is an
+// anonymous aux or sub-symbol containing some sub-part or payload of
+// another symbol.
+func (l *Loader) TopLevelSym(s Sym) bool {
+ return topLevelSym(l.SymName(s), l.SymType(s))
+}
+
+// topLevelSym tests a symbol name and kind to determine whether
+// the symbol first class sym (participating in the link) or is an
+// anonymous aux or sub-symbol containing some sub-part or payload of
+// another symbol.
+func topLevelSym(sname string, skind sym.SymKind) bool {
+ if sname != "" {
+ return true
+ }
+ switch skind {
+ case sym.SDWARFFCN, sym.SDWARFABSFCN, sym.SDWARFTYPE, sym.SDWARFCONST, sym.SDWARFCUINFO, sym.SDWARFRANGE, sym.SDWARFLOC, sym.SDWARFLINES, sym.SGOFUNC:
+ return true
+ default:
+ return false
+ }
+}
+
+// cloneToExternal takes the existing object file symbol (symIdx)
+// and creates a new external symbol payload that is a clone with
+// respect to name, version, type, relocations, etc. The idea here
+// is that if the linker decides it wants to update the contents of
+// a symbol originally discovered as part of an object file, it's
+// easier to do this if we make the updates to an external symbol
+// payload.
+func (l *Loader) cloneToExternal(symIdx Sym) {
+ if l.IsExternal(symIdx) {
+ panic("sym is already external, no need for clone")
+ }
+
+ // Read the particulars from object.
+ r, li := l.toLocal(symIdx)
+ osym := r.Sym(li)
+ sname := osym.Name(r.Reader)
+ sver := abiToVer(osym.ABI(), r.version)
+ skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+
+ // Create new symbol, update version and kind.
+ pi := l.newPayload(sname, sver)
+ pp := l.payloads[pi]
+ pp.kind = skind
+ pp.ver = sver
+ pp.size = int64(osym.Siz())
+ pp.objidx = r.objidx
+
+ // If this is a def, then copy the guts. We expect this case
+ // to be very rare (one case it may come up is with -X).
+ if li < uint32(r.NAlldef()) {
+
+ // Copy relocations
+ relocs := l.Relocs(symIdx)
+ pp.relocs = make([]goobj.Reloc, relocs.Count())
+ for i := range pp.relocs {
+ // Copy the relocs slice.
+ // Convert local reference to global reference.
+ rel := relocs.At(i)
+ pp.relocs[i].Set(rel.Off(), rel.Siz(), uint16(rel.Type()), rel.Add(), goobj.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())})
+ }
+
+ // Copy data
+ pp.data = r.Data(li)
+ }
+
+ // If we're overriding a data symbol, collect the associated
+ // Gotype, so as to propagate it to the new symbol.
+ auxs := r.Auxs(li)
+ pp.auxs = auxs
+
+ // Install new payload to global index space.
+ // (This needs to happen at the end, as the accessors above
+ // need to access the old symbol content.)
+ l.objSyms[symIdx] = objSym{l.extReader.objidx, uint32(pi)}
+ l.extReader.syms = append(l.extReader.syms, symIdx)
+
+ // Some attributes were encoded in the object file. Copy them over.
+ l.SetAttrDuplicateOK(symIdx, r.Sym(li).Dupok())
+ l.SetAttrShared(symIdx, r.Shared())
+}
+
+// Copy the payload of symbol src to dst. Both src and dst must be external
+// symbols.
+// The intended use case is that when building/linking against a shared library,
+// where we do symbol name mangling, the Go object file may have reference to
+// the original symbol name whereas the shared library provides a symbol with
+// the mangled name. When we do mangling, we copy payload of mangled to original.
+func (l *Loader) CopySym(src, dst Sym) {
+ if !l.IsExternal(dst) {
+ panic("dst is not external") //l.newExtSym(l.SymName(dst), l.SymVersion(dst))
+ }
+ if !l.IsExternal(src) {
+ panic("src is not external") //l.cloneToExternal(src)
+ }
+ l.payloads[l.extIndex(dst)] = l.payloads[l.extIndex(src)]
+ l.SetSymPkg(dst, l.SymPkg(src))
+ // TODO: other attributes?
+}
+
+// CreateExtSym creates a new external symbol with the specified name
+// without adding it to any lookup tables, returning a Sym index for it.
+func (l *Loader) CreateExtSym(name string, ver int) Sym {
+ return l.newExtSym(name, ver)
+}
+
+// CreateStaticSym creates a new static symbol with the specified name
+// without adding it to any lookup tables, returning a Sym index for it.
+func (l *Loader) CreateStaticSym(name string) Sym {
+ // Assign a new unique negative version -- this is to mark the
+ // symbol so that it is not included in the name lookup table.
+ l.anonVersion--
+ return l.newExtSym(name, l.anonVersion)
+}
+
+func (l *Loader) FreeSym(i Sym) {
+ if l.IsExternal(i) {
+ pp := l.getPayload(i)
+ *pp = extSymPayload{}
+ }
+}
+
+// relocId is essentially a <S,R> tuple identifying the Rth
+// relocation of symbol S.
+type relocId struct {
+ sym Sym
+ ridx int
+}
+
+// SetRelocVariant sets the 'variant' property of a relocation on
+// some specific symbol.
+func (l *Loader) SetRelocVariant(s Sym, ri int, v sym.RelocVariant) {
+ // sanity check
+ if relocs := l.Relocs(s); ri >= relocs.Count() {
+ panic("invalid relocation ID")
+ }
+ if l.relocVariant == nil {
+ l.relocVariant = make(map[relocId]sym.RelocVariant)
+ }
+ if v != 0 {
+ l.relocVariant[relocId{s, ri}] = v
+ } else {
+ delete(l.relocVariant, relocId{s, ri})
+ }
+}
+
+// RelocVariant returns the 'variant' property of a relocation on
+// some specific symbol.
+func (l *Loader) RelocVariant(s Sym, ri int) sym.RelocVariant {
+ return l.relocVariant[relocId{s, ri}]
+}
+
+// UndefinedRelocTargets iterates through the global symbol index
+// space, looking for symbols with relocations targeting undefined
+// references. The linker's loadlib method uses this to determine if
+// there are unresolved references to functions in system libraries
+// (for example, libgcc.a), presumably due to CGO code. Return value
+// is a pair of lists of loader.Sym's. First list corresponds to the
+// corresponding to the undefined symbols themselves, the second list
+// is the symbol that is making a reference to the undef. The "limit"
+// param controls the maximum number of results returned; if "limit"
+// is -1, then all undefs are returned.
+func (l *Loader) UndefinedRelocTargets(limit int) ([]Sym, []Sym) {
+ result, fromr := []Sym{}, []Sym{}
+outerloop:
+ for si := Sym(1); si < Sym(len(l.objSyms)); si++ {
+ relocs := l.Relocs(si)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ rs := r.Sym()
+ if rs != 0 && l.SymType(rs) == sym.SXREF && l.SymName(rs) != ".got" {
+ result = append(result, rs)
+ fromr = append(fromr, si)
+ if limit != -1 && len(result) >= limit {
+ break outerloop
+ }
+ }
+ }
+ }
+ return result, fromr
+}
+
+// AssignTextSymbolOrder populates the Textp slices within each
+// library and compilation unit, insuring that packages are laid down
+// in dependency order (internal first, then everything else). Return value
+// is a slice of all text syms.
+func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, extsyms []Sym) []Sym {
+
+ // Library Textp lists should be empty at this point.
+ for _, lib := range libs {
+ if len(lib.Textp) != 0 {
+ panic("expected empty Textp slice for library")
+ }
+ if len(lib.DupTextSyms) != 0 {
+ panic("expected empty DupTextSyms slice for library")
+ }
+ }
+
+ // Used to record which dupok symbol we've assigned to a unit.
+ // Can't use the onlist attribute here because it will need to
+ // clear for the later assignment of the sym.Symbol to a unit.
+ // NB: we can convert to using onList once we no longer have to
+ // call the regular addToTextp.
+ assignedToUnit := MakeBitmap(l.NSym() + 1)
+
+ // Start off textp with reachable external syms.
+ textp := []Sym{}
+ for _, sym := range extsyms {
+ if !l.attrReachable.Has(sym) {
+ continue
+ }
+ textp = append(textp, sym)
+ }
+
+ // Walk through all text symbols from Go object files and append
+ // them to their corresponding library's textp list.
+ for _, o := range l.objs[goObjStart:] {
+ r := o.r
+ lib := r.unit.Lib
+ for i, n := uint32(0), uint32(r.NAlldef()); i < n; i++ {
+ gi := l.toGlobal(r, i)
+ if !l.attrReachable.Has(gi) {
+ continue
+ }
+ osym := r.Sym(i)
+ st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+ if st != sym.STEXT {
+ continue
+ }
+ dupok := osym.Dupok()
+ if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
+ // A dupok text symbol is resolved to another package.
+ // We still need to record its presence in the current
+ // package, as the trampoline pass expects packages
+ // are laid out in dependency order.
+ lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
+ continue // symbol in different object
+ }
+ if dupok {
+ lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
+ continue
+ }
+
+ lib.Textp = append(lib.Textp, sym.LoaderSym(gi))
+ }
+ }
+
+ // Now assemble global textp, and assign text symbols to units.
+ for _, doInternal := range [2]bool{true, false} {
+ for idx, lib := range libs {
+ if intlibs[idx] != doInternal {
+ continue
+ }
+ lists := [2][]sym.LoaderSym{lib.Textp, lib.DupTextSyms}
+ for i, list := range lists {
+ for _, s := range list {
+ sym := Sym(s)
+ if !assignedToUnit.Has(sym) {
+ textp = append(textp, sym)
+ unit := l.SymUnit(sym)
+ if unit != nil {
+ unit.Textp = append(unit.Textp, s)
+ assignedToUnit.Set(sym)
+ }
+ // Dupok symbols may be defined in multiple packages; the
+ // associated package for a dupok sym is chosen sort of
+ // arbitrarily (the first containing package that the linker
+ // loads). Canonicalizes its Pkg to the package with which
+ // it will be laid down in text.
+ if i == 1 /* DupTextSyms2 */ && l.SymPkg(sym) != lib.Pkg {
+ l.SetSymPkg(sym, lib.Pkg)
+ }
+ }
+ }
+ }
+ lib.Textp = nil
+ lib.DupTextSyms = nil
+ }
+ }
+
+ return textp
+}
+
+// ErrorReporter is a helper class for reporting errors.
+type ErrorReporter struct {
+ ldr *Loader
+ AfterErrorAction func()
+}
+
+// Errorf method logs an error message.
+//
+// After each error, the error actions function will be invoked; this
+// will either terminate the link immediately (if -h option given)
+// or it will keep a count and exit if more than 20 errors have been printed.
+//
+// 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,
+ // due to the use of LinkString for names of instantiating types.
+ format = strings.Replace(reporter.ldr.SymName(s), "%", "%%", -1) + ": " + format
+ } else {
+ format = fmt.Sprintf("sym %d: %s", s, format)
+ }
+ format += "\n"
+ fmt.Fprintf(os.Stderr, format, args...)
+ reporter.AfterErrorAction()
+}
+
+// GetErrorReporter returns the loader's associated error reporter.
+func (l *Loader) GetErrorReporter() *ErrorReporter {
+ return l.errorReporter
+}
+
+// Errorf method logs an error message. See ErrorReporter.Errorf for details.
+func (l *Loader) Errorf(s Sym, format string, args ...interface{}) {
+ l.errorReporter.Errorf(s, format, args...)
+}
+
+// Symbol statistics.
+func (l *Loader) Stat() string {
+ s := fmt.Sprintf("%d symbols, %d reachable\n", l.NSym(), l.NReachableSym())
+ s += fmt.Sprintf("\t%d package symbols, %d hashed symbols, %d non-package symbols, %d external symbols\n",
+ l.npkgsyms, l.nhashedsyms, int(l.extStart)-l.npkgsyms-l.nhashedsyms, l.NSym()-int(l.extStart))
+ return s
+}
+
+// For debugging.
+func (l *Loader) Dump() {
+ fmt.Println("objs")
+ for _, obj := range l.objs[goObjStart:] {
+ if obj.r != nil {
+ fmt.Println(obj.i, obj.r.unit.Lib)
+ }
+ }
+ fmt.Println("extStart:", l.extStart)
+ fmt.Println("Nsyms:", len(l.objSyms))
+ fmt.Println("syms")
+ for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
+ pi := ""
+ if l.IsExternal(i) {
+ pi = fmt.Sprintf("<ext %d>", l.extIndex(i))
+ }
+ sect := ""
+ if l.SymSect(i) != nil {
+ sect = l.SymSect(i).Name
+ }
+ fmt.Printf("%v %v %v %v %x %v\n", i, l.SymName(i), l.SymType(i), pi, l.SymValue(i), sect)
+ }
+ fmt.Println("symsByName")
+ for name, i := range l.symsByName[0] {
+ fmt.Println(i, name, 0)
+ }
+ for name, i := range l.symsByName[1] {
+ fmt.Println(i, name, 1)
+ }
+ fmt.Println("payloads:")
+ for i := range l.payloads {
+ pp := l.payloads[i]
+ fmt.Println(i, pp.name, pp.ver, pp.kind)
+ }
+}
diff --git a/src/cmd/link/internal/loader/loader_test.go b/src/cmd/link/internal/loader/loader_test.go
new file mode 100644
index 0000000..7d1031e
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader_test.go
@@ -0,0 +1,455 @@
+// Copyright 2019 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.
+
+package loader
+
+import (
+ "bytes"
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "fmt"
+ "testing"
+)
+
+// dummyAddSym adds the named symbol to the loader as if it had been
+// read from a Go object file. Note that it allocates a global
+// index without creating an associated object reader, so one can't
+// do anything interesting with this symbol (such as look at its
+// data or relocations).
+func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym {
+ idx := uint32(len(ldr.objSyms))
+ st := loadState{l: ldr}
+ return st.addSym(name, 0, or, idx, nonPkgDef, &goobj.Sym{})
+}
+
+func mkLoader() *Loader {
+ edummy := func(str string, off int) {}
+ er := ErrorReporter{}
+ ldr := NewLoader(0, edummy, &er)
+ er.ldr = ldr
+ return ldr
+}
+
+func TestAddMaterializedSymbol(t *testing.T) {
+ ldr := mkLoader()
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Create some syms from a dummy object file symbol to get things going.
+ ts1 := addDummyObjSym(t, ldr, or, "type:uint8")
+ ts2 := addDummyObjSym(t, ldr, or, "mumble")
+ ts3 := addDummyObjSym(t, ldr, or, "type:string")
+
+ // Create some external symbols.
+ es1 := ldr.LookupOrCreateSym("extnew1", 0)
+ if es1 == 0 {
+ t.Fatalf("LookupOrCreateSym failed for extnew1")
+ }
+ es1x := ldr.LookupOrCreateSym("extnew1", 0)
+ if es1x != es1 {
+ t.Fatalf("LookupOrCreateSym lookup: expected %d got %d for second lookup", es1, es1x)
+ }
+ es2 := ldr.LookupOrCreateSym("go:info.type.uint8", 0)
+ if es2 == 0 {
+ t.Fatalf("LookupOrCreateSym failed for go.info.type.uint8")
+ }
+ // Create a nameless symbol
+ es3 := ldr.CreateStaticSym("")
+ if es3 == 0 {
+ t.Fatalf("CreateStaticSym failed for nameless sym")
+ }
+
+ // Grab symbol builder pointers
+ sb1 := ldr.MakeSymbolUpdater(es1)
+ sb2 := ldr.MakeSymbolUpdater(es2)
+ sb3 := ldr.MakeSymbolUpdater(es3)
+
+ // Suppose we create some more symbols, which triggers a grow.
+ // Make sure the symbol builder's payload pointer is valid,
+ // even across a grow.
+ for i := 0; i < 9999; i++ {
+ ldr.CreateStaticSym("dummy")
+ }
+
+ // Check get/set symbol type
+ es3typ := sb3.Type()
+ if es3typ != sym.Sxxx {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.Sxxx, es3typ)
+ }
+ sb3.SetType(sym.SRODATA)
+ es3typ = sb3.Type()
+ if es3typ != sym.SRODATA {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+ }
+ es3typ = ldr.SymType(es3)
+ if es3typ != sym.SRODATA {
+ t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
+ }
+
+ // New symbols should not initially be reachable.
+ if ldr.AttrReachable(es1) || ldr.AttrReachable(es2) || ldr.AttrReachable(es3) {
+ t.Errorf("newly materialized symbols should not be reachable")
+ }
+
+ // ... however it should be possible to set/unset their reachability.
+ ldr.SetAttrReachable(es3, true)
+ if !ldr.AttrReachable(es3) {
+ t.Errorf("expected reachable symbol after update")
+ }
+ ldr.SetAttrReachable(es3, false)
+ if ldr.AttrReachable(es3) {
+ t.Errorf("expected unreachable symbol after update")
+ }
+
+ // Test expansion of attr bitmaps
+ for idx := 0; idx < 36; idx++ {
+ es := ldr.LookupOrCreateSym(fmt.Sprintf("zext%d", idx), 0)
+ if ldr.AttrOnList(es) {
+ t.Errorf("expected OnList after creation")
+ }
+ ldr.SetAttrOnList(es, true)
+ if !ldr.AttrOnList(es) {
+ t.Errorf("expected !OnList after update")
+ }
+ if ldr.AttrDuplicateOK(es) {
+ t.Errorf("expected DupOK after creation")
+ }
+ ldr.SetAttrDuplicateOK(es, true)
+ if !ldr.AttrDuplicateOK(es) {
+ t.Errorf("expected !DupOK after update")
+ }
+ }
+
+ sb1 = ldr.MakeSymbolUpdater(es1)
+ sb2 = ldr.MakeSymbolUpdater(es2)
+
+ // Get/set a few other attributes
+ if ldr.AttrVisibilityHidden(es3) {
+ t.Errorf("expected initially not hidden")
+ }
+ ldr.SetAttrVisibilityHidden(es3, true)
+ if !ldr.AttrVisibilityHidden(es3) {
+ t.Errorf("expected hidden after update")
+ }
+
+ // Test get/set symbol value.
+ toTest := []Sym{ts2, es3}
+ for i, s := range toTest {
+ if v := ldr.SymValue(s); v != 0 {
+ t.Errorf("ldr.Value(%d): expected 0 got %d\n", s, v)
+ }
+ nv := int64(i + 101)
+ ldr.SetSymValue(s, nv)
+ if v := ldr.SymValue(s); v != nv {
+ t.Errorf("ldr.SetValue(%d,%d): expected %d got %d\n", s, nv, nv, v)
+ }
+ }
+
+ // Check/set alignment
+ es3al := ldr.SymAlign(es3)
+ if es3al != 0 {
+ t.Errorf("SymAlign(es3): expected 0, got %d", es3al)
+ }
+ ldr.SetSymAlign(es3, 128)
+ es3al = ldr.SymAlign(es3)
+ if es3al != 128 {
+ t.Errorf("SymAlign(es3): expected 128, got %d", es3al)
+ }
+
+ // Add some relocations to the new symbols.
+ r1, _ := sb1.AddRel(objabi.R_ADDR)
+ r1.SetOff(0)
+ r1.SetSiz(1)
+ r1.SetSym(ts1)
+ r2, _ := sb1.AddRel(objabi.R_CALL)
+ r2.SetOff(3)
+ r2.SetSiz(8)
+ r2.SetSym(ts2)
+ r3, _ := sb2.AddRel(objabi.R_USETYPE)
+ r3.SetOff(7)
+ r3.SetSiz(1)
+ r3.SetSym(ts3)
+
+ // Add some data to the symbols.
+ d1 := []byte{1, 2, 3}
+ d2 := []byte{4, 5, 6, 7}
+ sb1.AddBytes(d1)
+ sb2.AddBytes(d2)
+
+ // Now invoke the usual loader interfaces to make sure
+ // we're getting the right things back for these symbols.
+ // First relocations...
+ expRel := [][]Reloc{{r1, r2}, {r3}}
+ for k, sb := range []*SymbolBuilder{sb1, sb2} {
+ rsl := sb.Relocs()
+ exp := expRel[k]
+ if !sameRelocSlice(&rsl, exp) {
+ t.Errorf("expected relocs %v, got %v", exp, rsl)
+ }
+ }
+
+ // ... then data.
+ dat := sb2.Data()
+ if bytes.Compare(dat, d2) != 0 {
+ t.Errorf("expected es2 data %v, got %v", d2, dat)
+ }
+
+ // Nameless symbol should still be nameless.
+ es3name := ldr.SymName(es3)
+ if "" != es3name {
+ t.Errorf("expected es3 name of '', got '%s'", es3name)
+ }
+
+ // Read value of materialized symbol.
+ es1val := sb1.Value()
+ if 0 != es1val {
+ t.Errorf("expected es1 value of 0, got %v", es1val)
+ }
+
+ // Test other misc methods
+ irm := ldr.IsReflectMethod(es1)
+ if 0 != es1val {
+ t.Errorf("expected IsReflectMethod(es1) value of 0, got %v", irm)
+ }
+}
+
+func sameRelocSlice(s1 *Relocs, s2 []Reloc) bool {
+ if s1.Count() != len(s2) {
+ return false
+ }
+ for i := 0; i < s1.Count(); i++ {
+ r1 := s1.At(i)
+ r2 := &s2[i]
+ if r1.Sym() != r2.Sym() ||
+ r1.Type() != r2.Type() ||
+ r1.Off() != r2.Off() ||
+ r1.Add() != r2.Add() ||
+ r1.Siz() != r2.Siz() {
+ return false
+ }
+ }
+ return true
+}
+
+type addFunc func(l *Loader, s Sym, s2 Sym) Sym
+
+func mkReloc(l *Loader, typ objabi.RelocType, off int32, siz uint8, add int64, sym Sym) Reloc {
+ r := Reloc{&goobj.Reloc{}, l.extReader, l}
+ r.SetType(typ)
+ r.SetOff(off)
+ r.SetSiz(siz)
+ r.SetAdd(add)
+ r.SetSym(sym)
+ return r
+}
+
+func TestAddDataMethods(t *testing.T) {
+ ldr := mkLoader()
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Populate loader with some symbols.
+ addDummyObjSym(t, ldr, or, "type:uint8")
+ ldr.LookupOrCreateSym("hello", 0)
+
+ arch := sys.ArchAMD64
+ var testpoints = []struct {
+ which string
+ addDataFunc addFunc
+ expData []byte
+ expKind sym.SymKind
+ expRel []Reloc
+ }{
+ {
+ which: "AddUint8",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUint8('a')
+ return s
+ },
+ expData: []byte{'a'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "AddUintXX",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUintXX(arch, 25185, 2)
+ return s
+ },
+ expData: []byte{'a', 'b'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "SetUint8",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddUint8('a')
+ sb.AddUint8('b')
+ sb.SetUint8(arch, 1, 'c')
+ return s
+ },
+ expData: []byte{'a', 'c'},
+ expKind: sym.SDATA,
+ },
+ {
+ which: "AddString",
+ addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.Addstring("hello")
+ return s
+ },
+ expData: []byte{'h', 'e', 'l', 'l', 'o', 0},
+ expKind: sym.SNOPTRDATA,
+ },
+ {
+ which: "AddAddrPlus",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddAddrPlus(arch, s2, 3)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 8, 3, 6)},
+ },
+ {
+ which: "AddAddrPlus4",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddAddrPlus4(arch, s2, 3)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 4, 3, 7)},
+ },
+ {
+ which: "AddCURelativeAddrPlus",
+ addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
+ sb := l.MakeSymbolUpdater(s)
+ sb.AddCURelativeAddrPlus(arch, s2, 7)
+ return s
+ },
+ expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+ expKind: sym.SDATA,
+ expRel: []Reloc{mkReloc(ldr, objabi.R_ADDRCUOFF, 0, 8, 7, 8)},
+ },
+ }
+
+ var pmi Sym
+ for k, tp := range testpoints {
+ name := fmt.Sprintf("new%d", k+1)
+ mi := ldr.LookupOrCreateSym(name, 0)
+ if mi == 0 {
+ t.Fatalf("LookupOrCreateSym failed for '" + name + "'")
+ }
+ mi = tp.addDataFunc(ldr, mi, pmi)
+ if ldr.SymType(mi) != tp.expKind {
+ t.Errorf("testing Loader.%s: expected kind %s got %s",
+ tp.which, tp.expKind, ldr.SymType(mi))
+ }
+ if bytes.Compare(ldr.Data(mi), tp.expData) != 0 {
+ t.Errorf("testing Loader.%s: expected data %v got %v",
+ tp.which, tp.expData, ldr.Data(mi))
+ }
+ relocs := ldr.Relocs(mi)
+ if !sameRelocSlice(&relocs, tp.expRel) {
+ t.Fatalf("testing Loader.%s: got relocslice %+v wanted %+v",
+ tp.which, relocs, tp.expRel)
+ }
+ pmi = mi
+ }
+}
+
+func TestOuterSub(t *testing.T) {
+ ldr := mkLoader()
+ dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
+ or := &dummyOreader
+
+ // Populate loader with some symbols.
+ addDummyObjSym(t, ldr, or, "type:uint8")
+ es1 := ldr.LookupOrCreateSym("outer", 0)
+ ldr.MakeSymbolUpdater(es1).SetSize(101)
+ es2 := ldr.LookupOrCreateSym("sub1", 0)
+ es3 := ldr.LookupOrCreateSym("sub2", 0)
+ es4 := ldr.LookupOrCreateSym("sub3", 0)
+ es5 := ldr.LookupOrCreateSym("sub4", 0)
+ es6 := ldr.LookupOrCreateSym("sub5", 0)
+
+ // Should not have an outer sym initially
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("es1 outer sym set ")
+ }
+ if ldr.SubSym(es2) != 0 {
+ t.Errorf("es2 outer sym set ")
+ }
+
+ // Establish first outer/sub relationship
+ ldr.AddInteriorSym(es1, es2)
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+ }
+ if ldr.OuterSym(es2) != es1 {
+ t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+ }
+ if ldr.SubSym(es1) != es2 {
+ t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es2)
+ }
+ if ldr.SubSym(es2) != 0 {
+ t.Errorf("ldr.SubSym(es2) got %d wanted %d", ldr.SubSym(es2), 0)
+ }
+
+ // Establish second outer/sub relationship
+ ldr.AddInteriorSym(es1, es3)
+ if ldr.OuterSym(es1) != 0 {
+ t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
+ }
+ if ldr.OuterSym(es2) != es1 {
+ t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
+ }
+ if ldr.OuterSym(es3) != es1 {
+ t.Errorf("ldr.OuterSym(es3) got %d wanted %d", ldr.OuterSym(es3), es1)
+ }
+ if ldr.SubSym(es1) != es3 {
+ t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es3)
+ }
+ if ldr.SubSym(es3) != es2 {
+ t.Errorf("ldr.SubSym(es3) got %d wanted %d", ldr.SubSym(es3), es2)
+ }
+
+ // Some more
+ ldr.AddInteriorSym(es1, es4)
+ ldr.AddInteriorSym(es1, es5)
+ ldr.AddInteriorSym(es1, es6)
+
+ // Set values.
+ ldr.SetSymValue(es2, 7)
+ ldr.SetSymValue(es3, 1)
+ ldr.SetSymValue(es4, 13)
+ ldr.SetSymValue(es5, 101)
+ ldr.SetSymValue(es6, 3)
+
+ // Sort
+ news := ldr.SortSub(es1)
+ if news != es3 {
+ t.Errorf("ldr.SortSub leader got %d wanted %d", news, es3)
+ }
+ pv := int64(-1)
+ count := 0
+ for ss := ldr.SubSym(es1); ss != 0; ss = ldr.SubSym(ss) {
+ v := ldr.SymValue(ss)
+ if v <= pv {
+ t.Errorf("ldr.SortSub sortfail at %d: val %d >= prev val %d",
+ ss, v, pv)
+ }
+ pv = v
+ count++
+ }
+ if count != 5 {
+ t.Errorf("expected %d in sub list got %d", 5, count)
+ }
+}
diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go
new file mode 100644
index 0000000..558c0a7
--- /dev/null
+++ b/src/cmd/link/internal/loader/symbolbuilder.go
@@ -0,0 +1,446 @@
+// Copyright 2019 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.
+
+package loader
+
+import (
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "sort"
+)
+
+// SymbolBuilder is a helper designed to help with the construction
+// of new symbol contents.
+type SymbolBuilder struct {
+ *extSymPayload // points to payload being updated
+ symIdx Sym // index of symbol being updated/constructed
+ l *Loader // loader
+}
+
+// MakeSymbolBuilder creates a symbol builder for use in constructing
+// an entirely new symbol.
+func (l *Loader) MakeSymbolBuilder(name string) *SymbolBuilder {
+ // for now assume that any new sym is intended to be static
+ symIdx := l.CreateStaticSym(name)
+ sb := &SymbolBuilder{l: l, symIdx: symIdx}
+ sb.extSymPayload = l.getPayload(symIdx)
+ return sb
+}
+
+// MakeSymbolUpdater creates a symbol builder helper for an existing
+// symbol 'symIdx'. If 'symIdx' is not an external symbol, then create
+// a clone of it (copy name, properties, etc) fix things up so that
+// the lookup tables and caches point to the new version, not the old
+// version.
+func (l *Loader) MakeSymbolUpdater(symIdx Sym) *SymbolBuilder {
+ if symIdx == 0 {
+ panic("can't update the null symbol")
+ }
+ if !l.IsExternal(symIdx) {
+ // Create a clone with the same name/version/kind etc.
+ l.cloneToExternal(symIdx)
+ }
+
+ // Construct updater and return.
+ sb := &SymbolBuilder{l: l, symIdx: symIdx}
+ sb.extSymPayload = l.getPayload(symIdx)
+ return sb
+}
+
+// CreateSymForUpdate creates a symbol with given name and version,
+// returns a CreateSymForUpdate for update. If the symbol already
+// exists, it will update in-place.
+func (l *Loader) CreateSymForUpdate(name string, version int) *SymbolBuilder {
+ s := l.LookupOrCreateSym(name, version)
+ l.SetAttrReachable(s, true)
+ return l.MakeSymbolUpdater(s)
+}
+
+// Getters for properties of the symbol we're working on.
+
+func (sb *SymbolBuilder) Sym() Sym { return sb.symIdx }
+func (sb *SymbolBuilder) Name() string { return sb.name }
+func (sb *SymbolBuilder) Version() int { return sb.ver }
+func (sb *SymbolBuilder) Type() sym.SymKind { return sb.kind }
+func (sb *SymbolBuilder) Size() int64 { return sb.size }
+func (sb *SymbolBuilder) Data() []byte { return sb.data }
+func (sb *SymbolBuilder) Value() int64 { return sb.l.SymValue(sb.symIdx) }
+func (sb *SymbolBuilder) Align() int32 { return sb.l.SymAlign(sb.symIdx) }
+func (sb *SymbolBuilder) Localentry() uint8 { return sb.l.SymLocalentry(sb.symIdx) }
+func (sb *SymbolBuilder) OnList() bool { return sb.l.AttrOnList(sb.symIdx) }
+func (sb *SymbolBuilder) External() bool { return sb.l.AttrExternal(sb.symIdx) }
+func (sb *SymbolBuilder) Extname() string { return sb.l.SymExtname(sb.symIdx) }
+func (sb *SymbolBuilder) CgoExportDynamic() bool { return sb.l.AttrCgoExportDynamic(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimplib() string { return sb.l.SymDynimplib(sb.symIdx) }
+func (sb *SymbolBuilder) Dynimpvers() string { return sb.l.SymDynimpvers(sb.symIdx) }
+func (sb *SymbolBuilder) SubSym() Sym { return sb.l.SubSym(sb.symIdx) }
+func (sb *SymbolBuilder) GoType() Sym { return sb.l.SymGoType(sb.symIdx) }
+func (sb *SymbolBuilder) VisibilityHidden() bool { return sb.l.AttrVisibilityHidden(sb.symIdx) }
+func (sb *SymbolBuilder) Sect() *sym.Section { return sb.l.SymSect(sb.symIdx) }
+
+// Setters for symbol properties.
+
+func (sb *SymbolBuilder) SetType(kind sym.SymKind) { sb.kind = kind }
+func (sb *SymbolBuilder) SetSize(size int64) { sb.size = size }
+func (sb *SymbolBuilder) SetData(data []byte) { sb.data = data }
+func (sb *SymbolBuilder) SetOnList(v bool) { sb.l.SetAttrOnList(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetExternal(v bool) { sb.l.SetAttrExternal(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetValue(v int64) { sb.l.SetSymValue(sb.symIdx, v) }
+func (sb *SymbolBuilder) SetAlign(align int32) { sb.l.SetSymAlign(sb.symIdx, align) }
+func (sb *SymbolBuilder) SetLocalentry(value uint8) { sb.l.SetSymLocalentry(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetExtname(value string) { sb.l.SetSymExtname(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimplib(value string) { sb.l.SetSymDynimplib(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetDynimpvers(value string) { sb.l.SetSymDynimpvers(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetPlt(value int32) { sb.l.SetPlt(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetGot(value int32) { sb.l.SetGot(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetSpecial(value bool) { sb.l.SetAttrSpecial(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetLocal(value bool) { sb.l.SetAttrLocal(sb.symIdx, value) }
+func (sb *SymbolBuilder) SetVisibilityHidden(value bool) {
+ sb.l.SetAttrVisibilityHidden(sb.symIdx, value)
+}
+func (sb *SymbolBuilder) SetNotInSymbolTable(value bool) {
+ sb.l.SetAttrNotInSymbolTable(sb.symIdx, value)
+}
+func (sb *SymbolBuilder) SetSect(sect *sym.Section) { sb.l.SetSymSect(sb.symIdx, sect) }
+
+func (sb *SymbolBuilder) AddBytes(data []byte) {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ sb.data = append(sb.data, data...)
+ sb.size = int64(len(sb.data))
+}
+
+func (sb *SymbolBuilder) Relocs() Relocs {
+ return sb.l.Relocs(sb.symIdx)
+}
+
+// ResetRelocs removes all relocations on this symbol.
+func (sb *SymbolBuilder) ResetRelocs() {
+ sb.relocs = sb.relocs[:0]
+}
+
+// SetRelocType sets the type of the 'i'-th relocation on this sym to 't'
+func (sb *SymbolBuilder) SetRelocType(i int, t objabi.RelocType) {
+ sb.relocs[i].SetType(uint16(t))
+}
+
+// SetRelocSym sets the target sym of the 'i'-th relocation on this sym to 's'
+func (sb *SymbolBuilder) SetRelocSym(i int, tgt Sym) {
+ sb.relocs[i].SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(tgt)})
+}
+
+// SetRelocAdd sets the addend of the 'i'-th relocation on this sym to 'a'
+func (sb *SymbolBuilder) SetRelocAdd(i int, a int64) {
+ sb.relocs[i].SetAdd(a)
+}
+
+// Add n relocations, return a handle to the relocations.
+func (sb *SymbolBuilder) AddRelocs(n int) Relocs {
+ sb.relocs = append(sb.relocs, make([]goobj.Reloc, n)...)
+ return sb.l.Relocs(sb.symIdx)
+}
+
+// Add a relocation with given type, return its handle and index
+// (to set other fields).
+func (sb *SymbolBuilder) AddRel(typ objabi.RelocType) (Reloc, int) {
+ j := len(sb.relocs)
+ sb.relocs = append(sb.relocs, goobj.Reloc{})
+ sb.relocs[j].SetType(uint16(typ))
+ relocs := sb.Relocs()
+ return relocs.At(j), j
+}
+
+// Sort relocations by offset.
+func (sb *SymbolBuilder) SortRelocs() {
+ sort.Sort((*relocsByOff)(sb.extSymPayload))
+}
+
+// Implement sort.Interface
+type relocsByOff extSymPayload
+
+func (p *relocsByOff) Len() int { return len(p.relocs) }
+func (p *relocsByOff) Less(i, j int) bool { return p.relocs[i].Off() < p.relocs[j].Off() }
+func (p *relocsByOff) Swap(i, j int) {
+ p.relocs[i], p.relocs[j] = p.relocs[j], p.relocs[i]
+}
+
+func (sb *SymbolBuilder) Reachable() bool {
+ return sb.l.AttrReachable(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReachable(v bool) {
+ sb.l.SetAttrReachable(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) setReachable() {
+ sb.SetReachable(true)
+}
+
+func (sb *SymbolBuilder) ReadOnly() bool {
+ return sb.l.AttrReadOnly(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetReadOnly(v bool) {
+ sb.l.SetAttrReadOnly(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) DuplicateOK() bool {
+ return sb.l.AttrDuplicateOK(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SetDuplicateOK(v bool) {
+ sb.l.SetAttrDuplicateOK(sb.symIdx, v)
+}
+
+func (sb *SymbolBuilder) Outer() Sym {
+ return sb.l.OuterSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) Sub() Sym {
+ return sb.l.SubSym(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) SortSub() {
+ sb.l.SortSub(sb.symIdx)
+}
+
+func (sb *SymbolBuilder) AddInteriorSym(sub Sym) {
+ sb.l.AddInteriorSym(sb.symIdx, sub)
+}
+
+func (sb *SymbolBuilder) AddUint8(v uint8) int64 {
+ off := sb.size
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ sb.size++
+ sb.data = append(sb.data, v)
+ return off
+}
+
+func (sb *SymbolBuilder) AddUintXX(arch *sys.Arch, v uint64, wid int) int64 {
+ off := sb.size
+ sb.setUintXX(arch, off, v, int64(wid))
+ return off
+}
+
+func (sb *SymbolBuilder) setUintXX(arch *sys.Arch, off int64, v uint64, wid int64) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ if sb.size < off+wid {
+ sb.size = off + wid
+ sb.Grow(sb.size)
+ }
+
+ switch wid {
+ case 1:
+ sb.data[off] = uint8(v)
+ case 2:
+ arch.ByteOrder.PutUint16(sb.data[off:], uint16(v))
+ case 4:
+ arch.ByteOrder.PutUint32(sb.data[off:], uint32(v))
+ case 8:
+ arch.ByteOrder.PutUint64(sb.data[off:], v)
+ }
+
+ return off + wid
+}
+
+func (sb *SymbolBuilder) AddUint16(arch *sys.Arch, v uint16) int64 {
+ return sb.AddUintXX(arch, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) AddUint32(arch *sys.Arch, v uint32) int64 {
+ return sb.AddUintXX(arch, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) AddUint64(arch *sys.Arch, v uint64) int64 {
+ return sb.AddUintXX(arch, v, 8)
+}
+
+func (sb *SymbolBuilder) AddUint(arch *sys.Arch, v uint64) int64 {
+ return sb.AddUintXX(arch, v, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) SetUint8(arch *sys.Arch, r int64, v uint8) int64 {
+ return sb.setUintXX(arch, r, uint64(v), 1)
+}
+
+func (sb *SymbolBuilder) SetUint16(arch *sys.Arch, r int64, v uint16) int64 {
+ return sb.setUintXX(arch, r, uint64(v), 2)
+}
+
+func (sb *SymbolBuilder) SetUint32(arch *sys.Arch, r int64, v uint32) int64 {
+ return sb.setUintXX(arch, r, uint64(v), 4)
+}
+
+func (sb *SymbolBuilder) SetUint(arch *sys.Arch, r int64, v uint64) int64 {
+ return sb.setUintXX(arch, r, v, int64(arch.PtrSize))
+}
+
+func (sb *SymbolBuilder) SetUintptr(arch *sys.Arch, r int64, v uintptr) int64 {
+ return sb.setUintXX(arch, r, uint64(v), int64(arch.PtrSize))
+}
+
+func (sb *SymbolBuilder) SetAddrPlus(arch *sys.Arch, off int64, tgt Sym, add int64) int64 {
+ if sb.Type() == 0 {
+ sb.SetType(sym.SDATA)
+ }
+ if off+int64(arch.PtrSize) > sb.size {
+ sb.size = off + int64(arch.PtrSize)
+ sb.Grow(sb.size)
+ }
+ r, _ := sb.AddRel(objabi.R_ADDR)
+ r.SetSym(tgt)
+ r.SetOff(int32(off))
+ r.SetSiz(uint8(arch.PtrSize))
+ r.SetAdd(add)
+ return off + int64(r.Siz())
+}
+
+func (sb *SymbolBuilder) SetAddr(arch *sys.Arch, off int64, tgt Sym) int64 {
+ return sb.SetAddrPlus(arch, off, tgt, 0)
+}
+
+func (sb *SymbolBuilder) AddStringAt(off int64, str string) int64 {
+ strLen := int64(len(str))
+ if off+strLen > int64(len(sb.data)) {
+ panic("attempt to write past end of buffer")
+ }
+ copy(sb.data[off:off+strLen], str)
+ return off + strLen
+}
+
+// AddCStringAt adds str plus a null terminating byte.
+func (sb *SymbolBuilder) AddCStringAt(off int64, str string) int64 {
+ strLen := int64(len(str))
+ if off+strLen+1 > int64(len(sb.data)) {
+ panic("attempt to write past end of buffer")
+ }
+ copy(sb.data[off:off+strLen], str)
+ sb.data[off+strLen] = 0
+ return off + strLen + 1
+}
+
+func (sb *SymbolBuilder) Addstring(str string) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SNOPTRDATA
+ }
+ r := sb.size
+ if sb.name == ".shstrtab" {
+ // FIXME: find a better mechanism for this
+ sb.l.elfsetstring(str, int(r))
+ }
+ sb.data = append(sb.data, str...)
+ sb.data = append(sb.data, 0)
+ sb.size = int64(len(sb.data))
+ return r
+}
+
+func (sb *SymbolBuilder) SetBytesAt(off int64, b []byte) int64 {
+ datLen := int64(len(b))
+ if off+datLen > int64(len(sb.data)) {
+ panic("attempt to write past end of buffer")
+ }
+ copy(sb.data[off:off+datLen], b)
+ return off + datLen
+}
+
+func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+ if sb.kind == 0 {
+ sb.kind = sym.SDATA
+ }
+ i := sb.size
+
+ sb.size += int64(rsize)
+ sb.Grow(sb.size)
+
+ r, _ := sb.AddRel(typ)
+ r.SetSym(tgt)
+ r.SetOff(int32(i))
+ r.SetSiz(uint8(rsize))
+ r.SetAdd(add)
+
+ return i + int64(rsize)
+}
+
+// Add a symbol reference (relocation) with given type, addend, and size
+// (the most generic form).
+func (sb *SymbolBuilder) AddSymRef(arch *sys.Arch, tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
+ return sb.addSymRef(tgt, add, typ, rsize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_ADDR, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddAddrPlus4(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_ADDR, 4)
+}
+
+func (sb *SymbolBuilder) AddAddr(arch *sys.Arch, tgt Sym) int64 {
+ return sb.AddAddrPlus(arch, tgt, 0)
+}
+
+func (sb *SymbolBuilder) AddPCRelPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_PCREL, 4)
+}
+
+func (sb *SymbolBuilder) AddCURelativeAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 {
+ return sb.addSymRef(tgt, add, objabi.R_ADDRCUOFF, arch.PtrSize)
+}
+
+func (sb *SymbolBuilder) AddSize(arch *sys.Arch, tgt Sym) int64 {
+ return sb.addSymRef(tgt, 0, objabi.R_SIZE, arch.PtrSize)
+}
+
+// GenAddAddrPlusFunc returns a function to be called when capturing
+// a function symbol's address. In later stages of the link (when
+// address assignment is done) when doing internal linking and
+// targeting an executable, we can just emit the address of a function
+// directly instead of generating a relocation. Clients can call
+// this function (setting 'internalExec' based on build mode and target)
+// and then invoke the returned function in roughly the same way that
+// loader.*SymbolBuilder.AddAddrPlus would be used.
+func GenAddAddrPlusFunc(internalExec bool) func(s *SymbolBuilder, arch *sys.Arch, tgt Sym, add int64) int64 {
+ if internalExec {
+ return func(s *SymbolBuilder, arch *sys.Arch, tgt Sym, add int64) int64 {
+ if v := s.l.SymValue(tgt); v != 0 {
+ return s.AddUint(arch, uint64(v+add))
+ }
+ return s.AddAddrPlus(arch, tgt, add)
+ }
+ } else {
+ return (*SymbolBuilder).AddAddrPlus
+ }
+}
+
+func (sb *SymbolBuilder) MakeWritable() {
+ if sb.ReadOnly() {
+ sb.data = append([]byte(nil), sb.data...)
+ sb.l.SetAttrReadOnly(sb.symIdx, false)
+ }
+}
+
+func (sb *SymbolBuilder) AddUleb(v uint64) {
+ if v < 128 { // common case: 1 byte
+ sb.AddUint8(uint8(v))
+ return
+ }
+ for {
+ c := uint8(v & 0x7f)
+ v >>= 7
+ if v != 0 {
+ c |= 0x80
+ }
+ sb.AddUint8(c)
+ if c&0x80 == 0 {
+ break
+ }
+ }
+}
diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go
new file mode 100644
index 0000000..6e78392
--- /dev/null
+++ b/src/cmd/link/internal/loadmacho/ldmacho.go
@@ -0,0 +1,806 @@
+// Copyright 2017 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.
+
+// Package loadmacho implements a Mach-O file reader.
+package loadmacho
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "encoding/binary"
+ "fmt"
+)
+
+/*
+Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
+https://github.com/9fans/plan9port/tree/master/src/libmach/
+
+ Copyright © 2004 Russ Cox.
+ Portions Copyright © 2008-2010 Google Inc.
+ Portions Copyright © 2010 The Go Authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+// TODO(crawshaw): de-duplicate these symbols with cmd/link/internal/ld
+const (
+ MACHO_X86_64_RELOC_UNSIGNED = 0
+ MACHO_X86_64_RELOC_SIGNED = 1
+ MACHO_ARM64_RELOC_ADDEND = 10
+)
+
+type ldMachoObj struct {
+ f *bio.Reader
+ base int64 // off in f where Mach-O begins
+ length int64 // length of Mach-O
+ is64 bool
+ name string
+ e binary.ByteOrder
+ cputype uint
+ subcputype uint
+ filetype uint32
+ flags uint32
+ cmd []ldMachoCmd
+ ncmd uint
+}
+
+type ldMachoCmd struct {
+ type_ int
+ off uint32
+ size uint32
+ seg ldMachoSeg
+ sym ldMachoSymtab
+ dsym ldMachoDysymtab
+}
+
+type ldMachoSeg struct {
+ name string
+ vmaddr uint64
+ vmsize uint64
+ fileoff uint32
+ filesz uint32
+ maxprot uint32
+ initprot uint32
+ nsect uint32
+ flags uint32
+ sect []ldMachoSect
+}
+
+type ldMachoSect struct {
+ name string
+ segname string
+ addr uint64
+ size uint64
+ off uint32
+ align uint32
+ reloff uint32
+ nreloc uint32
+ flags uint32
+ res1 uint32
+ res2 uint32
+ sym loader.Sym
+ rel []ldMachoRel
+}
+
+type ldMachoRel struct {
+ addr uint32
+ symnum uint32
+ pcrel uint8
+ length uint8
+ extrn uint8
+ type_ uint8
+ scattered uint8
+ value uint32
+}
+
+type ldMachoSymtab struct {
+ symoff uint32
+ nsym uint32
+ stroff uint32
+ strsize uint32
+ str []byte
+ sym []ldMachoSym
+}
+
+type ldMachoSym struct {
+ name string
+ type_ uint8
+ sectnum uint8
+ desc uint16
+ kind int8
+ value uint64
+ sym loader.Sym
+}
+
+type ldMachoDysymtab struct {
+ ilocalsym uint32
+ nlocalsym uint32
+ iextdefsym uint32
+ nextdefsym uint32
+ iundefsym uint32
+ nundefsym uint32
+ tocoff uint32
+ ntoc uint32
+ modtaboff uint32
+ nmodtab uint32
+ extrefsymoff uint32
+ nextrefsyms uint32
+ indirectsymoff uint32
+ nindirectsyms uint32
+ extreloff uint32
+ nextrel uint32
+ locreloff uint32
+ nlocrel uint32
+ indir []uint32
+}
+
+// ldMachoSym.type_
+const (
+ N_EXT = 0x01
+ N_TYPE = 0x1e
+ N_STAB = 0xe0
+)
+
+// ldMachoSym.desc
+const (
+ N_WEAK_REF = 0x40
+ N_WEAK_DEF = 0x80
+)
+
+const (
+ LdMachoCpuVax = 1
+ LdMachoCpu68000 = 6
+ LdMachoCpu386 = 7
+ LdMachoCpuAmd64 = 1<<24 | 7
+ LdMachoCpuMips = 8
+ LdMachoCpu98000 = 10
+ LdMachoCpuHppa = 11
+ LdMachoCpuArm = 12
+ LdMachoCpuArm64 = 1<<24 | 12
+ LdMachoCpu88000 = 13
+ LdMachoCpuSparc = 14
+ LdMachoCpu860 = 15
+ LdMachoCpuAlpha = 16
+ LdMachoCpuPower = 18
+ LdMachoCmdSegment = 1
+ LdMachoCmdSymtab = 2
+ LdMachoCmdSymseg = 3
+ LdMachoCmdThread = 4
+ LdMachoCmdDysymtab = 11
+ LdMachoCmdSegment64 = 25
+ LdMachoFileObject = 1
+ LdMachoFileExecutable = 2
+ LdMachoFileFvmlib = 3
+ LdMachoFileCore = 4
+ LdMachoFilePreload = 5
+)
+
+func unpackcmd(p []byte, m *ldMachoObj, c *ldMachoCmd, type_ uint, sz uint) int {
+ e4 := m.e.Uint32
+ e8 := m.e.Uint64
+
+ c.type_ = int(type_)
+ c.size = uint32(sz)
+ switch type_ {
+ default:
+ return -1
+
+ case LdMachoCmdSegment:
+ if sz < 56 {
+ return -1
+ }
+ c.seg.name = cstring(p[8:24])
+ c.seg.vmaddr = uint64(e4(p[24:]))
+ c.seg.vmsize = uint64(e4(p[28:]))
+ c.seg.fileoff = e4(p[32:])
+ c.seg.filesz = e4(p[36:])
+ c.seg.maxprot = e4(p[40:])
+ c.seg.initprot = e4(p[44:])
+ c.seg.nsect = e4(p[48:])
+ c.seg.flags = e4(p[52:])
+ c.seg.sect = make([]ldMachoSect, c.seg.nsect)
+ if uint32(sz) < 56+c.seg.nsect*68 {
+ return -1
+ }
+ p = p[56:]
+ var s *ldMachoSect
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ s = &c.seg.sect[i]
+ s.name = cstring(p[0:16])
+ s.segname = cstring(p[16:32])
+ s.addr = uint64(e4(p[32:]))
+ s.size = uint64(e4(p[36:]))
+ s.off = e4(p[40:])
+ s.align = e4(p[44:])
+ s.reloff = e4(p[48:])
+ s.nreloc = e4(p[52:])
+ s.flags = e4(p[56:])
+ s.res1 = e4(p[60:])
+ s.res2 = e4(p[64:])
+ p = p[68:]
+ }
+
+ case LdMachoCmdSegment64:
+ if sz < 72 {
+ return -1
+ }
+ c.seg.name = cstring(p[8:24])
+ c.seg.vmaddr = e8(p[24:])
+ c.seg.vmsize = e8(p[32:])
+ c.seg.fileoff = uint32(e8(p[40:]))
+ c.seg.filesz = uint32(e8(p[48:]))
+ c.seg.maxprot = e4(p[56:])
+ c.seg.initprot = e4(p[60:])
+ c.seg.nsect = e4(p[64:])
+ c.seg.flags = e4(p[68:])
+ c.seg.sect = make([]ldMachoSect, c.seg.nsect)
+ if uint32(sz) < 72+c.seg.nsect*80 {
+ return -1
+ }
+ p = p[72:]
+ var s *ldMachoSect
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ s = &c.seg.sect[i]
+ s.name = cstring(p[0:16])
+ s.segname = cstring(p[16:32])
+ s.addr = e8(p[32:])
+ s.size = e8(p[40:])
+ s.off = e4(p[48:])
+ s.align = e4(p[52:])
+ s.reloff = e4(p[56:])
+ s.nreloc = e4(p[60:])
+ s.flags = e4(p[64:])
+ s.res1 = e4(p[68:])
+ s.res2 = e4(p[72:])
+
+ // p+76 is reserved
+ p = p[80:]
+ }
+
+ case LdMachoCmdSymtab:
+ if sz < 24 {
+ return -1
+ }
+ c.sym.symoff = e4(p[8:])
+ c.sym.nsym = e4(p[12:])
+ c.sym.stroff = e4(p[16:])
+ c.sym.strsize = e4(p[20:])
+
+ case LdMachoCmdDysymtab:
+ if sz < 80 {
+ return -1
+ }
+ c.dsym.ilocalsym = e4(p[8:])
+ c.dsym.nlocalsym = e4(p[12:])
+ c.dsym.iextdefsym = e4(p[16:])
+ c.dsym.nextdefsym = e4(p[20:])
+ c.dsym.iundefsym = e4(p[24:])
+ c.dsym.nundefsym = e4(p[28:])
+ c.dsym.tocoff = e4(p[32:])
+ c.dsym.ntoc = e4(p[36:])
+ c.dsym.modtaboff = e4(p[40:])
+ c.dsym.nmodtab = e4(p[44:])
+ c.dsym.extrefsymoff = e4(p[48:])
+ c.dsym.nextrefsyms = e4(p[52:])
+ c.dsym.indirectsymoff = e4(p[56:])
+ c.dsym.nindirectsyms = e4(p[60:])
+ c.dsym.extreloff = e4(p[64:])
+ c.dsym.nextrel = e4(p[68:])
+ c.dsym.locreloff = e4(p[72:])
+ c.dsym.nlocrel = e4(p[76:])
+ }
+
+ return 0
+}
+
+func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int {
+ if sect.rel != nil || sect.nreloc == 0 {
+ return 0
+ }
+ rel := make([]ldMachoRel, sect.nreloc)
+ m.f.MustSeek(m.base+int64(sect.reloff), 0)
+ buf, _, err := m.f.Slice(uint64(sect.nreloc * 8))
+ if err != nil {
+ return -1
+ }
+ for i := uint32(0); i < sect.nreloc; i++ {
+ r := &rel[i]
+ p := buf[i*8:]
+ r.addr = m.e.Uint32(p)
+
+ // TODO(rsc): Wrong interpretation for big-endian bitfields?
+ if r.addr&0x80000000 != 0 {
+ // scatterbrained relocation
+ r.scattered = 1
+
+ v := r.addr >> 24
+ r.addr &= 0xFFFFFF
+ r.type_ = uint8(v & 0xF)
+ v >>= 4
+ r.length = 1 << (v & 3)
+ v >>= 2
+ r.pcrel = uint8(v & 1)
+ r.value = m.e.Uint32(p[4:])
+ } else {
+ v := m.e.Uint32(p[4:])
+ r.symnum = v & 0xFFFFFF
+ v >>= 24
+ r.pcrel = uint8(v & 1)
+ v >>= 1
+ r.length = 1 << (v & 3)
+ v >>= 2
+ r.extrn = uint8(v & 1)
+ v >>= 1
+ r.type_ = uint8(v)
+ }
+ }
+
+ sect.rel = rel
+ return 0
+}
+
+func macholoaddsym(m *ldMachoObj, d *ldMachoDysymtab) int {
+ n := int(d.nindirectsyms)
+ m.f.MustSeek(m.base+int64(d.indirectsymoff), 0)
+ p, _, err := m.f.Slice(uint64(n * 4))
+ if err != nil {
+ return -1
+ }
+
+ d.indir = make([]uint32, n)
+ for i := 0; i < n; i++ {
+ d.indir[i] = m.e.Uint32(p[4*i:])
+ }
+ return 0
+}
+
+func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
+ if symtab.sym != nil {
+ return 0
+ }
+
+ m.f.MustSeek(m.base+int64(symtab.stroff), 0)
+ strbuf, _, err := m.f.Slice(uint64(symtab.strsize))
+ if err != nil {
+ return -1
+ }
+
+ symsize := 12
+ if m.is64 {
+ symsize = 16
+ }
+ n := int(symtab.nsym * uint32(symsize))
+ m.f.MustSeek(m.base+int64(symtab.symoff), 0)
+ symbuf, _, err := m.f.Slice(uint64(n))
+ if err != nil {
+ return -1
+ }
+ sym := make([]ldMachoSym, symtab.nsym)
+ p := symbuf
+ for i := uint32(0); i < symtab.nsym; i++ {
+ s := &sym[i]
+ v := m.e.Uint32(p)
+ if v >= symtab.strsize {
+ return -1
+ }
+ s.name = cstring(strbuf[v:])
+ s.type_ = p[4]
+ s.sectnum = p[5]
+ s.desc = m.e.Uint16(p[6:])
+ if m.is64 {
+ s.value = m.e.Uint64(p[8:])
+ } else {
+ s.value = uint64(m.e.Uint32(p[8:]))
+ }
+ p = p[symsize:]
+ }
+
+ symtab.str = strbuf
+ symtab.sym = sym
+ return 0
+}
+
+// Load the Mach-O file pn from f.
+// Symbols are written into syms, and a slice of the text symbols is returned.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
+ return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
+ }
+
+ base := f.Offset()
+
+ hdr, _, err := f.Slice(7 * 4)
+ if err != nil {
+ return errorf("reading hdr: %v", err)
+ }
+
+ var e binary.ByteOrder
+ if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
+ e = binary.BigEndian
+ } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE {
+ e = binary.LittleEndian
+ } else {
+ return errorf("bad magic - not mach-o file")
+ }
+
+ is64 := e.Uint32(hdr[:]) == 0xFEEDFACF
+ ncmd := e.Uint32(hdr[4*4:])
+ cmdsz := e.Uint32(hdr[5*4:])
+ if ncmd > 0x10000 || cmdsz >= 0x01000000 {
+ return errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz)
+ }
+
+ if is64 {
+ f.MustSeek(4, 1) // skip reserved word in header
+ }
+
+ m := &ldMachoObj{
+ f: f,
+ e: e,
+ cputype: uint(e.Uint32(hdr[1*4:])),
+ subcputype: uint(e.Uint32(hdr[2*4:])),
+ filetype: e.Uint32(hdr[3*4:]),
+ ncmd: uint(ncmd),
+ flags: e.Uint32(hdr[6*4:]),
+ is64: is64,
+ base: base,
+ length: length,
+ name: pn,
+ }
+
+ switch arch.Family {
+ default:
+ return errorf("mach-o %s unimplemented", arch.Name)
+ case sys.AMD64:
+ if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 {
+ return errorf("mach-o object but not amd64")
+ }
+ case sys.ARM64:
+ if e != binary.LittleEndian || m.cputype != LdMachoCpuArm64 {
+ return errorf("mach-o object but not arm64")
+ }
+ }
+
+ m.cmd = make([]ldMachoCmd, ncmd)
+ cmdp, _, err := f.Slice(uint64(cmdsz))
+ if err != nil {
+ return errorf("reading cmds: %v", err)
+ }
+
+ // read and parse load commands
+ var c *ldMachoCmd
+
+ var symtab *ldMachoSymtab
+ var dsymtab *ldMachoDysymtab
+
+ off := uint32(len(hdr))
+ for i := uint32(0); i < ncmd; i++ {
+ ty := e.Uint32(cmdp)
+ sz := e.Uint32(cmdp[4:])
+ m.cmd[i].off = off
+ unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz))
+ cmdp = cmdp[sz:]
+ off += sz
+ if ty == LdMachoCmdSymtab {
+ if symtab != nil {
+ return errorf("multiple symbol tables")
+ }
+
+ symtab = &m.cmd[i].sym
+ macholoadsym(m, symtab)
+ }
+
+ if ty == LdMachoCmdDysymtab {
+ dsymtab = &m.cmd[i].dsym
+ macholoaddsym(m, dsymtab)
+ }
+
+ if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) {
+ if c != nil {
+ return errorf("multiple load commands")
+ }
+
+ c = &m.cmd[i]
+ }
+ }
+
+ // load text and data segments into memory.
+ // they are not as small as the load commands, but we'll need
+ // the memory anyway for the symbol images, so we might
+ // as well use one large chunk.
+ if c == nil {
+ return errorf("no load command")
+ }
+
+ if symtab == nil {
+ // our work is done here - no symbols means nothing can refer to this file
+ return
+ }
+
+ if int64(c.seg.fileoff+c.seg.filesz) >= length {
+ return errorf("load segment out of range")
+ }
+
+ f.MustSeek(m.base+int64(c.seg.fileoff), 0)
+ dat, readOnly, err := f.Slice(uint64(c.seg.filesz))
+ if err != nil {
+ return errorf("cannot load object data: %v", err)
+ }
+
+ for i := uint32(0); i < c.seg.nsect; i++ {
+ sect := &c.seg.sect[i]
+ if sect.segname != "__TEXT" && sect.segname != "__DATA" {
+ continue
+ }
+ if sect.name == "__eh_frame" {
+ continue
+ }
+ name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
+ s := l.LookupOrCreateSym(name, localSymVersion)
+ bld := l.MakeSymbolUpdater(s)
+ if bld.Type() != 0 {
+ return errorf("duplicate %s/%s", sect.segname, sect.name)
+ }
+
+ if sect.flags&0xff == 1 { // S_ZEROFILL
+ bld.SetData(make([]byte, sect.size))
+ } else {
+ bld.SetReadOnly(readOnly)
+ bld.SetData(dat[sect.addr-c.seg.vmaddr:][:sect.size])
+ }
+ bld.SetSize(int64(len(bld.Data())))
+
+ if sect.segname == "__TEXT" {
+ if sect.name == "__text" {
+ bld.SetType(sym.STEXT)
+ } else {
+ bld.SetType(sym.SRODATA)
+ }
+ } else {
+ if sect.name == "__bss" {
+ bld.SetType(sym.SNOPTRBSS)
+ bld.SetData(nil)
+ } else {
+ bld.SetType(sym.SNOPTRDATA)
+ }
+ }
+
+ sect.sym = s
+ }
+
+ // enter sub-symbols into symbol table.
+ // have to guess sizes from next symbol.
+ for i := uint32(0); i < symtab.nsym; i++ {
+ machsym := &symtab.sym[i]
+ if machsym.type_&N_STAB != 0 {
+ continue
+ }
+
+ // TODO: check sym->type against outer->type.
+ name := machsym.name
+
+ if name[0] == '_' && name[1] != '\x00' {
+ name = name[1:]
+ }
+ v := 0
+ if machsym.type_&N_EXT == 0 {
+ v = localSymVersion
+ }
+ s := l.LookupOrCreateCgoExport(name, v)
+ if machsym.type_&N_EXT == 0 {
+ l.SetAttrDuplicateOK(s, true)
+ }
+ if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
+ l.SetAttrDuplicateOK(s, true)
+ }
+ machsym.sym = s
+ if machsym.sectnum == 0 { // undefined
+ continue
+ }
+ if uint32(machsym.sectnum) > c.seg.nsect {
+ return errorf("reference to invalid section %d", machsym.sectnum)
+ }
+
+ sect := &c.seg.sect[machsym.sectnum-1]
+ bld := l.MakeSymbolUpdater(s)
+ outer := sect.sym
+ if outer == 0 {
+ continue // ignore reference to invalid section
+ }
+
+ if osym := l.OuterSym(s); osym != 0 {
+ if l.AttrDuplicateOK(s) {
+ continue
+ }
+ return errorf("duplicate symbol reference: %s in both %s and %s", l.SymName(s), l.SymName(osym), l.SymName(sect.sym))
+ }
+
+ bld.SetType(l.SymType(outer))
+ if l.SymSize(outer) != 0 { // skip empty section (0-sized symbol)
+ l.AddInteriorSym(outer, s)
+ }
+
+ bld.SetValue(int64(machsym.value - sect.addr))
+ if !l.AttrCgoExportDynamic(s) {
+ bld.SetDynimplib("") // satisfy dynimport
+ }
+ if l.SymType(outer) == sym.STEXT {
+ if bld.External() && !bld.DuplicateOK() {
+ return errorf("%v: duplicate symbol definition", s)
+ }
+ bld.SetExternal(true)
+ }
+ }
+
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ sect := &c.seg.sect[i]
+ s := sect.sym
+ if s == 0 {
+ continue
+ }
+ bld := l.MakeSymbolUpdater(s)
+ if bld.SubSym() != 0 {
+
+ bld.SortSub()
+
+ // assign sizes, now that we know symbols in sorted order.
+ for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+ s1Bld := l.MakeSymbolUpdater(s1)
+ if sub := l.SubSym(s1); sub != 0 {
+ s1Bld.SetSize(l.SymValue(sub) - l.SymValue(s1))
+ } else {
+ dlen := int64(len(l.Data(s)))
+ s1Bld.SetSize(l.SymValue(s) + dlen - l.SymValue(s1))
+ }
+ }
+ }
+
+ if bld.Type() == sym.STEXT {
+ if bld.OnList() {
+ return errorf("symbol %s listed multiple times", bld.Name())
+ }
+ bld.SetOnList(true)
+ textp = append(textp, s)
+ for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) {
+ if l.AttrOnList(s1) {
+ return errorf("symbol %s listed multiple times", l.SymName(s1))
+ }
+ l.SetAttrOnList(s1, true)
+ textp = append(textp, s1)
+ }
+ }
+ }
+
+ // load relocations
+ for i := 0; uint32(i) < c.seg.nsect; i++ {
+ sect := &c.seg.sect[i]
+ s := sect.sym
+ if s == 0 {
+ continue
+ }
+ macholoadrel(m, sect)
+ if sect.rel == nil {
+ continue
+ }
+
+ sb := l.MakeSymbolUpdater(sect.sym)
+ var rAdd int64
+ for j := uint32(0); j < sect.nreloc; j++ {
+ var (
+ rOff int32
+ rSize uint8
+ rType objabi.RelocType
+ rSym loader.Sym
+ )
+ rel := &sect.rel[j]
+ if rel.scattered != 0 {
+ // mach-o only uses scattered relocation on 32-bit platforms,
+ // which are no longer supported.
+ return errorf("%v: unexpected scattered relocation", s)
+ }
+
+ if arch.Family == sys.ARM64 && rel.type_ == MACHO_ARM64_RELOC_ADDEND {
+ // Two relocations. This addend will be applied to the next one.
+ rAdd = int64(rel.symnum) << 40 >> 40 // convert unsigned 24-bit to signed 24-bit
+ continue
+ }
+
+ rSize = rel.length
+ rType = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel)
+ rOff = int32(rel.addr)
+
+ // Handle X86_64_RELOC_SIGNED referencing a section (rel.extrn == 0).
+ p := l.Data(s)
+ if arch.Family == sys.AMD64 {
+ if rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED {
+ // Calculate the addend as the offset into the section.
+ //
+ // The rip-relative offset stored in the object file is encoded
+ // as follows:
+ //
+ // movsd 0x00000360(%rip),%xmm0
+ //
+ // To get the absolute address of the value this rip-relative address is pointing
+ // to, we must add the address of the next instruction to it. This is done by
+ // taking the address of the relocation and adding 4 to it (since the rip-relative
+ // offset can at most be 32 bits long). To calculate the offset into the section the
+ // relocation is referencing, we subtract the vaddr of the start of the referenced
+ // section found in the original object file.
+ //
+ // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h]
+ secaddr := c.seg.sect[rel.symnum-1].addr
+ rAdd = int64(uint64(int64(int32(e.Uint32(p[rOff:])))+int64(rOff)+4) - secaddr)
+ } else {
+ rAdd = int64(int32(e.Uint32(p[rOff:])))
+ }
+ }
+
+ // An unsigned internal relocation has a value offset
+ // by the section address.
+ if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_UNSIGNED {
+ secaddr := c.seg.sect[rel.symnum-1].addr
+ rAdd -= int64(secaddr)
+ }
+
+ if rel.extrn == 0 {
+ if rel.symnum < 1 || rel.symnum > c.seg.nsect {
+ return errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect)
+ }
+
+ rSym = c.seg.sect[rel.symnum-1].sym
+ if rSym == 0 {
+ return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name)
+ }
+ } else {
+ if rel.symnum >= symtab.nsym {
+ return errorf("invalid relocation: symbol reference out of range")
+ }
+
+ rSym = symtab.sym[rel.symnum].sym
+ }
+
+ r, _ := sb.AddRel(rType)
+ r.SetOff(rOff)
+ r.SetSiz(rSize)
+ r.SetSym(rSym)
+ r.SetAdd(rAdd)
+
+ rAdd = 0 // clear rAdd for next iteration
+ }
+
+ sb.SortRelocs()
+ }
+
+ return textp, nil
+}
+
+func cstring(x []byte) string {
+ i := bytes.IndexByte(x, '\x00')
+ if i >= 0 {
+ x = x[:i]
+ }
+ return string(x)
+}
diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go
new file mode 100644
index 0000000..0d33823
--- /dev/null
+++ b/src/cmd/link/internal/loadpe/ldpe.go
@@ -0,0 +1,777 @@
+// Copyright 2010 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.
+
+// Package loadpe implements a PE/COFF file reader.
+package loadpe
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/pe"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+)
+
+const (
+ // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 (same with IMAGE_SYM_DTYPE_POINTER and IMAGE_SYM_DTYPE_FUNCTION)
+ IMAGE_SYM_UNDEFINED = 0
+ IMAGE_SYM_ABSOLUTE = -1
+ IMAGE_SYM_DEBUG = -2
+ IMAGE_SYM_TYPE_NULL = 0
+ IMAGE_SYM_TYPE_VOID = 1
+ IMAGE_SYM_TYPE_CHAR = 2
+ IMAGE_SYM_TYPE_SHORT = 3
+ IMAGE_SYM_TYPE_INT = 4
+ IMAGE_SYM_TYPE_LONG = 5
+ IMAGE_SYM_TYPE_FLOAT = 6
+ IMAGE_SYM_TYPE_DOUBLE = 7
+ IMAGE_SYM_TYPE_STRUCT = 8
+ IMAGE_SYM_TYPE_UNION = 9
+ IMAGE_SYM_TYPE_ENUM = 10
+ IMAGE_SYM_TYPE_MOE = 11
+ IMAGE_SYM_TYPE_BYTE = 12
+ IMAGE_SYM_TYPE_WORD = 13
+ IMAGE_SYM_TYPE_UINT = 14
+ IMAGE_SYM_TYPE_DWORD = 15
+ IMAGE_SYM_TYPE_PCODE = 32768
+ IMAGE_SYM_DTYPE_NULL = 0
+ IMAGE_SYM_DTYPE_POINTER = 0x10
+ IMAGE_SYM_DTYPE_FUNCTION = 0x20
+ IMAGE_SYM_DTYPE_ARRAY = 0x30
+ IMAGE_SYM_CLASS_END_OF_FUNCTION = -1
+ IMAGE_SYM_CLASS_NULL = 0
+ IMAGE_SYM_CLASS_AUTOMATIC = 1
+ IMAGE_SYM_CLASS_EXTERNAL = 2
+ IMAGE_SYM_CLASS_STATIC = 3
+ IMAGE_SYM_CLASS_REGISTER = 4
+ IMAGE_SYM_CLASS_EXTERNAL_DEF = 5
+ IMAGE_SYM_CLASS_LABEL = 6
+ IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7
+ IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8
+ IMAGE_SYM_CLASS_ARGUMENT = 9
+ IMAGE_SYM_CLASS_STRUCT_TAG = 10
+ IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11
+ IMAGE_SYM_CLASS_UNION_TAG = 12
+ IMAGE_SYM_CLASS_TYPE_DEFINITION = 13
+ IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14
+ IMAGE_SYM_CLASS_ENUM_TAG = 15
+ IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16
+ IMAGE_SYM_CLASS_REGISTER_PARAM = 17
+ IMAGE_SYM_CLASS_BIT_FIELD = 18
+ IMAGE_SYM_CLASS_FAR_EXTERNAL = 68 /* Not in PECOFF v8 spec */
+ IMAGE_SYM_CLASS_BLOCK = 100
+ IMAGE_SYM_CLASS_FUNCTION = 101
+ IMAGE_SYM_CLASS_END_OF_STRUCT = 102
+ IMAGE_SYM_CLASS_FILE = 103
+ IMAGE_SYM_CLASS_SECTION = 104
+ IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105
+ IMAGE_SYM_CLASS_CLR_TOKEN = 107
+ IMAGE_REL_I386_ABSOLUTE = 0x0000
+ IMAGE_REL_I386_DIR16 = 0x0001
+ IMAGE_REL_I386_REL16 = 0x0002
+ IMAGE_REL_I386_DIR32 = 0x0006
+ IMAGE_REL_I386_DIR32NB = 0x0007
+ IMAGE_REL_I386_SEG12 = 0x0009
+ IMAGE_REL_I386_SECTION = 0x000A
+ IMAGE_REL_I386_SECREL = 0x000B
+ IMAGE_REL_I386_TOKEN = 0x000C
+ IMAGE_REL_I386_SECREL7 = 0x000D
+ IMAGE_REL_I386_REL32 = 0x0014
+ IMAGE_REL_AMD64_ABSOLUTE = 0x0000
+ IMAGE_REL_AMD64_ADDR64 = 0x0001
+ IMAGE_REL_AMD64_ADDR32 = 0x0002
+ IMAGE_REL_AMD64_ADDR32NB = 0x0003
+ IMAGE_REL_AMD64_REL32 = 0x0004
+ IMAGE_REL_AMD64_REL32_1 = 0x0005
+ IMAGE_REL_AMD64_REL32_2 = 0x0006
+ IMAGE_REL_AMD64_REL32_3 = 0x0007
+ IMAGE_REL_AMD64_REL32_4 = 0x0008
+ IMAGE_REL_AMD64_REL32_5 = 0x0009
+ IMAGE_REL_AMD64_SECTION = 0x000A
+ IMAGE_REL_AMD64_SECREL = 0x000B
+ IMAGE_REL_AMD64_SECREL7 = 0x000C
+ IMAGE_REL_AMD64_TOKEN = 0x000D
+ IMAGE_REL_AMD64_SREL32 = 0x000E
+ IMAGE_REL_AMD64_PAIR = 0x000F
+ IMAGE_REL_AMD64_SSPAN32 = 0x0010
+ IMAGE_REL_ARM_ABSOLUTE = 0x0000
+ IMAGE_REL_ARM_ADDR32 = 0x0001
+ IMAGE_REL_ARM_ADDR32NB = 0x0002
+ IMAGE_REL_ARM_BRANCH24 = 0x0003
+ IMAGE_REL_ARM_BRANCH11 = 0x0004
+ IMAGE_REL_ARM_SECTION = 0x000E
+ IMAGE_REL_ARM_SECREL = 0x000F
+ IMAGE_REL_ARM_MOV32 = 0x0010
+ IMAGE_REL_THUMB_MOV32 = 0x0011
+ IMAGE_REL_THUMB_BRANCH20 = 0x0012
+ IMAGE_REL_THUMB_BRANCH24 = 0x0014
+ IMAGE_REL_THUMB_BLX23 = 0x0015
+ IMAGE_REL_ARM_PAIR = 0x0016
+ IMAGE_REL_ARM64_ABSOLUTE = 0x0000
+ IMAGE_REL_ARM64_ADDR32 = 0x0001
+ IMAGE_REL_ARM64_ADDR32NB = 0x0002
+ IMAGE_REL_ARM64_BRANCH26 = 0x0003
+ IMAGE_REL_ARM64_PAGEBASE_REL21 = 0x0004
+ IMAGE_REL_ARM64_REL21 = 0x0005
+ IMAGE_REL_ARM64_PAGEOFFSET_12A = 0x0006
+ IMAGE_REL_ARM64_PAGEOFFSET_12L = 0x0007
+ IMAGE_REL_ARM64_SECREL = 0x0008
+ IMAGE_REL_ARM64_SECREL_LOW12A = 0x0009
+ IMAGE_REL_ARM64_SECREL_HIGH12A = 0x000A
+ IMAGE_REL_ARM64_SECREL_LOW12L = 0x000B
+ IMAGE_REL_ARM64_TOKEN = 0x000C
+ IMAGE_REL_ARM64_SECTION = 0x000D
+ IMAGE_REL_ARM64_ADDR64 = 0x000E
+ IMAGE_REL_ARM64_BRANCH19 = 0x000F
+ IMAGE_REL_ARM64_BRANCH14 = 0x0010
+ IMAGE_REL_ARM64_REL32 = 0x0011
+)
+
+const (
+ // When stored into the PLT value for a symbol, this token tells
+ // windynrelocsym to redirect direct references to this symbol to a stub
+ // that loads from the corresponding import symbol and then does
+ // a jump to the loaded value.
+ CreateImportStubPltToken = -2
+
+ // When stored into the GOT value for a import symbol __imp_X this
+ // token tells windynrelocsym to redirect references to the
+ // underlying DYNIMPORT symbol X.
+ RedirectToDynImportGotToken = -2
+)
+
+// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf
+
+// peBiobuf makes bio.Reader look like io.ReaderAt.
+type peBiobuf bio.Reader
+
+func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) {
+ ret := ((*bio.Reader)(f)).MustSeek(off, 0)
+ if ret < 0 {
+ return 0, errors.New("fail to seek")
+ }
+ n, err := f.Read(p)
+ if err != nil {
+ return 0, err
+ }
+ return n, nil
+}
+
+// makeUpdater creates a loader.SymbolBuilder if one hasn't been created previously.
+// We use this to lazily make SymbolBuilders as we don't always need a builder, and creating them for all symbols might be an error.
+func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loader.SymbolBuilder {
+ if bld != nil {
+ return bld
+ }
+ bld = l.MakeSymbolUpdater(s)
+ return bld
+}
+
+// peImportSymsState tracks the set of DLL import symbols we've seen
+// while reading host objects. We create a singleton instance of this
+// type, which will persist across multiple host objects.
+type peImportSymsState struct {
+
+ // Text and non-text sections read in by the host object loader.
+ secSyms []loader.Sym
+
+ // SDYNIMPORT symbols encountered along the way
+ dynimports map[loader.Sym]struct{}
+
+ // Loader and arch, for use in postprocessing.
+ l *loader.Loader
+ arch *sys.Arch
+}
+
+var importSymsState *peImportSymsState
+
+func createImportSymsState(l *loader.Loader, arch *sys.Arch) {
+ if importSymsState != nil {
+ return
+ }
+ importSymsState = &peImportSymsState{
+ dynimports: make(map[loader.Sym]struct{}),
+ l: l,
+ arch: arch,
+ }
+}
+
+// peLoaderState holds various bits of useful state information needed
+// while loading a single PE object file.
+type peLoaderState struct {
+ l *loader.Loader
+ arch *sys.Arch
+ f *pe.File
+ pn string
+ sectsyms map[*pe.Section]loader.Sym
+ 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 from the object file are created via the loader 'l', and
+// 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) {
+ state := &peLoaderState{
+ l: l,
+ arch: arch,
+ sectsyms: make(map[*pe.Section]loader.Sym),
+ sectdata: make(map[*pe.Section][]byte),
+ localSymVersion: localSymVersion,
+ pn: pn,
+ }
+ createImportSymsState(state.l, state.arch)
+
+ // Some input files are archives containing multiple of
+ // object files, and pe.NewFile seeks to the start of
+ // input file and get confused. Create section reader
+ // to stop pe.NewFile looking before current position.
+ sr := io.NewSectionReader((*peBiobuf)(input), input.Offset(), 1<<63-1)
+
+ // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details)
+ f, err := pe.NewFile(sr)
+ if err != nil {
+ 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&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 {
+ continue
+ }
+
+ 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 := state.l.LookupOrCreateCgoExport(name, localSymVersion)
+ bld := l.MakeSymbolUpdater(s)
+
+ 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 pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.bss
+ bld.SetType(sym.SNOPTRBSS)
+
+ case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.data
+ bld.SetType(sym.SNOPTRDATA)
+
+ case pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE | pe.IMAGE_SCN_MEM_READ: //.text
+ bld.SetType(sym.STEXT)
+
+ default:
+ return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
+ }
+
+ if bld.Type() != sym.SNOPTRBSS {
+ data, err := sect.Data()
+ if err != nil {
+ return nil, nil, err
+ }
+ state.sectdata[sect] = data
+ bld.SetData(data)
+ }
+ bld.SetSize(int64(sect.Size))
+ state.sectsyms[sect] = s
+ if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") {
+ rsrc = append(rsrc, s)
+ }
+ }
+
+ // Make a prepass over the symbols to collect info about COMDAT symbols.
+ if err := state.preprocessSymbols(); err != nil {
+ return nil, nil, err
+ }
+
+ // load relocations
+ for _, rsect := range f.Sections {
+ if _, found := state.sectsyms[rsect]; !found {
+ continue
+ }
+ if rsect.NumberOfRelocations == 0 {
+ continue
+ }
+ if rsect.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 {
+ continue
+ }
+ 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(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 := state.readpesym(pesym)
+ if err != nil {
+ return nil, nil, err
+ }
+ if gosym == 0 {
+ name, err := pesym.FullName(f.StringTable)
+ if err != nil {
+ name = string(pesym.Name[:])
+ }
+ return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
+ }
+
+ rSym := gosym
+ rSize := uint8(4)
+ rOff := int32(r.VirtualAddress)
+ var rAdd int64
+ var rType objabi.RelocType
+ switch arch.Family {
+ default:
+ return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
+ case sys.I386, sys.AMD64:
+ switch r.Type {
+ default:
+ 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(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(state.sectdata[rsect][rOff:])))
+
+ case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
+ rSize = 8
+
+ rType = objabi.R_ADDR
+
+ // load addend from image
+ 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, state.sectsyms[rsect], r.Type)
+
+ case IMAGE_REL_ARM_SECREL:
+ rType = objabi.R_PCREL
+
+ 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(state.sectdata[rsect][rOff:])))
+
+ case IMAGE_REL_ARM_BRANCH24:
+ rType = objabi.R_CALLARM
+
+ 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, state.sectsyms[rsect], r.Type)
+
+ case IMAGE_REL_ARM64_ADDR32, IMAGE_REL_ARM64_ADDR32NB:
+ rType = objabi.R_ADDR
+
+ rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
+ }
+ }
+
+ // ld -r could generate multiple section symbols for the
+ // same section but with different values, we have to take
+ // that into account, or in the case of split resources,
+ // the section and its symbols are split into two sections.
+ if issect(pesym) || splitResources {
+ rAdd += int64(pesym.Value)
+ }
+
+ rel, _ := sb.AddRel(rType)
+ rel.SetOff(rOff)
+ rel.SetSiz(rSize)
+ rel.SetSym(rSym)
+ rel.SetAdd(rAdd)
+ }
+
+ sb.SortRelocs()
+ }
+
+ // enter sub-symbols into symbol table.
+ for i, numaux := 0, 0; i < len(f.COFFSymbols); i += numaux + 1 {
+ pesym := &f.COFFSymbols[i]
+
+ numaux = int(pesym.NumberOfAuxSymbols)
+
+ name, err := pesym.FullName(f.StringTable)
+ if err != nil {
+ return nil, nil, err
+ }
+ if name == "" {
+ continue
+ }
+ if issect(pesym) {
+ continue
+ }
+ if int(pesym.SectionNumber) > len(f.Sections) {
+ continue
+ }
+ if pesym.SectionNumber == IMAGE_SYM_DEBUG {
+ continue
+ }
+ if pesym.SectionNumber == IMAGE_SYM_ABSOLUTE && bytes.Equal(pesym.Name[:], []byte("@feat.00")) {
+ // Microsoft's linker looks at whether all input objects have an empty
+ // section called @feat.00. If all of them do, then it enables SEH;
+ // otherwise it doesn't enable that feature. So, since around the Windows
+ // XP SP2 era, most tools that make PE objects just tack on that section,
+ // so that it won't gimp Microsoft's linker logic. Go doesn't support SEH,
+ // so in theory, none of this really matters to us. But actually, if the
+ // linker tries to ingest an object with @feat.00 -- which are produced by
+ // LLVM's resource compiler, for example -- it chokes because of the
+ // IMAGE_SYM_ABSOLUTE section that it doesn't know how to deal with. Since
+ // @feat.00 is just a marking anyway, skip IMAGE_SYM_ABSOLUTE sections that
+ // are called @feat.00.
+ continue
+ }
+ var sect *pe.Section
+ if pesym.SectionNumber > 0 {
+ sect = f.Sections[pesym.SectionNumber-1]
+ if _, found := state.sectsyms[sect]; !found {
+ continue
+ }
+ }
+
+ bld, s, err := state.readpesym(pesym)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if pesym.SectionNumber == 0 { // extern
+ if l.SymType(s) == sym.SXREF && pesym.Value > 0 { // global data
+ bld = makeUpdater(l, bld, s)
+ bld.SetType(sym.SNOPTRDATA)
+ bld.SetSize(int64(pesym.Value))
+ }
+
+ continue
+ } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
+ sect = f.Sections[pesym.SectionNumber-1]
+ if _, found := state.sectsyms[sect]; !found {
+ return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
+ }
+ } else {
+ return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
+ }
+
+ if sect == nil {
+ 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(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 := state.sectsyms[sect]
+ bld.SetType(l.SymType(sectsym))
+ l.AddInteriorSym(sectsym, s)
+ bld.SetValue(int64(pesym.Value))
+ bld.SetSize(4)
+ if l.SymType(sectsym) == sym.STEXT {
+ if bld.External() && !bld.DuplicateOK() {
+ return nil, nil, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
+ }
+ 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 := state.sectsyms[sect]
+ if s == 0 {
+ continue
+ }
+ l.SortSub(s)
+ importSymsState.secSyms = append(importSymsState.secSyms, s)
+ if l.SymType(s) == sym.STEXT {
+ for ; s != 0; s = l.SubSym(s) {
+ if l.AttrOnList(s) {
+ return nil, nil, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
+ }
+ l.SetAttrOnList(s, true)
+ textp = append(textp, s)
+ }
+ }
+ }
+
+ return textp, rsrc, nil
+}
+
+// PostProcessImports works to resolve inconsistencies with DLL import
+// symbols; it is needed when building with more "modern" C compilers
+// with internal linkage.
+//
+// Background: DLL import symbols are data (SNOPTRDATA) symbols whose
+// name is of the form "__imp_XXX", which contain a pointer/reference
+// to symbol XXX. It's possible to have import symbols for both data
+// symbols ("__imp__fmode") and text symbols ("__imp_CreateEventA").
+// In some case import symbols are just references to some external
+// thing, and in other cases we see actual definitions of import
+// symbols when reading host objects.
+//
+// Previous versions of the linker would in most cases immediately
+// "forward" import symbol references, e.g. treat a references to
+// "__imp_XXX" a references to "XXX", however this doesn't work well
+// with more modern compilers, where you can sometimes see import
+// symbols that are defs (as opposed to external refs).
+//
+// The main actions taken below are to search for references to
+// SDYNIMPORT symbols in host object text/data sections and flag the
+// symbols for later fixup. When we see a reference to an import
+// symbol __imp_XYZ where XYZ corresponds to some SDYNIMPORT symbol,
+// we flag the symbol (via GOT setting) so that it can be redirected
+// to XYZ later in windynrelocsym. When we see a direct reference to
+// an SDYNIMPORT symbol XYZ, we also flag the symbol (via PLT setting)
+// to indicated that the reference will need to be redirected to a
+// stub.
+func PostProcessImports() error {
+ ldr := importSymsState.l
+ arch := importSymsState.arch
+ keeprelocneeded := make(map[loader.Sym]loader.Sym)
+ for _, s := range importSymsState.secSyms {
+ isText := ldr.SymType(s) == sym.STEXT
+ relocs := ldr.Relocs(s)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At(i)
+ rs := r.Sym()
+ if ldr.SymType(rs) == sym.SDYNIMPORT {
+ // Tag the symbol for later stub generation.
+ ldr.SetPlt(rs, CreateImportStubPltToken)
+ continue
+ }
+ isym, err := LookupBaseFromImport(rs, ldr, arch)
+ if err != nil {
+ return err
+ }
+ if isym == 0 {
+ continue
+ }
+ if ldr.SymType(isym) != sym.SDYNIMPORT {
+ continue
+ }
+ // For non-text symbols, forward the reference from __imp_X to
+ // X immediately.
+ if !isText {
+ r.SetSym(isym)
+ continue
+ }
+ // Flag this imp symbol to be processed later in windynrelocsym.
+ ldr.SetGot(rs, RedirectToDynImportGotToken)
+ // Consistency check: should be no PLT token here.
+ splt := ldr.SymPlt(rs)
+ if splt != -1 {
+ return fmt.Errorf("internal error: import symbol %q has invalid PLT setting %d", ldr.SymName(rs), splt)
+ }
+ // Flag for dummy relocation.
+ keeprelocneeded[rs] = isym
+ }
+ }
+ for k, v := range keeprelocneeded {
+ sb := ldr.MakeSymbolUpdater(k)
+ r, _ := sb.AddRel(objabi.R_KEEP)
+ r.SetSym(v)
+ }
+ importSymsState = nil
+ return nil
+}
+
+func issect(s *pe.COFFSymbol) bool {
+ return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
+}
+
+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 = state.l.SymName(state.sectsyms[state.f.Sections[pesym.SectionNumber-1]])
+ } else {
+ name = symname
+ // A note on the "_main" exclusion below: the main routine
+ // defined by the Go runtime is named "_main", not "main", so
+ // when reading references to _main from a host object we want
+ // to avoid rewriting "_main" to "main" in this specific
+ // instance. See #issuecomment-1143698749 on #35006 for more
+ // details on this problem.
+ if state.arch.Family == sys.I386 && name[0] == '_' && name != "_main" && !strings.HasPrefix(name, "__imp_") {
+ name = name[1:] // _Name => Name
+ }
+ }
+
+ // remove last @XXX
+ if i := strings.LastIndex(name, "@"); i >= 0 {
+ name = name[:i]
+ }
+
+ var s loader.Sym
+ var bld *loader.SymbolBuilder
+ switch pesym.Type {
+ default:
+ return nil, 0, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type)
+
+ case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
+ switch pesym.StorageClass {
+ case IMAGE_SYM_CLASS_EXTERNAL: //global
+ s = state.l.LookupOrCreateCgoExport(name, 0)
+
+ case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
+ s = state.l.LookupOrCreateCgoExport(name, state.localSymVersion)
+ bld = makeUpdater(state.l, bld, s)
+ bld.SetDuplicateOK(true)
+
+ default:
+ return nil, 0, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass)
+ }
+ }
+
+ 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)
+ }
+
+ 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.
+ 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
+ }
+ 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)
+ }
+ }
+ return nil
+}
+
+// LookupBaseFromImport examines the symbol "s" to see if it
+// corresponds to an import symbol (name of the form "__imp_XYZ") and
+// if so, it looks up the underlying target of the import symbol and
+// returns it. An error is returned if the symbol is of the form
+// "__imp_XYZ" but no XYZ can be found.
+func LookupBaseFromImport(s loader.Sym, ldr *loader.Loader, arch *sys.Arch) (loader.Sym, error) {
+ sname := ldr.SymName(s)
+ if !strings.HasPrefix(sname, "__imp_") {
+ return 0, nil
+ }
+ basename := sname[len("__imp_"):]
+ if arch.Family == sys.I386 && basename[0] == '_' {
+ basename = basename[1:] // _Name => Name
+ }
+ isym := ldr.Lookup(basename, 0)
+ if isym == 0 {
+ return 0, fmt.Errorf("internal error: import symbol %q with no underlying sym", sname)
+ }
+ return isym, nil
+}
diff --git a/src/cmd/link/internal/loadxcoff/ldxcoff.go b/src/cmd/link/internal/loadxcoff/ldxcoff.go
new file mode 100644
index 0000000..920e1c8
--- /dev/null
+++ b/src/cmd/link/internal/loadxcoff/ldxcoff.go
@@ -0,0 +1,228 @@
+// 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.
+
+// Package loadxcoff implements a XCOFF file reader.
+package loadxcoff
+
+import (
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "errors"
+ "fmt"
+ "internal/xcoff"
+)
+
+// ldSection is an XCOFF section with its symbols.
+type ldSection struct {
+ xcoff.Section
+ sym loader.Sym
+}
+
+// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating xcoffBiobuf
+
+// xcoffBiobuf makes bio.Reader look like io.ReaderAt.
+type xcoffBiobuf bio.Reader
+
+func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) {
+ ret := ((*bio.Reader)(f)).MustSeek(off, 0)
+ if ret < 0 {
+ return 0, errors.New("fail to seek")
+ }
+ n, err := f.Read(p)
+ if err != nil {
+ return 0, err
+ }
+ return n, nil
+}
+
+// loads the Xcoff file pn from f.
+// Symbols are written into loader, and a slice of the text symbols is returned.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
+ return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...))
+ }
+
+ var ldSections []*ldSection
+
+ f, err := xcoff.NewFile((*xcoffBiobuf)(input))
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ for _, sect := range f.Sections {
+ //only text, data and bss section
+ if sect.Type < xcoff.STYP_TEXT || sect.Type > xcoff.STYP_BSS {
+ continue
+ }
+ lds := new(ldSection)
+ lds.Section = *sect
+ name := fmt.Sprintf("%s(%s)", pkg, lds.Name)
+ symbol := l.LookupOrCreateSym(name, localSymVersion)
+ s := l.MakeSymbolUpdater(symbol)
+
+ switch lds.Type {
+ default:
+ return errorf("unrecognized section type 0x%x", lds.Type)
+ case xcoff.STYP_TEXT:
+ s.SetType(sym.STEXT)
+ case xcoff.STYP_DATA:
+ s.SetType(sym.SNOPTRDATA)
+ case xcoff.STYP_BSS:
+ s.SetType(sym.SNOPTRBSS)
+ }
+
+ s.SetSize(int64(lds.Size))
+ if s.Type() != sym.SNOPTRBSS {
+ data, err := lds.Section.Data()
+ if err != nil {
+ return nil, err
+ }
+ s.SetData(data)
+ }
+
+ lds.sym = symbol
+ ldSections = append(ldSections, lds)
+ }
+
+ // sx = symbol from file
+ // s = symbol for loader
+ for _, sx := range f.Symbols {
+ // get symbol type
+ stype, errmsg := getSymbolType(f, sx)
+ if errmsg != "" {
+ return errorf("error reading symbol %s: %s", sx.Name, errmsg)
+ }
+ if stype == sym.Sxxx {
+ continue
+ }
+
+ s := l.LookupOrCreateSym(sx.Name, 0)
+
+ // Text symbol
+ if l.SymType(s) == sym.STEXT {
+ if l.AttrOnList(s) {
+ return errorf("symbol %s listed multiple times", l.SymName(s))
+ }
+ l.SetAttrOnList(s, true)
+ textp = append(textp, s)
+ }
+ }
+
+ // Read relocations
+ for _, sect := range ldSections {
+ // TODO(aix): Dwarf section relocation if needed
+ if sect.Type != xcoff.STYP_TEXT && sect.Type != xcoff.STYP_DATA {
+ continue
+ }
+ sb := l.MakeSymbolUpdater(sect.sym)
+ for _, rx := range sect.Relocs {
+ rSym := l.LookupOrCreateCgoExport(rx.Symbol.Name, 0)
+ if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress {
+ return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress)
+ }
+ rOff := int32(rx.VirtualAddress)
+ var rSize uint8
+ var rType objabi.RelocType
+ var rAdd int64
+ switch rx.Type {
+ default:
+ return errorf("section %s: unknown relocation of type 0x%x", sect.Name, rx.Type)
+ case xcoff.R_POS:
+ // Reloc the address of r.Sym
+ // Length should be 64
+ if rx.Length != 64 {
+ return errorf("section %s: relocation R_POS has length different from 64: %d", sect.Name, rx.Length)
+ }
+ rSize = 8
+ rType = objabi.R_CONST
+ rAdd = int64(rx.Symbol.Value)
+
+ case xcoff.R_RBR:
+ rSize = 4
+ rType = objabi.R_CALLPOWER
+ rAdd = 0
+ }
+ r, _ := sb.AddRel(rType)
+ r.SetOff(rOff)
+ r.SetSiz(rSize)
+ r.SetSym(rSym)
+ r.SetAdd(rAdd)
+ }
+ }
+ return textp, nil
+
+}
+
+// Convert symbol xcoff type to sym.SymKind
+// Returns nil if this shouldn't be added into loader (like .file or .dw symbols )
+func getSymbolType(f *xcoff.File, s *xcoff.Symbol) (stype sym.SymKind, err string) {
+ // .file symbol
+ if s.SectionNumber == -2 {
+ if s.StorageClass == xcoff.C_FILE {
+ return sym.Sxxx, ""
+ }
+ return sym.Sxxx, "unrecognised StorageClass for sectionNumber = -2"
+ }
+
+ // extern symbols
+ // TODO(aix)
+ if s.SectionNumber == 0 {
+ return sym.Sxxx, ""
+ }
+
+ sectType := f.Sections[s.SectionNumber-1].SectionHeader.Type
+ switch sectType {
+ default:
+ return sym.Sxxx, fmt.Sprintf("getSymbolType for Section type 0x%x not implemented", sectType)
+ case xcoff.STYP_DWARF, xcoff.STYP_DEBUG:
+ return sym.Sxxx, ""
+ case xcoff.STYP_DATA, xcoff.STYP_BSS, xcoff.STYP_TEXT:
+ }
+
+ switch s.StorageClass {
+ default:
+ return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x not implemented", s.StorageClass)
+ case xcoff.C_HIDEXT, xcoff.C_EXT, xcoff.C_WEAKEXT:
+ switch s.AuxCSect.StorageMappingClass {
+ default:
+ return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x and Storage Map 0x%x not implemented", s.StorageClass, s.AuxCSect.StorageMappingClass)
+
+ // Program Code
+ case xcoff.XMC_PR:
+ if sectType == xcoff.STYP_TEXT {
+ return sym.STEXT, ""
+ }
+ return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_PR", sectType, s.StorageClass)
+
+ // Read/Write Data
+ case xcoff.XMC_RW:
+ if sectType == xcoff.STYP_DATA {
+ return sym.SDATA, ""
+ }
+ if sectType == xcoff.STYP_BSS {
+ return sym.SBSS, ""
+ }
+ return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_RW", sectType, s.StorageClass)
+
+ // Function descriptor
+ case xcoff.XMC_DS:
+ if sectType == xcoff.STYP_DATA {
+ return sym.SDATA, ""
+ }
+ return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass)
+
+ // TOC anchor and TOC entry
+ case xcoff.XMC_TC0, xcoff.XMC_TE:
+ if sectType == xcoff.STYP_DATA {
+ return sym.SXCOFFTOC, ""
+ }
+ return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass)
+
+ }
+ }
+}
diff --git a/src/cmd/link/internal/loong64/asm.go b/src/cmd/link/internal/loong64/asm.go
new file mode 100644
index 0000000..0eb3a81
--- /dev/null
+++ b/src/cmd/link/internal/loong64/asm.go
@@ -0,0 +1,240 @@
+// Copyright 2022 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.
+
+package loong64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "log"
+)
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ log.Fatalf("adddynrel not implemented")
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ // loong64 ELF relocation (endian neutral)
+ // offset uint64
+ // symreloc uint64 // The high 32-bit is the symbol, the low 32-bit is the relocation type.
+ // addend int64
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ switch r.Size {
+ case 4:
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_32) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ case 8:
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_64) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ default:
+ return false
+ }
+ case objabi.R_ADDRLOONG64TLS:
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_TLS_TPREL) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE))
+ out.Write64(uint64(0xfff))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_AND))
+ out.Write64(uint64(0x0))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_POP_32_U_10_12))
+ out.Write64(uint64(0x0))
+
+ case objabi.R_ADDRLOONG64TLSU:
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_TLS_TPREL) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE))
+ out.Write64(uint64(0xc))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_SR))
+ out.Write64(uint64(0x0))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_POP_32_S_5_20) | uint64(0)<<32)
+ out.Write64(uint64(0x0))
+
+ case objabi.R_CALLLOONG64:
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_PLT_PCREL) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_POP_32_S_0_10_10_16_S2))
+ out.Write64(uint64(0x0))
+ // The pcaddu12i + addi.d instructions is used to obtain address of a symbol on Loong64.
+ // The low 12-bit of the symbol address need to be added. The addi.d instruction have
+ // signed 12-bit immediate operand. The 0x800 (addr+U12 <=> addr+0x800+S12) is introduced
+ // to do sign extending from 12 bits. The 0x804 is 0x800 + 4, 4 is instruction bit
+ // width on Loong64 and is used to correct the PC of the addi.d instruction.
+ case objabi.R_ADDRLOONG64:
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_PCREL) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd + 0x4))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_PCREL) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd + 0x804))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE))
+ out.Write64(uint64(0xc))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_SR))
+ out.Write64(uint64(0x0))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE))
+ out.Write64(uint64(0xc))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_SL))
+ out.Write64(uint64(0x0))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_SUB))
+ out.Write64(uint64(0x0))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_POP_32_S_10_12))
+ out.Write64(uint64(0x0))
+
+ case objabi.R_ADDRLOONG64U:
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_PCREL) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd + 0x800))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_PUSH_ABSOLUTE))
+ out.Write64(uint64(0xc))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_SR))
+ out.Write64(uint64(0x0))
+
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_LARCH_SOP_POP_32_S_5_20) | uint64(0)<<32)
+ out.Write64(uint64(0x0))
+ }
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ return
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ rs := r.Sym()
+ if target.IsExternal() {
+ nExtReloc := 0
+ switch r.Type() {
+ default:
+ return val, 0, false
+ case objabi.R_ADDRLOONG64,
+ objabi.R_ADDRLOONG64U:
+ // set up addend for eventual relocation via outer symbol.
+ rs, _ := ld.FoldSubSymbolOffset(ldr, rs)
+ rst := ldr.SymType(rs)
+ if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil {
+ ldr.Errorf(s, "missing section for %s", ldr.SymName(rs))
+ }
+ nExtReloc = 8 // need 8 ELF relocations. see elfreloc1
+ if r.Type() == objabi.R_ADDRLOONG64U {
+ nExtReloc = 4
+ }
+ return val, nExtReloc, true
+ case objabi.R_ADDRLOONG64TLS,
+ objabi.R_ADDRLOONG64TLSU,
+ objabi.R_CALLLOONG64,
+ objabi.R_JMPLOONG64:
+ nExtReloc = 4
+ if r.Type() == objabi.R_CALLLOONG64 || r.Type() == objabi.R_JMPLOONG64 {
+ nExtReloc = 2
+ }
+ return val, nExtReloc, true
+ }
+ }
+
+ const isOk = true
+ const noExtReloc = 0
+
+ switch r.Type() {
+ case objabi.R_CONST:
+ return r.Add(), noExtReloc, isOk
+ case objabi.R_GOTOFF:
+ return ldr.SymValue(r.Sym()) + r.Add() - ldr.SymValue(syms.GOT), noExtReloc, isOk
+ case objabi.R_ADDRLOONG64,
+ objabi.R_ADDRLOONG64U:
+ pc := ldr.SymValue(s) + int64(r.Off())
+ t := ldr.SymAddr(rs) + r.Add() - pc
+ if r.Type() == objabi.R_ADDRLOONG64 {
+ return int64(val&0xffc003ff | (((t + 4 - ((t + 4 + 1<<11) >> 12 << 12)) << 10) & 0x3ffc00)), noExtReloc, isOk
+ }
+ return int64(val&0xfe00001f | (((t + 1<<11) >> 12 << 5) & 0x1ffffe0)), noExtReloc, isOk
+ case objabi.R_ADDRLOONG64TLS,
+ objabi.R_ADDRLOONG64TLSU:
+ t := ldr.SymAddr(rs) + r.Add()
+ if r.Type() == objabi.R_ADDRLOONG64TLS {
+ return int64(val&0xffc003ff | ((t & 0xfff) << 10)), noExtReloc, isOk
+ }
+ return int64(val&0xfe00001f | (((t) >> 12 << 5) & 0x1ffffe0)), noExtReloc, isOk
+ case objabi.R_CALLLOONG64,
+ objabi.R_JMPLOONG64:
+ pc := ldr.SymValue(s) + int64(r.Off())
+ t := ldr.SymAddr(rs) + r.Add() - pc
+ return int64(val&0xfc000000 | (((t >> 2) & 0xffff) << 10) | (((t >> 2) & 0x3ff0000) >> 16)), noExtReloc, isOk
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch r.Type() {
+ case objabi.R_ADDRLOONG64,
+ objabi.R_ADDRLOONG64U:
+ return ld.ExtrelocViaOuterSym(ldr, r, s), true
+
+ case objabi.R_ADDRLOONG64TLS,
+ objabi.R_ADDRLOONG64TLSU,
+ objabi.R_CONST,
+ objabi.R_GOTOFF,
+ objabi.R_CALLLOONG64,
+ objabi.R_JMPLOONG64:
+ return ld.ExtrelocSimple(ldr, r), true
+ }
+ return loader.ExtReloc{}, false
+}
diff --git a/src/cmd/link/internal/loong64/l.go b/src/cmd/link/internal/loong64/l.go
new file mode 100644
index 0000000..e97a868
--- /dev/null
+++ b/src/cmd/link/internal/loong64/l.go
@@ -0,0 +1,17 @@
+// Copyright 2022 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.
+
+package loong64
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 8
+)
+
+/* Used by ../../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 3
+ dwarfRegLR = 1
+)
diff --git a/src/cmd/link/internal/loong64/obj.go b/src/cmd/link/internal/loong64/obj.go
new file mode 100644
index 0000000..0a5bb0a
--- /dev/null
+++ b/src/cmd/link/internal/loong64/obj.go
@@ -0,0 +1,59 @@
+// Copyright 2022 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.
+
+package loong64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchLoong64
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Machoreloc1: machoreloc1,
+ Gentext: gentext,
+
+ Linuxdynld: "/lib64/ld.so.1",
+ LinuxdynldMusl: "/lib64/ld-musl-loongarch.so.1",
+ Freebsddynld: "XXX",
+ Openbsddynld: "XXX",
+ Netbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+ case objabi.Hlinux: /* loong64 elf */
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+ }
+}
diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go
new file mode 100644
index 0000000..5891d35
--- /dev/null
+++ b/src/cmd/link/internal/mips/asm.go
@@ -0,0 +1,155 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2016 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package mips
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+)
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ return
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write32(uint32(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ if r.Size != 4 {
+ return false
+ }
+ out.Write32(uint32(elf.R_MIPS_32) | uint32(elfsym)<<8)
+ case objabi.R_ADDRMIPS:
+ out.Write32(uint32(elf.R_MIPS_LO16) | uint32(elfsym)<<8)
+ case objabi.R_ADDRMIPSU:
+ out.Write32(uint32(elf.R_MIPS_HI16) | uint32(elfsym)<<8)
+ case objabi.R_ADDRMIPSTLS:
+ out.Write32(uint32(elf.R_MIPS_TLS_TPREL_LO16) | uint32(elfsym)<<8)
+ case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ out.Write32(uint32(elf.R_MIPS_26) | uint32(elfsym)<<8)
+ }
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ return
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func applyrel(arch *sys.Arch, ldr *loader.Loader, rt objabi.RelocType, off int32, s loader.Sym, val int64, t int64) int64 {
+ o := uint32(val)
+ switch rt {
+ case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSTLS:
+ return int64(o&0xffff0000 | uint32(t)&0xffff)
+ case objabi.R_ADDRMIPSU:
+ return int64(o&0xffff0000 | uint32((t+(1<<15))>>16)&0xffff)
+ case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ return int64(o&0xfc000000 | uint32(t>>2)&^0xfc000000)
+ default:
+ return val
+ }
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ rs := r.Sym()
+ if target.IsExternal() {
+ switch r.Type() {
+ default:
+ return val, 0, false
+
+ case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
+ // set up addend for eventual relocation via outer symbol.
+ _, off := ld.FoldSubSymbolOffset(ldr, rs)
+ xadd := r.Add() + off
+ return applyrel(target.Arch, ldr, r.Type(), r.Off(), s, val, xadd), 1, true
+
+ case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ return applyrel(target.Arch, ldr, r.Type(), r.Off(), s, val, r.Add()), 1, true
+ }
+ }
+
+ const isOk = true
+ const noExtReloc = 0
+ switch rt := r.Type(); rt {
+ case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
+ t := ldr.SymValue(rs) + r.Add()
+ return applyrel(target.Arch, ldr, rt, r.Off(), s, val, t), noExtReloc, isOk
+ case objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ t := ldr.SymValue(rs) + r.Add()
+
+ if t&3 != 0 {
+ ldr.Errorf(s, "direct call is not aligned: %s %x", ldr.SymName(rs), t)
+ }
+
+ // check if target address is in the same 256 MB region as the next instruction
+ if (ldr.SymValue(s)+int64(r.Off())+4)&0xf0000000 != (t & 0xf0000000) {
+ ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t)
+ }
+
+ return applyrel(target.Arch, ldr, rt, r.Off(), s, val, t), noExtReloc, isOk
+ case objabi.R_ADDRMIPSTLS:
+ // thread pointer is at 0x7000 offset from the start of TLS data area
+ t := ldr.SymValue(rs) + r.Add() - 0x7000
+ if t < -32768 || t >= 32678 {
+ ldr.Errorf(s, "TLS offset out of range %d", t)
+ }
+ return applyrel(target.Arch, ldr, rt, r.Off(), s, val, t), noExtReloc, isOk
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch r.Type() {
+ case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU:
+ return ld.ExtrelocViaOuterSym(ldr, r, s), true
+
+ case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS:
+ return ld.ExtrelocSimple(ldr, r), true
+ }
+ return loader.ExtReloc{}, false
+}
diff --git a/src/cmd/link/internal/mips/l.go b/src/cmd/link/internal/mips/l.go
new file mode 100644
index 0000000..affc48c
--- /dev/null
+++ b/src/cmd/link/internal/mips/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2016 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package mips
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
+// Portions Copyright © 2016 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+const (
+ MaxAlign = 32 // max data alignment
+ MinAlign = 1 // min data alignment
+ FuncAlign = 4
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ DWARFREGSP = 29
+ DWARFREGLR = 31
+)
diff --git a/src/cmd/link/internal/mips/obj.go b/src/cmd/link/internal/mips/obj.go
new file mode 100644
index 0000000..f03c9ab
--- /dev/null
+++ b/src/cmd/link/internal/mips/obj.go
@@ -0,0 +1,92 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2016 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package mips
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "internal/buildcfg"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchMIPS
+ musl := "/lib/ld-musl-mips.so.1"
+ if buildcfg.GOARCH == "mipsle" {
+ arch = sys.ArchMIPSLE
+ musl = "/lib/ld-musl-mipsel.so.1"
+ }
+
+ theArch := ld.Arch{
+ Funcalign: FuncAlign,
+ Maxalign: MaxAlign,
+ Minalign: MinAlign,
+ Dwarfregsp: DWARFREGSP,
+ Dwarfreglr: DWARFREGLR,
+
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 8,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+
+ Linuxdynld: "/lib/ld.so.1",
+ LinuxdynldMusl: musl,
+
+ Freebsddynld: "XXX",
+ Openbsddynld: "XXX",
+ Netbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+ case objabi.Hlinux: /* mips elf */
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+ }
+}
diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go
new file mode 100644
index 0000000..0e64af3
--- /dev/null
+++ b/src/cmd/link/internal/mips64/asm.go
@@ -0,0 +1,169 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package mips64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+)
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+
+ // mips64 ELF relocation (endian neutral)
+ // offset uint64
+ // sym uint32
+ // ssym uint8
+ // type3 uint8
+ // type2 uint8
+ // type uint8
+ // addend int64
+
+ addend := r.Xadd
+
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ out.Write32(uint32(elfsym))
+ out.Write8(0)
+ out.Write8(0)
+ out.Write8(0)
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ switch r.Size {
+ case 4:
+ out.Write8(uint8(elf.R_MIPS_32))
+ case 8:
+ out.Write8(uint8(elf.R_MIPS_64))
+ default:
+ return false
+ }
+ case objabi.R_ADDRMIPS:
+ out.Write8(uint8(elf.R_MIPS_LO16))
+ case objabi.R_ADDRMIPSU:
+ out.Write8(uint8(elf.R_MIPS_HI16))
+ case objabi.R_ADDRMIPSTLS:
+ out.Write8(uint8(elf.R_MIPS_TLS_TPREL_LO16))
+ if ctxt.Target.IsOpenbsd() {
+ // OpenBSD mips64 does not currently offset TLS by 0x7000,
+ // as such we need to add this back to get the correct offset
+ // via the external linker.
+ addend += 0x7000
+ }
+ case objabi.R_CALLMIPS,
+ objabi.R_JMPMIPS:
+ out.Write8(uint8(elf.R_MIPS_26))
+ }
+ out.Write64(uint64(addend))
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ return
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ if target.IsExternal() {
+ switch r.Type() {
+ default:
+ return val, 0, false
+
+ case objabi.R_ADDRMIPS,
+ objabi.R_ADDRMIPSU,
+ objabi.R_ADDRMIPSTLS,
+ objabi.R_CALLMIPS,
+ objabi.R_JMPMIPS:
+ return val, 1, true
+ }
+ }
+
+ const isOk = true
+ const noExtReloc = 0
+ rs := r.Sym()
+ switch r.Type() {
+ case objabi.R_ADDRMIPS,
+ objabi.R_ADDRMIPSU:
+ t := ldr.SymValue(rs) + r.Add()
+ if r.Type() == objabi.R_ADDRMIPS {
+ return int64(val&0xffff0000 | t&0xffff), noExtReloc, isOk
+ }
+ return int64(val&0xffff0000 | ((t+1<<15)>>16)&0xffff), noExtReloc, isOk
+ case objabi.R_ADDRMIPSTLS:
+ // thread pointer is at 0x7000 offset from the start of TLS data area
+ t := ldr.SymValue(rs) + r.Add() - 0x7000
+ if target.IsOpenbsd() {
+ // OpenBSD mips64 does not currently offset TLS by 0x7000,
+ // as such we need to add this back to get the correct offset.
+ t += 0x7000
+ }
+ if t < -32768 || t >= 32678 {
+ ldr.Errorf(s, "TLS offset out of range %d", t)
+ }
+ return int64(val&0xffff0000 | t&0xffff), noExtReloc, isOk
+ case objabi.R_CALLMIPS,
+ objabi.R_JMPMIPS:
+ // Low 26 bits = (S + A) >> 2
+ t := ldr.SymValue(rs) + r.Add()
+ return int64(val&0xfc000000 | (t>>2)&^0xfc000000), noExtReloc, isOk
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch r.Type() {
+ case objabi.R_ADDRMIPS,
+ objabi.R_ADDRMIPSU:
+ return ld.ExtrelocViaOuterSym(ldr, r, s), true
+
+ case objabi.R_ADDRMIPSTLS,
+ objabi.R_CALLMIPS,
+ objabi.R_JMPMIPS:
+ return ld.ExtrelocSimple(ldr, r), true
+ }
+ return loader.ExtReloc{}, false
+}
diff --git a/src/cmd/link/internal/mips64/l.go b/src/cmd/link/internal/mips64/l.go
new file mode 100644
index 0000000..837af0e
--- /dev/null
+++ b/src/cmd/link/internal/mips64/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package mips64
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 8
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 29
+ dwarfRegLR = 31
+)
diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go
new file mode 100644
index 0000000..557d799
--- /dev/null
+++ b/src/cmd/link/internal/mips64/obj.go
@@ -0,0 +1,102 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package mips64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "internal/buildcfg"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchMIPS64
+ musl := "/lib/ld-musl-mips64.so.1"
+ if buildcfg.GOARCH == "mips64le" {
+ arch = sys.ArchMIPS64LE
+ musl = "/lib/ld-musl-mips64el.so.1"
+ }
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+
+ Linuxdynld: "/lib64/ld64.so.1",
+ LinuxdynldMusl: musl,
+ Freebsddynld: "XXX",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 16*1024 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 16 * 1024
+ }
+
+ case objabi.Hlinux, /* mips64 elf */
+ objabi.Hopenbsd:
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
new file mode 100644
index 0000000..7413599
--- /dev/null
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -0,0 +1,1529 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ppc64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/binary"
+ "fmt"
+ "log"
+ "strconv"
+ "strings"
+)
+
+func genpltstub(ctxt *ld.Link, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (sym loader.Sym, firstUse bool) {
+ // The ppc64 ABI PLT has similar concepts to other
+ // architectures, but is laid out quite differently. When we
+ // see an R_PPC64_REL24 relocation to a dynamic symbol
+ // (indicating that the call needs to go through the PLT), we
+ // generate up to three stubs and reserve a PLT slot.
+ //
+ // 1) The call site will be bl x; nop (where the relocation
+ // applies to the bl). We rewrite this to bl x_stub; ld
+ // r2,24(r1). The ld is necessary because x_stub will save
+ // r2 (the TOC pointer) at 24(r1) (the "TOC save slot").
+ //
+ // 2) We reserve space for a pointer in the .plt section (once
+ // per referenced dynamic function). .plt is a data
+ // section filled solely by the dynamic linker (more like
+ // .plt.got on other architectures). Initially, the
+ // dynamic linker will fill each slot with a pointer to the
+ // corresponding x@plt entry point.
+ //
+ // 3) We generate the "call stub" x_stub (once per dynamic
+ // function/object file pair). This saves the TOC in the
+ // TOC save slot, reads the function pointer from x's .plt
+ // slot and calls it like any other global entry point
+ // (including setting r12 to the function address).
+ //
+ // 4) We generate the "symbol resolver stub" x@plt (once per
+ // dynamic function). This is solely a branch to the glink
+ // resolver stub.
+ //
+ // 5) We generate the glink resolver stub (only once). This
+ // computes which symbol resolver stub we came through and
+ // invokes the dynamic resolver via a pointer provided by
+ // the dynamic linker. This will patch up the .plt slot to
+ // point directly at the function so future calls go
+ // straight from the call stub to the real function, and
+ // then call the function.
+
+ // NOTE: It's possible we could make ppc64 closer to other
+ // architectures: ppc64's .plt is like .plt.got on other
+ // platforms and ppc64's .glink is like .plt on other
+ // platforms.
+
+ // Find all R_PPC64_REL24 relocations that reference dynamic
+ // imports. Reserve PLT entries for these symbols and
+ // generate call stubs. The call stubs need to live in .text,
+ // which is why we need to do this pass this early.
+ //
+ // This assumes "case 1" from the ABI, where the caller needs
+ // us to save and restore the TOC pointer.
+
+ // Reserve PLT entry and generate symbol
+ // resolver
+ addpltsym(ctxt, ldr, r.Sym())
+
+ // Generate call stub. Important to note that we're looking
+ // up the stub using the same version as the parent symbol (s),
+ // needed so that symtoc() will select the right .TOC. symbol
+ // when processing the stub. In older versions of the linker
+ // this was done by setting stub.Outer to the parent, but
+ // if the stub has the right version initially this is not needed.
+ n := fmt.Sprintf("%s.%s", ldr.SymName(s), ldr.SymName(r.Sym()))
+ stub := ldr.CreateSymForUpdate(n, ldr.SymVersion(s))
+ firstUse = stub.Size() == 0
+ if firstUse {
+ gencallstub(ctxt, ldr, 1, stub, r.Sym())
+ }
+
+ // Update the relocation to use the call stub
+ r.SetSym(stub.Sym())
+
+ // Make the symbol writeable so we can fixup toc.
+ su := ldr.MakeSymbolUpdater(s)
+ su.MakeWritable()
+ p := su.Data()
+
+ // Check for toc restore slot (a nop), and replace with toc restore.
+ var nop uint32
+ if len(p) >= int(r.Off()+8) {
+ nop = ctxt.Arch.ByteOrder.Uint32(p[r.Off()+4:])
+ }
+ if nop != 0x60000000 {
+ ldr.Errorf(s, "Symbol %s is missing toc restoration slot at offset %d", ldr.SymName(s), r.Off()+4)
+ }
+ const o1 = 0xe8410018 // ld r2,24(r1)
+ ctxt.Arch.ByteOrder.PutUint32(p[r.Off()+4:], o1)
+
+ return stub.Sym(), firstUse
+}
+
+// Scan relocs and generate PLT stubs and generate/fixup ABI defined functions created by the linker.
+func genstubs(ctxt *ld.Link, ldr *loader.Loader) {
+ var stubs []loader.Sym
+ var abifuncs []loader.Sym
+ for _, s := range ctxt.Textp {
+ relocs := ldr.Relocs(s)
+ for i := 0; i < relocs.Count(); i++ {
+ r := relocs.At(i)
+ switch r.Type() {
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24):
+ switch ldr.SymType(r.Sym()) {
+ case sym.SDYNIMPORT:
+ // This call goes through the PLT, generate and call through a PLT stub.
+ if sym, firstUse := genpltstub(ctxt, ldr, r, s); firstUse {
+ stubs = append(stubs, sym)
+ }
+
+ case sym.SXREF:
+ // Is this an ELF ABI defined function which is (in practice)
+ // generated by the linker to save/restore callee save registers?
+ // These are defined similarly for both PPC64 ELF and ELFv2.
+ targName := ldr.SymName(r.Sym())
+ if strings.HasPrefix(targName, "_save") || strings.HasPrefix(targName, "_rest") {
+ if sym, firstUse := rewriteABIFuncReloc(ctxt, ldr, targName, r); firstUse {
+ abifuncs = append(abifuncs, sym)
+ }
+ }
+ }
+
+ // Handle objects compiled with -fno-plt. Rewrite local calls to avoid indirect calling.
+ // These are 0 sized relocs. They mark the mtctr r12, or bctrl + ld r2,24(r1).
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLTSEQ):
+ if ldr.SymType(r.Sym()) == sym.STEXT {
+ // This should be an mtctr instruction. Turn it into a nop.
+ su := ldr.MakeSymbolUpdater(s)
+ const OP_MTCTR = 31<<26 | 0x9<<16 | 467<<1
+ const MASK_OP_MTCTR = 63<<26 | 0x3FF<<11 | 0x1FF<<1
+ rewritetonop(&ctxt.Target, ldr, su, int64(r.Off()), MASK_OP_MTCTR, OP_MTCTR)
+ }
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLTCALL):
+ if ldr.SymType(r.Sym()) == sym.STEXT {
+ // This relocation should point to a bctrl followed by a ld r2, 24(41)
+ const OP_BL = 0x48000001 // bl 0
+ const OP_TOCRESTORE = 0xe8410018 // ld r2,24(r1)
+ const OP_BCTRL = 0x4e800421 // bctrl
+
+ // Convert the bctrl into a bl.
+ su := ldr.MakeSymbolUpdater(s)
+ rewritetoinsn(&ctxt.Target, ldr, su, int64(r.Off()), 0xFFFFFFFF, OP_BCTRL, OP_BL)
+
+ // Turn this reloc into an R_CALLPOWER, and convert the TOC restore into a nop.
+ su.SetRelocType(i, objabi.R_CALLPOWER)
+ su.SetRelocAdd(i, r.Add()+int64(ldr.SymLocalentry(r.Sym())))
+ r.SetSiz(4)
+ rewritetonop(&ctxt.Target, ldr, su, int64(r.Off()+4), 0xFFFFFFFF, OP_TOCRESTORE)
+ }
+ }
+ }
+ }
+
+ // Append any usage of the go versions of ELF save/restore
+ // functions to the end of the callstub list to minimize
+ // chances a trampoline might be needed.
+ stubs = append(stubs, abifuncs...)
+
+ // Put stubs at the beginning (instead of the end).
+ // So when resolving the relocations to calls to the stubs,
+ // the addresses are known and trampolines can be inserted
+ // when necessary.
+ ctxt.Textp = append(stubs, ctxt.Textp...)
+}
+
+func genaddmoduledata(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op uint32) {
+ initfunc.AddUint32(ctxt.Arch, op)
+ }
+
+ // addis r2, r12, .TOC.-func@ha
+ toc := ctxt.DotTOC[0]
+ rel1, _ := initfunc.AddRel(objabi.R_ADDRPOWER_PCREL)
+ rel1.SetOff(0)
+ rel1.SetSiz(8)
+ rel1.SetSym(toc)
+ o(0x3c4c0000)
+ // addi r2, r2, .TOC.-func@l
+ o(0x38420000)
+ // mflr r31
+ o(0x7c0802a6)
+ // stdu r31, -32(r1)
+ o(0xf801ffe1)
+ // addis r3, r2, local.moduledata@got@ha
+ var tgt loader.Sym
+ if s := ldr.Lookup("local.moduledata", 0); s != 0 {
+ tgt = s
+ } else if s := ldr.Lookup("local.pluginmoduledata", 0); s != 0 {
+ tgt = s
+ } else {
+ tgt = ldr.LookupOrCreateSym("runtime.firstmoduledata", 0)
+ }
+ rel2, _ := initfunc.AddRel(objabi.R_ADDRPOWER_GOT)
+ rel2.SetOff(int32(initfunc.Size()))
+ rel2.SetSiz(8)
+ rel2.SetSym(tgt)
+ o(0x3c620000)
+ // ld r3, local.moduledata@got@l(r3)
+ o(0xe8630000)
+ // bl runtime.addmoduledata
+ rel3, _ := initfunc.AddRel(objabi.R_CALLPOWER)
+ rel3.SetOff(int32(initfunc.Size()))
+ rel3.SetSiz(4)
+ rel3.SetSym(addmoduledata)
+ o(0x48000001)
+ // nop
+ o(0x60000000)
+ // ld r31, 0(r1)
+ o(0xe8010000)
+ // mtlr r31
+ o(0x7c0803a6)
+ // addi r1,r1,32
+ o(0x38210020)
+ // blr
+ o(0x4e800020)
+}
+
+// Rewrite ELF (v1 or v2) calls to _savegpr0_n, _savegpr1_n, _savefpr_n, _restfpr_n, _savevr_m, or
+// _restvr_m (14<=n<=31, 20<=m<=31). Redirect them to runtime.elf_restgpr0+(n-14)*4,
+// runtime.elf_restvr+(m-20)*8, and similar.
+//
+// These functions are defined in the ELFv2 ABI (generated when using gcc -Os option) to save and
+// restore callee-saved registers (as defined in the PPC64 ELF ABIs) from registers n or m to 31 of
+// the named type. R12 and R0 are sometimes used in exceptional ways described in the ABI.
+//
+// Final note, this is only needed when linking internally. The external linker will generate these
+// functions if they are used.
+func rewriteABIFuncReloc(ctxt *ld.Link, ldr *loader.Loader, tname string, r loader.Reloc) (sym loader.Sym, firstUse bool) {
+ s := strings.Split(tname, "_")
+ // A valid call will split like {"", "savegpr0", "20"}
+ if len(s) != 3 {
+ return 0, false // Not an abi func.
+ }
+ minReg := 14 // _savegpr0_{n}, _savegpr1_{n}, _savefpr_{n}, 14 <= n <= 31
+ offMul := 4 // 1 instruction per register op.
+ switch s[1] {
+ case "savegpr0", "savegpr1", "savefpr":
+ case "restgpr0", "restgpr1", "restfpr":
+ case "savevr", "restvr":
+ minReg = 20 // _savevr_{n} or _restvr_{n}, 20 <= n <= 31
+ offMul = 8 // 2 instructions per register op.
+ default:
+ return 0, false // Not an abi func
+ }
+ n, e := strconv.Atoi(s[2])
+ if e != nil || n < minReg || n > 31 || r.Add() != 0 {
+ return 0, false // Invalid register number, or non-zero addend. Not an abi func.
+ }
+
+ // tname is a valid relocation to an ABI defined register save/restore function. Re-relocate
+ // them to a go version of these functions in runtime/asm_ppc64x.s
+ ts := ldr.LookupOrCreateSym("runtime.elf_"+s[1], 0)
+ r.SetSym(ts)
+ r.SetAdd(int64((n - minReg) * offMul))
+ firstUse = !ldr.AttrReachable(ts)
+ if firstUse {
+ ldr.SetAttrReachable(ts, true)
+ // This function only becomes reachable now. It has been dropped from
+ // the text section (it was unreachable until now), it needs included.
+ //
+ // Similarly, TOC regeneration should not happen for these functions,
+ // remove it from this save/restore function.
+ if ldr.AttrShared(ts) {
+ sb := ldr.MakeSymbolUpdater(ts)
+ sb.SetData(sb.Data()[8:])
+ sb.SetSize(sb.Size() - 8)
+ relocs := sb.Relocs()
+ // Only one PCREL reloc to .TOC. should be present.
+ if relocs.Count() != 1 {
+ log.Fatalf("Unexpected number of relocs in %s\n", ldr.SymName(ts))
+ }
+ sb.ResetRelocs()
+
+ }
+ }
+ return ts, firstUse
+}
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ if ctxt.DynlinkingGo() {
+ genaddmoduledata(ctxt, ldr)
+ }
+
+ if ctxt.LinkMode == ld.LinkInternal {
+ genstubs(ctxt, ldr)
+ }
+}
+
+// Construct a call stub in stub that calls symbol targ via its PLT
+// entry.
+func gencallstub(ctxt *ld.Link, ldr *loader.Loader, abicase int, stub *loader.SymbolBuilder, targ loader.Sym) {
+ if abicase != 1 {
+ // If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC
+ // relocations, we'll need to implement cases 2 and 3.
+ log.Fatalf("gencallstub only implements case 1 calls")
+ }
+
+ plt := ctxt.PLT
+
+ stub.SetType(sym.STEXT)
+
+ // Save TOC pointer in TOC save slot
+ stub.AddUint32(ctxt.Arch, 0xf8410018) // std r2,24(r1)
+
+ // Load the function pointer from the PLT.
+ rel, ri1 := stub.AddRel(objabi.R_POWER_TOC)
+ rel.SetOff(int32(stub.Size()))
+ rel.SetSiz(2)
+ rel.SetAdd(int64(ldr.SymPlt(targ)))
+ rel.SetSym(plt)
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ rel.SetOff(rel.Off() + int32(rel.Siz()))
+ }
+ ldr.SetRelocVariant(stub.Sym(), int(ri1), sym.RV_POWER_HA)
+ stub.AddUint32(ctxt.Arch, 0x3d820000) // addis r12,r2,targ@plt@toc@ha
+
+ rel2, ri2 := stub.AddRel(objabi.R_POWER_TOC)
+ rel2.SetOff(int32(stub.Size()))
+ rel2.SetSiz(2)
+ rel2.SetAdd(int64(ldr.SymPlt(targ)))
+ rel2.SetSym(plt)
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ rel2.SetOff(rel2.Off() + int32(rel2.Siz()))
+ }
+ ldr.SetRelocVariant(stub.Sym(), int(ri2), sym.RV_POWER_LO)
+ stub.AddUint32(ctxt.Arch, 0xe98c0000) // ld r12,targ@plt@toc@l(r12)
+
+ // Jump to the loaded pointer
+ stub.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
+ stub.AddUint32(ctxt.Arch, 0x4e800420) // bctr
+}
+
+// Rewrite the instruction at offset into newinsn. Also, verify the
+// existing instruction under mask matches the check value.
+func rewritetoinsn(target *ld.Target, ldr *loader.Loader, su *loader.SymbolBuilder, offset int64, mask, check, newinsn uint32) {
+ su.MakeWritable()
+ op := target.Arch.ByteOrder.Uint32(su.Data()[offset:])
+ if op&mask != check {
+ ldr.Errorf(su.Sym(), "Rewrite offset 0x%x to 0x%08X failed check (0x%08X&0x%08X != 0x%08X)", offset, newinsn, op, mask, check)
+ }
+ su.SetUint32(target.Arch, offset, newinsn)
+}
+
+// Rewrite the instruction at offset into a hardware nop instruction. Also, verify the
+// existing instruction under mask matches the check value.
+func rewritetonop(target *ld.Target, ldr *loader.Loader, su *loader.SymbolBuilder, offset int64, mask, check uint32) {
+ const NOP = 0x60000000
+ rewritetoinsn(target, ldr, su, offset, mask, check, NOP)
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ if target.IsElf() {
+ return addelfdynrel(target, ldr, syms, s, r, rIdx)
+ } else if target.IsAIX() {
+ return ld.Xcoffadddynrel(target, ldr, syms, s, r, rIdx)
+ }
+ return false
+}
+
+func addelfdynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CALLPOWER)
+
+ // This is a local call, so the caller isn't setting
+ // up r12 and r2 is the same for the caller and
+ // callee. Hence, we need to go to the local entry
+ // point. (If we don't do this, the callee will try
+ // to use r12 to compute r2.)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymLocalentry(targ)))
+
+ if targType == sym.SDYNIMPORT {
+ // Should have been handled in elfsetupplt
+ ldr.Errorf(s, "unexpected R_PPC64_REL24 for dyn import")
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC_REL32):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_PPC_REL32 for dyn import")
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_ADDR64):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if targType == sym.SDYNIMPORT {
+ // These happen in .toc sections
+ ld.Adddynsym(ldr, target, syms, targ)
+
+ rela := ldr.MakeSymbolUpdater(syms.Rela)
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_PPC64_ADDR64)))
+ rela.AddUint64(target.Arch, uint64(r.Add()))
+ su.SetRelocType(rIdx, objabi.ElfRelocOffset) // ignore during relocsym
+ } else if target.IsPIE() && target.IsInternal() {
+ // For internal linking PIE, this R_ADDR relocation cannot
+ // be resolved statically. We need to generate a dynamic
+ // relocation. Let the code below handle it.
+ break
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO|sym.RV_CHECK_OVERFLOW)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HA):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HI):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HI|sym.RV_CHECK_OVERFLOW)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_DS):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS|sym.RV_CHECK_OVERFLOW)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO_DS):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_LO):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO)
+ su.SetRelocAdd(rIdx, r.Add()+2) // Compensate for relocation size of 2
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HI):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HI|sym.RV_CHECK_OVERFLOW)
+ su.SetRelocAdd(rIdx, r.Add()+2)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HA):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW)
+ su.SetRelocAdd(rIdx, r.Add()+2)
+ return true
+
+ // When compiling with gcc's -fno-plt option (no PLT), the following code and relocation
+ // sequences may be present to call an external function:
+ //
+ // 1. addis Rx,foo@R_PPC64_PLT16_HA
+ // 2. ld 12,foo@R_PPC64_PLT16_LO_DS(Rx)
+ // 3. mtctr 12 ; foo@R_PPC64_PLTSEQ
+ // 4. bctrl ; foo@R_PPC64_PLTCALL
+ // 5. ld r2,24(r1)
+ //
+ // Note, 5 is required to follow the R_PPC64_PLTCALL. Similarly, relocations targeting
+ // instructions 3 and 4 are zero sized informational relocations.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLT16_HA),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_PLT16_LO_DS):
+ su := ldr.MakeSymbolUpdater(s)
+ isPLT16_LO_DS := r.Type() == objabi.ElfRelocOffset+objabi.RelocType(elf.R_PPC64_PLT16_LO_DS)
+ if isPLT16_LO_DS {
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS)
+ } else {
+ ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW)
+ }
+ su.SetRelocType(rIdx, objabi.R_POWER_TOC)
+ if targType == sym.SDYNIMPORT {
+ // This is an external symbol, make space in the GOT and retarget the reloc.
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_PPC64_GLOB_DAT))
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ } else if targType == sym.STEXT {
+ if isPLT16_LO_DS {
+ // Expect an ld opcode to nop
+ const MASK_OP_LD = 63<<26 | 0x3
+ const OP_LD = 58 << 26
+ rewritetonop(target, ldr, su, int64(r.Off()), MASK_OP_LD, OP_LD)
+ } else {
+ // Expect an addis opcode to nop
+ const MASK_OP_ADDIS = 63 << 26
+ const OP_ADDIS = 15 << 26
+ rewritetonop(target, ldr, su, int64(r.Off()), MASK_OP_ADDIS, OP_ADDIS)
+ }
+ // And we can ignore this reloc now.
+ su.SetRelocType(rIdx, objabi.ElfRelocOffset)
+ } else {
+ ldr.Errorf(s, "unexpected PLT relocation target symbol type %s", targType.String())
+ }
+ return true
+ }
+
+ // Handle references to ELF symbols from our own object files.
+ relocs := ldr.Relocs(s)
+ r = relocs.At(rIdx)
+
+ switch r.Type() {
+ case objabi.R_ADDR:
+ if ldr.SymType(s) == sym.STEXT {
+ log.Fatalf("R_ADDR relocation in text symbol %s is unsupported\n", ldr.SymName(s))
+ }
+ if target.IsPIE() && target.IsInternal() {
+ // When internally linking, generate dynamic relocations
+ // for all typical R_ADDR relocations. The exception
+ // are those R_ADDR that are created as part of generating
+ // the dynamic relocations and must be resolved statically.
+ //
+ // There are three phases relevant to understanding this:
+ //
+ // dodata() // we are here
+ // address() // symbol address assignment
+ // reloc() // resolution of static R_ADDR relocs
+ //
+ // At this point symbol addresses have not been
+ // assigned yet (as the final size of the .rela section
+ // will affect the addresses), and so we cannot write
+ // the Elf64_Rela.r_offset now. Instead we delay it
+ // until after the 'address' phase of the linker is
+ // complete. We do this via Addaddrplus, which creates
+ // a new R_ADDR relocation which will be resolved in
+ // the 'reloc' phase.
+ //
+ // These synthetic static R_ADDR relocs must be skipped
+ // now, or else we will be caught in an infinite loop
+ // of generating synthetic relocs for our synthetic
+ // relocs.
+ //
+ // Furthermore, the rela sections contain dynamic
+ // relocations with R_ADDR relocations on
+ // Elf64_Rela.r_offset. This field should contain the
+ // symbol offset as determined by reloc(), not the
+ // final dynamically linked address as a dynamic
+ // relocation would provide.
+ switch ldr.SymName(s) {
+ case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic":
+ return false
+ }
+ } else {
+ // Either internally linking a static executable,
+ // in which case we can resolve these relocations
+ // statically in the 'reloc' phase, or externally
+ // linking, in which case the relocation will be
+ // prepared in the 'reloc' phase and passed to the
+ // external linker in the 'asmb' phase.
+ if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA {
+ break
+ }
+ }
+ // Generate R_PPC64_RELATIVE relocations for best
+ // efficiency in the dynamic linker.
+ //
+ // As noted above, symbol addresses have not been
+ // assigned yet, so we can't generate the final reloc
+ // entry yet. We ultimately want:
+ //
+ // r_offset = s + r.Off
+ // r_info = R_PPC64_RELATIVE
+ // r_addend = targ + r.Add
+ //
+ // The dynamic linker will set *offset = base address +
+ // addend.
+ //
+ // AddAddrPlus is used for r_offset and r_addend to
+ // generate new R_ADDR relocations that will update
+ // these fields in the 'reloc' phase.
+ rela := ldr.MakeSymbolUpdater(syms.Rela)
+ rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ if r.Siz() == 8 {
+ rela.AddUint64(target.Arch, elf.R_INFO(0, uint32(elf.R_PPC64_RELATIVE)))
+ } else {
+ ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ rela.AddAddrPlus(target.Arch, targ, int64(r.Add()))
+
+ // Not mark r done here. So we still apply it statically,
+ // so in the file content we'll also have the right offset
+ // to the relocation target. So it can be examined statically
+ // (e.g. go version).
+ return true
+ }
+
+ return false
+}
+
+func xcoffreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ rs := r.Xsym
+
+ emitReloc := func(v uint16, off uint64) {
+ out.Write64(uint64(sectoff) + off)
+ out.Write32(uint32(ldr.SymDynid(rs)))
+ out.Write16(v)
+ }
+
+ var v uint16
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ v = ld.XCOFF_R_POS
+ if r.Size == 4 {
+ v |= 0x1F << 8
+ } else {
+ v |= 0x3F << 8
+ }
+ emitReloc(v, 0)
+ case objabi.R_ADDRPOWER_TOCREL:
+ case objabi.R_ADDRPOWER_TOCREL_DS:
+ emitReloc(ld.XCOFF_R_TOCU|(0x0F<<8), 2)
+ emitReloc(ld.XCOFF_R_TOCL|(0x0F<<8), 6)
+ case objabi.R_POWER_TLS_LE:
+ // This only supports 16b relocations. It is fixed up in archreloc.
+ emitReloc(ld.XCOFF_R_TLS_LE|0x0F<<8, 2)
+ case objabi.R_CALLPOWER:
+ if r.Size != 4 {
+ return false
+ }
+ emitReloc(ld.XCOFF_R_RBR|0x19<<8, 0)
+ case objabi.R_XCOFFREF:
+ emitReloc(ld.XCOFF_R_REF|0x3F<<8, 0)
+ }
+ return true
+
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ // Beware that bit0~bit15 start from the third byte of a instruction in Big-Endian machines.
+ rt := r.Type
+ if rt == objabi.R_ADDR || rt == objabi.R_POWER_TLS || rt == objabi.R_CALLPOWER {
+ } else {
+ if ctxt.Arch.ByteOrder == binary.BigEndian {
+ sectoff += 2
+ }
+ }
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ switch rt {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ switch r.Size {
+ case 4:
+ out.Write64(uint64(elf.R_PPC64_ADDR32) | uint64(elfsym)<<32)
+ case 8:
+ out.Write64(uint64(elf.R_PPC64_ADDR64) | uint64(elfsym)<<32)
+ default:
+ return false
+ }
+ case objabi.R_ADDRPOWER_D34:
+ out.Write64(uint64(elf.R_PPC64_D34) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER_PCREL34:
+ out.Write64(uint64(elf.R_PPC64_PCREL34) | uint64(elfsym)<<32)
+ case objabi.R_POWER_TLS:
+ out.Write64(uint64(elf.R_PPC64_TLS) | uint64(elfsym)<<32)
+ case objabi.R_POWER_TLS_LE:
+ out.Write64(uint64(elf.R_PPC64_TPREL16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_TPREL16_LO) | uint64(elfsym)<<32)
+ case objabi.R_POWER_TLS_LE_TPREL34:
+ out.Write64(uint64(elf.R_PPC64_TPREL34) | uint64(elfsym)<<32)
+ case objabi.R_POWER_TLS_IE_PCREL34:
+ out.Write64(uint64(elf.R_PPC64_GOT_TPREL_PCREL34) | uint64(elfsym)<<32)
+ case objabi.R_POWER_TLS_IE:
+ out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_LO_DS) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER:
+ out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_ADDR16_LO) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER_DS:
+ out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_ADDR16_LO_DS) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER_GOT:
+ out.Write64(uint64(elf.R_PPC64_GOT16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_GOT16_LO_DS) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER_PCREL:
+ out.Write64(uint64(elf.R_PPC64_REL16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_REL16_LO) | uint64(elfsym)<<32)
+ r.Xadd += 4
+ case objabi.R_ADDRPOWER_TOCREL:
+ out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_TOC16_LO) | uint64(elfsym)<<32)
+ case objabi.R_ADDRPOWER_TOCREL_DS:
+ out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(elf.R_PPC64_TOC16_LO_DS) | uint64(elfsym)<<32)
+ case objabi.R_CALLPOWER:
+ if r.Size != 4 {
+ return false
+ }
+ out.Write64(uint64(elf.R_PPC64_REL24) | uint64(elfsym)<<32)
+
+ }
+ out.Write64(uint64(r.Xadd))
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // The dynamic linker stores the address of the
+ // dynamic resolver and the DSO identifier in the two
+ // doublewords at the beginning of the .plt section
+ // before the PLT array. Reserve space for these.
+ plt.SetSize(16)
+ }
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+// Return the value of .TOC. for symbol s
+func symtoc(ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) int64 {
+ v := ldr.SymVersion(s)
+ if out := ldr.OuterSym(s); out != 0 {
+ v = ldr.SymVersion(out)
+ }
+
+ toc := syms.DotTOC[v]
+ if toc == 0 {
+ ldr.Errorf(s, "TOC-relative relocation in object without .TOC.")
+ return 0
+ }
+
+ return ldr.SymValue(toc)
+}
+
+// archreloctoc relocates a TOC relative symbol.
+func archreloctoc(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) int64 {
+ rs := r.Sym()
+ var o1, o2 uint32
+ var t int64
+ useAddi := false
+
+ if target.IsBigEndian() {
+ o1 = uint32(val >> 32)
+ o2 = uint32(val)
+ } else {
+ o1 = uint32(val)
+ o2 = uint32(val >> 32)
+ }
+
+ // On AIX, TOC data accesses are always made indirectly against R2 (a sequence of addis+ld+load/store). If the
+ // The target of the load is known, the sequence can be written into addis+addi+load/store. On Linux,
+ // TOC data accesses are always made directly against R2 (e.g addis+load/store).
+ if target.IsAIX() {
+ if !strings.HasPrefix(ldr.SymName(rs), "TOC.") {
+ ldr.Errorf(s, "archreloctoc called for a symbol without TOC anchor")
+ }
+ relocs := ldr.Relocs(rs)
+ tarSym := relocs.At(0).Sym()
+
+ if target.IsInternal() && tarSym != 0 && ldr.AttrReachable(tarSym) && ldr.SymSect(tarSym).Seg == &ld.Segdata {
+ t = ldr.SymValue(tarSym) + r.Add() - ldr.SymValue(syms.TOC)
+ // change ld to addi in the second instruction
+ o2 = (o2 & 0x03FF0000) | 0xE<<26
+ useAddi = true
+ } else {
+ t = ldr.SymValue(rs) + r.Add() - ldr.SymValue(syms.TOC)
+ }
+ } else {
+ t = ldr.SymValue(rs) + r.Add() - symtoc(ldr, syms, s)
+ }
+
+ if t != int64(int32(t)) {
+ ldr.Errorf(s, "TOC relocation for %s is too big to relocate %s: 0x%x", ldr.SymName(s), rs, t)
+ }
+
+ if t&0x8000 != 0 {
+ t += 0x10000
+ }
+
+ o1 |= uint32((t >> 16) & 0xFFFF)
+
+ switch r.Type() {
+ case objabi.R_ADDRPOWER_TOCREL_DS:
+ if useAddi {
+ o2 |= uint32(t) & 0xFFFF
+ } else {
+ if t&3 != 0 {
+ ldr.Errorf(s, "bad DS reloc for %s: %d", ldr.SymName(s), ldr.SymValue(rs))
+ }
+ o2 |= uint32(t) & 0xFFFC
+ }
+ case objabi.R_ADDRPOWER_TOCREL:
+ o2 |= uint32(t) & 0xffff
+ default:
+ return -1
+ }
+
+ if target.IsBigEndian() {
+ return int64(o1)<<32 | int64(o2)
+ }
+ return int64(o2)<<32 | int64(o1)
+}
+
+// archrelocaddr relocates a symbol address.
+// This code is for linux only.
+func archrelocaddr(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) int64 {
+ rs := r.Sym()
+ if target.IsAIX() {
+ ldr.Errorf(s, "archrelocaddr called for %s relocation\n", ldr.SymName(rs))
+ }
+ o1, o2 := unpackInstPair(target, val)
+
+ // Verify resulting address fits within a 31 bit (2GB) address space.
+ // This is a restriction arising from the usage of lis (HA) + d-form
+ // (LO) instruction sequences used to implement absolute relocations
+ // on PPC64 prior to ISA 3.1 (P10). For consistency, maintain this
+ // restriction for ISA 3.1 unless it becomes problematic.
+ t := ldr.SymAddr(rs) + r.Add()
+ if t < 0 || t >= 1<<31 {
+ ldr.Errorf(s, "relocation for %s is too big (>=2G): 0x%x", ldr.SymName(s), ldr.SymValue(rs))
+ }
+
+ switch r.Type() {
+ case objabi.R_ADDRPOWER_PCREL34:
+ // S + A - P
+ t -= (ldr.SymValue(s) + int64(r.Off()))
+ o1 |= computePrefix34HI(t)
+ o2 |= computeLO(int32(t))
+ case objabi.R_ADDRPOWER_D34:
+ o1 |= computePrefix34HI(t)
+ o2 |= computeLO(int32(t))
+ case objabi.R_ADDRPOWER:
+ o1 |= computeHA(int32(t))
+ o2 |= computeLO(int32(t))
+ case objabi.R_ADDRPOWER_DS:
+ o1 |= computeHA(int32(t))
+ o2 |= computeLO(int32(t))
+ if t&3 != 0 {
+ ldr.Errorf(s, "bad DS reloc for %s: %d", ldr.SymName(s), ldr.SymValue(rs))
+ }
+ default:
+ return -1
+ }
+
+ return packInstPair(target, o1, o2)
+}
+
+// Determine if the code was compiled so that the TOC register R2 is initialized and maintained.
+func r2Valid(ctxt *ld.Link) bool {
+ switch ctxt.BuildMode {
+ case ld.BuildModeCArchive, ld.BuildModeCShared, ld.BuildModePIE, ld.BuildModeShared, ld.BuildModePlugin:
+ return true
+ }
+ // -linkshared option
+ return ctxt.IsSharedGoLink()
+}
+
+// resolve direct jump relocation r in s, and add trampoline if necessary.
+func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
+
+ // Trampolines are created if the branch offset is too large and the linker cannot insert a call stub to handle it.
+ // For internal linking, trampolines are always created for long calls.
+ // For external linking, the linker can insert a call stub to handle a long call, but depends on having the TOC address in
+ // r2. For those build modes with external linking where the TOC address is not maintained in r2, trampolines must be created.
+ if ctxt.IsExternal() && r2Valid(ctxt) {
+ // The TOC pointer is valid. The external linker will insert trampolines.
+ return
+ }
+
+ relocs := ldr.Relocs(s)
+ r := relocs.At(ri)
+ var t int64
+ // ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet
+ // laid out. Conservatively use a trampoline. This should be rare, as we lay out packages
+ // in dependency order.
+ if ldr.SymValue(rs) != 0 {
+ t = ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
+ }
+ switch r.Type() {
+ case objabi.R_CALLPOWER:
+
+ // If branch offset is too far then create a trampoline.
+
+ if (ctxt.IsExternal() && ldr.SymSect(s) != ldr.SymSect(rs)) || (ctxt.IsInternal() && int64(int32(t<<6)>>6) != t) || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && ldr.SymPkg(s) != ldr.SymPkg(rs)) {
+ var tramp loader.Sym
+ for i := 0; ; i++ {
+
+ // Using r.Add as part of the name is significant in functions like duffzero where the call
+ // target is at some offset within the function. Calls to duff+8 and duff+256 must appear as
+ // distinct trampolines.
+
+ oName := ldr.SymName(rs)
+ name := oName
+ if r.Add() == 0 {
+ name += fmt.Sprintf("-tramp%d", i)
+ } else {
+ name += fmt.Sprintf("%+x-tramp%d", r.Add(), i)
+ }
+
+ // Look up the trampoline in case it already exists
+
+ tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
+ if oName == "runtime.deferreturn" {
+ ldr.SetIsDeferReturnTramp(tramp, true)
+ }
+ if ldr.SymValue(tramp) == 0 {
+ break
+ }
+ // Note, the trampoline is always called directly. The addend of the original relocation is accounted for in the
+ // trampoline itself.
+ t = ldr.SymValue(tramp) - (ldr.SymValue(s) + int64(r.Off()))
+
+ // With internal linking, the trampoline can be used if it is not too far.
+ // With external linking, the trampoline must be in this section for it to be reused.
+ if (ctxt.IsInternal() && int64(int32(t<<6)>>6) == t) || (ctxt.IsExternal() && ldr.SymSect(s) == ldr.SymSect(tramp)) {
+ break
+ }
+ }
+ if ldr.SymType(tramp) == 0 {
+ trampb := ldr.MakeSymbolUpdater(tramp)
+ ctxt.AddTramp(trampb)
+ gentramp(ctxt, ldr, trampb, rs, r.Add())
+ }
+ sb := ldr.MakeSymbolUpdater(s)
+ relocs := sb.Relocs()
+ r := relocs.At(ri)
+ r.SetSym(tramp)
+ r.SetAdd(0) // This was folded into the trampoline target address
+ }
+ default:
+ ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
+ }
+}
+
+func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.SetSize(16) // 4 instructions
+ P := make([]byte, tramp.Size())
+ var o1, o2 uint32
+
+ if ctxt.IsAIX() {
+ // On AIX, the address is retrieved with a TOC symbol.
+ // For internal linking, the "Linux" way might still be used.
+ // However, all text symbols are accessed with a TOC symbol as
+ // text relocations aren't supposed to be possible.
+ // So, keep using the external linking way to be more AIX friendly.
+ o1 = uint32(0x3c000000) | 12<<21 | 2<<16 // addis r12, r2, toctargetaddr hi
+ o2 = uint32(0xe8000000) | 12<<21 | 12<<16 // ld r12, r12, toctargetaddr lo
+
+ toctramp := ldr.CreateSymForUpdate("TOC."+ldr.SymName(tramp.Sym()), 0)
+ toctramp.SetType(sym.SXCOFFTOC)
+ toctramp.AddAddrPlus(ctxt.Arch, target, offset)
+
+ r, _ := tramp.AddRel(objabi.R_ADDRPOWER_TOCREL_DS)
+ r.SetOff(0)
+ r.SetSiz(8) // generates 2 relocations: HA + LO
+ r.SetSym(toctramp.Sym())
+ } else {
+ // Used for default build mode for an executable
+ // Address of the call target is generated using
+ // relocation and doesn't depend on r2 (TOC).
+ o1 = uint32(0x3c000000) | 12<<21 // lis r12,targetaddr hi
+ o2 = uint32(0x38000000) | 12<<21 | 12<<16 // addi r12,r12,targetaddr lo
+
+ // ELFv2 save/restore functions use R0/R12 in special ways, therefore trampolines
+ // as generated here will not always work correctly.
+ if strings.HasPrefix(ldr.SymName(target), "runtime.elf_") {
+ log.Fatalf("Internal linker does not support trampolines to ELFv2 ABI"+
+ " register save/restore function %s", ldr.SymName(target))
+ }
+
+ t := ldr.SymValue(target)
+ if t == 0 || r2Valid(ctxt) || ctxt.IsExternal() {
+ // Target address is unknown, generate relocations
+ r, _ := tramp.AddRel(objabi.R_ADDRPOWER)
+ if r2Valid(ctxt) {
+ // Use a TOC relative address if R2 holds the TOC pointer
+ o1 |= uint32(2 << 16) // Transform lis r31,ha into addis r31,r2,ha
+ r.SetType(objabi.R_ADDRPOWER_TOCREL)
+ }
+ r.SetOff(0)
+ r.SetSiz(8) // generates 2 relocations: HA + LO
+ r.SetSym(target)
+ r.SetAdd(offset)
+ } else {
+ // The target address is known, resolve it
+ t += offset
+ o1 |= (uint32(t) + 0x8000) >> 16 // HA
+ o2 |= uint32(t) & 0xFFFF // LO
+ }
+ }
+
+ o3 := uint32(0x7c0903a6) | 12<<21 // mtctr r12
+ o4 := uint32(0x4e800420) // bctr
+ ctxt.Arch.ByteOrder.PutUint32(P, o1)
+ ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
+ ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
+ ctxt.Arch.ByteOrder.PutUint32(P[12:], o4)
+ tramp.SetData(P)
+}
+
+// Unpack a pair of 32 bit instruction words from
+// a 64 bit relocation into instN and instN+1 in endian order.
+func unpackInstPair(target *ld.Target, r int64) (uint32, uint32) {
+ if target.IsBigEndian() {
+ return uint32(r >> 32), uint32(r)
+ }
+ return uint32(r), uint32(r >> 32)
+}
+
+// Pack a pair of 32 bit instruction words o1, o2 into 64 bit relocation
+// in endian order.
+func packInstPair(target *ld.Target, o1, o2 uint32) int64 {
+ if target.IsBigEndian() {
+ return (int64(o1) << 32) | int64(o2)
+ }
+ return int64(o1) | (int64(o2) << 32)
+}
+
+// Compute the high-adjusted value (always a signed 32b value) per the ELF ABI.
+// The returned value is always 0 <= x <= 0xFFFF.
+func computeHA(val int32) uint32 {
+ return uint32(uint16((val + 0x8000) >> 16))
+}
+
+// Compute the low value (the lower 16 bits of any 32b value) per the ELF ABI.
+// The returned value is always 0 <= x <= 0xFFFF.
+func computeLO(val int32) uint32 {
+ return uint32(uint16(val))
+}
+
+// Compute the high 18 bits of a signed 34b constant. Used to pack the high 18 bits
+// of a prefix34 relocation field. This assumes the input is already restricted to
+// 34 bits.
+func computePrefix34HI(val int64) uint32 {
+ return uint32((val >> 16) & 0x3FFFF)
+}
+
+func computeTLSLEReloc(target *ld.Target, ldr *loader.Loader, rs, s loader.Sym) int64 {
+ // The thread pointer points 0x7000 bytes after the start of the
+ // thread local storage area as documented in section "3.7.2 TLS
+ // Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI
+ // Specification".
+ v := ldr.SymValue(rs) - 0x7000
+ if target.IsAIX() {
+ // On AIX, the thread pointer points 0x7800 bytes after
+ // the TLS.
+ v -= 0x800
+ }
+
+ if int64(int32(v)) != v {
+ ldr.Errorf(s, "TLS offset out of range %d", v)
+ }
+ return v
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (relocatedOffset int64, nExtReloc int, ok bool) {
+ rs := r.Sym()
+ if target.IsExternal() {
+ // On AIX, relocations (except TLS ones) must be also done to the
+ // value with the current addresses.
+ switch rt := r.Type(); rt {
+ default:
+ if !target.IsAIX() {
+ return val, nExtReloc, false
+ }
+ case objabi.R_POWER_TLS, objabi.R_POWER_TLS_IE_PCREL34, objabi.R_POWER_TLS_LE_TPREL34:
+ nExtReloc = 1
+ return val, nExtReloc, true
+ case objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE:
+ if target.IsAIX() && rt == objabi.R_POWER_TLS_LE {
+ // Fixup val, an addis/addi pair of instructions, which generate a 32b displacement
+ // from the threadpointer (R13), into a 16b relocation. XCOFF only supports 16b
+ // TLS LE relocations. Likewise, verify this is an addis/addi sequence.
+ const expectedOpcodes = 0x3C00000038000000
+ const expectedOpmasks = 0xFC000000FC000000
+ if uint64(val)&expectedOpmasks != expectedOpcodes {
+ ldr.Errorf(s, "relocation for %s+%d is not an addis/addi pair: %16x", ldr.SymName(rs), r.Off(), uint64(val))
+ }
+ nval := (int64(uint32(0x380d0000)) | val&0x03e00000) << 32 // addi rX, r13, $0
+ nval |= int64(0x60000000) // nop
+ val = nval
+ nExtReloc = 1
+ } else {
+ nExtReloc = 2
+ }
+ return val, nExtReloc, true
+ case objabi.R_ADDRPOWER,
+ objabi.R_ADDRPOWER_DS,
+ objabi.R_ADDRPOWER_TOCREL,
+ objabi.R_ADDRPOWER_TOCREL_DS,
+ objabi.R_ADDRPOWER_GOT,
+ objabi.R_ADDRPOWER_PCREL:
+ nExtReloc = 2 // need two ELF relocations, see elfreloc1
+ if !target.IsAIX() {
+ return val, nExtReloc, true
+ }
+ case objabi.R_CALLPOWER, objabi.R_ADDRPOWER_D34, objabi.R_ADDRPOWER_PCREL34:
+ nExtReloc = 1
+ if !target.IsAIX() {
+ return val, nExtReloc, true
+ }
+ }
+ }
+
+ switch r.Type() {
+ case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS:
+ return archreloctoc(ldr, target, syms, r, s, val), nExtReloc, true
+ case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS, objabi.R_ADDRPOWER_D34, objabi.R_ADDRPOWER_PCREL34:
+ return archrelocaddr(ldr, target, syms, r, s, val), nExtReloc, true
+ case objabi.R_CALLPOWER:
+ // Bits 6 through 29 = (S + A - P) >> 2
+
+ t := ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
+
+ tgtName := ldr.SymName(rs)
+
+ // If we are linking PIE or shared code, all golang generated object files have an extra 2 instruction prologue
+ // to regenerate the TOC pointer from R12. The exception are two special case functions tested below. Note,
+ // local call offsets for externally generated objects are accounted for when converting into golang relocs.
+ if !ldr.AttrExternal(rs) && ldr.AttrShared(rs) && tgtName != "runtime.duffzero" && tgtName != "runtime.duffcopy" {
+ // Furthermore, only apply the offset if the target looks like the start of a function call.
+ if r.Add() == 0 && ldr.SymType(rs) == sym.STEXT {
+ t += 8
+ }
+ }
+
+ if t&3 != 0 {
+ ldr.Errorf(s, "relocation for %s+%d is not aligned: %d", ldr.SymName(rs), r.Off(), t)
+ }
+ // If branch offset is too far then create a trampoline.
+
+ if int64(int32(t<<6)>>6) != t {
+ ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t)
+ }
+ return val | int64(uint32(t)&^0xfc000003), nExtReloc, true
+ case objabi.R_POWER_TOC: // S + A - .TOC.
+ return ldr.SymValue(rs) + r.Add() - symtoc(ldr, syms, s), nExtReloc, true
+
+ case objabi.R_ADDRPOWER_PCREL: // S + A - P
+ t := ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
+ ha, l := unpackInstPair(target, val)
+ l |= computeLO(int32(t))
+ ha |= computeHA(int32(t))
+ return packInstPair(target, ha, l), nExtReloc, true
+
+ case objabi.R_POWER_TLS:
+ const OP_ADD = 31<<26 | 266<<1
+ const MASK_OP_ADD = 0x3F<<26 | 0x1FF<<1
+ if val&MASK_OP_ADD != OP_ADD {
+ ldr.Errorf(s, "R_POWER_TLS reloc only supports XO form ADD, not %08X", val)
+ }
+ // Verify RB is R13 in ADD RA,RB,RT.
+ if (val>>11)&0x1F != 13 {
+ // If external linking is made to support this, it may expect the linker to rewrite RB.
+ ldr.Errorf(s, "R_POWER_TLS reloc requires R13 in RB (%08X).", uint32(val))
+ }
+ return val, nExtReloc, true
+
+ case objabi.R_POWER_TLS_IE:
+ // Convert TLS_IE relocation to TLS_LE if supported.
+ if !(target.IsPIE() && target.IsElf()) {
+ log.Fatalf("cannot handle R_POWER_TLS_IE (sym %s) when linking non-PIE, non-ELF binaries internally", ldr.SymName(s))
+ }
+
+ // We are an ELF binary, we can safely convert to TLS_LE from:
+ // addis to, r2, x@got@tprel@ha
+ // ld to, to, x@got@tprel@l(to)
+ //
+ // to TLS_LE by converting to:
+ // addis to, r0, x@tprel@ha
+ // addi to, to, x@tprel@l(to)
+
+ const OP_ADDI = 14 << 26
+ const OP_MASK = 0x3F << 26
+ const OP_RA_MASK = 0x1F << 16
+ // convert r2 to r0, and ld to addi
+ mask := packInstPair(target, OP_RA_MASK, OP_MASK)
+ addi_op := packInstPair(target, 0, OP_ADDI)
+ val &^= mask
+ val |= addi_op
+ fallthrough
+
+ case objabi.R_POWER_TLS_LE:
+ v := computeTLSLEReloc(target, ldr, rs, s)
+ o1, o2 := unpackInstPair(target, val)
+ o1 |= computeHA(int32(v))
+ o2 |= computeLO(int32(v))
+ return packInstPair(target, o1, o2), nExtReloc, true
+
+ case objabi.R_POWER_TLS_IE_PCREL34:
+ // Convert TLS_IE relocation to TLS_LE if supported.
+ if !(target.IsPIE() && target.IsElf()) {
+ log.Fatalf("cannot handle R_POWER_TLS_IE (sym %s) when linking non-PIE, non-ELF binaries internally", ldr.SymName(s))
+ }
+
+ // We are an ELF binary, we can safely convert to TLS_LE_TPREL34 from:
+ // pld rX, x@got@tprel@pcrel
+ //
+ // to TLS_LE_TPREL32 by converting to:
+ // pla rX, x@tprel
+
+ const OP_MASK_PFX = 0xFFFFFFFF // Discard prefix word
+ const OP_MASK = (0x3F << 26) | 0xFFFF // Preserve RT, RA
+ const OP_PFX = 1<<26 | 2<<24
+ const OP_PLA = 14 << 26
+ mask := packInstPair(target, OP_MASK_PFX, OP_MASK)
+ pla_op := packInstPair(target, OP_PFX, OP_PLA)
+ val &^= mask
+ val |= pla_op
+ fallthrough
+
+ case objabi.R_POWER_TLS_LE_TPREL34:
+ v := computeTLSLEReloc(target, ldr, rs, s)
+ o1, o2 := unpackInstPair(target, val)
+ o1 |= computePrefix34HI(v)
+ o2 |= computeLO(int32(v))
+ return packInstPair(target, o1, o2), nExtReloc, true
+ }
+
+ return val, nExtReloc, false
+}
+
+func archrelocvariant(target *ld.Target, ldr *loader.Loader, r loader.Reloc, rv sym.RelocVariant, s loader.Sym, t int64, p []byte) (relocatedOffset int64) {
+ rs := r.Sym()
+ switch rv & sym.RV_TYPE_MASK {
+ default:
+ ldr.Errorf(s, "unexpected relocation variant %d", rv)
+ fallthrough
+
+ case sym.RV_NONE:
+ return t
+
+ case sym.RV_POWER_LO:
+ if rv&sym.RV_CHECK_OVERFLOW != 0 {
+ // Whether to check for signed or unsigned
+ // overflow depends on the instruction
+ var o1 uint32
+ if target.IsBigEndian() {
+ o1 = binary.BigEndian.Uint32(p[r.Off()-2:])
+
+ } else {
+ o1 = binary.LittleEndian.Uint32(p[r.Off():])
+ }
+ switch o1 >> 26 {
+ case 24, // ori
+ 26, // xori
+ 28: // andi
+ if t>>16 != 0 {
+ goto overflow
+ }
+
+ default:
+ if int64(int16(t)) != t {
+ goto overflow
+ }
+ }
+ }
+
+ return int64(int16(t))
+
+ case sym.RV_POWER_HA:
+ t += 0x8000
+ fallthrough
+
+ // Fallthrough
+ case sym.RV_POWER_HI:
+ t >>= 16
+
+ if rv&sym.RV_CHECK_OVERFLOW != 0 {
+ // Whether to check for signed or unsigned
+ // overflow depends on the instruction
+ var o1 uint32
+ if target.IsBigEndian() {
+ o1 = binary.BigEndian.Uint32(p[r.Off()-2:])
+ } else {
+ o1 = binary.LittleEndian.Uint32(p[r.Off():])
+ }
+ switch o1 >> 26 {
+ case 25, // oris
+ 27, // xoris
+ 29: // andis
+ if t>>16 != 0 {
+ goto overflow
+ }
+
+ default:
+ if int64(int16(t)) != t {
+ goto overflow
+ }
+ }
+ }
+
+ return int64(int16(t))
+
+ case sym.RV_POWER_DS:
+ var o1 uint32
+ if target.IsBigEndian() {
+ o1 = uint32(binary.BigEndian.Uint16(p[r.Off():]))
+ } else {
+ o1 = uint32(binary.LittleEndian.Uint16(p[r.Off():]))
+ }
+ if t&3 != 0 {
+ ldr.Errorf(s, "relocation for %s+%d is not aligned: %d", ldr.SymName(rs), r.Off(), t)
+ }
+ if (rv&sym.RV_CHECK_OVERFLOW != 0) && int64(int16(t)) != t {
+ goto overflow
+ }
+ return int64(o1)&0x3 | int64(int16(t))
+ }
+
+overflow:
+ ldr.Errorf(s, "relocation for %s+%d is too big: %d", ldr.SymName(rs), r.Off(), t)
+ return t
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch r.Type() {
+ case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE, objabi.R_POWER_TLS_IE_PCREL34, objabi.R_POWER_TLS_LE_TPREL34, objabi.R_CALLPOWER:
+ return ld.ExtrelocSimple(ldr, r), true
+ case objabi.R_ADDRPOWER,
+ objabi.R_ADDRPOWER_DS,
+ objabi.R_ADDRPOWER_TOCREL,
+ objabi.R_ADDRPOWER_TOCREL_DS,
+ objabi.R_ADDRPOWER_GOT,
+ objabi.R_ADDRPOWER_PCREL,
+ objabi.R_ADDRPOWER_D34,
+ objabi.R_ADDRPOWER_PCREL34:
+ return ld.ExtrelocViaOuterSym(ldr, r, s), true
+ }
+ return loader.ExtReloc{}, false
+}
+
+func addpltsym(ctxt *ld.Link, ldr *loader.Loader, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, &ctxt.Target, &ctxt.ArchSyms, s)
+
+ if ctxt.IsELF {
+ plt := ldr.MakeSymbolUpdater(ctxt.PLT)
+ rela := ldr.MakeSymbolUpdater(ctxt.RelaPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // Create the glink resolver if necessary
+ glink := ensureglinkresolver(ctxt, ldr)
+
+ // Write symbol resolver stub (just a branch to the
+ // glink resolver stub)
+ rel, _ := glink.AddRel(objabi.R_CALLPOWER)
+ rel.SetOff(int32(glink.Size()))
+ rel.SetSiz(4)
+ rel.SetSym(glink.Sym())
+ glink.AddUint32(ctxt.Arch, 0x48000000) // b .glink
+
+ // In the ppc64 ABI, the dynamic linker is responsible
+ // for writing the entire PLT. We just need to
+ // reserve 8 bytes for each PLT entry and generate a
+ // JMP_SLOT dynamic relocation for it.
+ //
+ // TODO(austin): ABI v1 is different
+ ldr.SetPlt(s, int32(plt.Size()))
+
+ plt.Grow(plt.Size() + 8)
+ plt.SetSize(plt.Size() + 8)
+
+ rela.AddAddrPlus(ctxt.Arch, plt.Sym(), int64(ldr.SymPlt(s)))
+ rela.AddUint64(ctxt.Arch, elf.R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_PPC64_JMP_SLOT)))
+ rela.AddUint64(ctxt.Arch, 0)
+ } else {
+ ctxt.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
+
+// Generate the glink resolver stub if necessary and return the .glink section.
+func ensureglinkresolver(ctxt *ld.Link, ldr *loader.Loader) *loader.SymbolBuilder {
+ glink := ldr.CreateSymForUpdate(".glink", 0)
+ if glink.Size() != 0 {
+ return glink
+ }
+
+ // This is essentially the resolver from the ppc64 ELFv2 ABI.
+ // At entry, r12 holds the address of the symbol resolver stub
+ // for the target routine and the argument registers hold the
+ // arguments for the target routine.
+ //
+ // PC-rel offsets are computed once the final codesize of the
+ // resolver is known.
+ //
+ // This stub is PIC, so first get the PC of label 1 into r11.
+ glink.AddUint32(ctxt.Arch, 0x7c0802a6) // mflr r0
+ glink.AddUint32(ctxt.Arch, 0x429f0005) // bcl 20,31,1f
+ glink.AddUint32(ctxt.Arch, 0x7d6802a6) // 1: mflr r11
+ glink.AddUint32(ctxt.Arch, 0x7c0803a6) // mtlr r0
+
+ // Compute the .plt array index from the entry point address
+ // into r0. This is computed relative to label 1 above.
+ glink.AddUint32(ctxt.Arch, 0x38000000) // li r0,-(res_0-1b)
+ glink.AddUint32(ctxt.Arch, 0x7c006214) // add r0,r0,r12
+ glink.AddUint32(ctxt.Arch, 0x7c0b0050) // sub r0,r0,r11
+ glink.AddUint32(ctxt.Arch, 0x7800f082) // srdi r0,r0,2
+
+ // Load the PC-rel offset of ".plt - 1b", and add it to 1b.
+ // This is stored after this stub and before the resolvers.
+ glink.AddUint32(ctxt.Arch, 0xe98b0000) // ld r12,res_0-1b-8(r11)
+ glink.AddUint32(ctxt.Arch, 0x7d6b6214) // add r11,r11,r12
+
+ // Load r12 = dynamic resolver address and r11 = DSO
+ // identifier from the first two doublewords of the PLT.
+ glink.AddUint32(ctxt.Arch, 0xe98b0000) // ld r12,0(r11)
+ glink.AddUint32(ctxt.Arch, 0xe96b0008) // ld r11,8(r11)
+
+ // Jump to the dynamic resolver
+ glink.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
+ glink.AddUint32(ctxt.Arch, 0x4e800420) // bctr
+
+ // Store the PC-rel offset to the PLT
+ r, _ := glink.AddRel(objabi.R_PCREL)
+ r.SetSym(ctxt.PLT)
+ r.SetSiz(8)
+ r.SetOff(int32(glink.Size()))
+ r.SetAdd(glink.Size()) // Adjust the offset to be relative to label 1 above.
+ glink.AddUint64(ctxt.Arch, 0) // The offset to the PLT.
+
+ // Resolve PC-rel offsets above now the final size of the stub is known.
+ res0m1b := glink.Size() - 8 // res_0 - 1b
+ glink.SetUint32(ctxt.Arch, 16, 0x38000000|uint32(uint16(-res0m1b)))
+ glink.SetUint32(ctxt.Arch, 32, 0xe98b0000|uint32(uint16(res0m1b-8)))
+
+ // The symbol resolvers must immediately follow.
+ // res_0:
+
+ // Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes
+ // before the first symbol resolver stub.
+ du := ldr.MakeSymbolUpdater(ctxt.Dynamic)
+ ld.Elfwritedynentsymplus(ctxt, du, elf.DT_PPC64_GLINK, glink.Sym(), glink.Size()-32)
+
+ return glink
+}
diff --git a/src/cmd/link/internal/ppc64/l.go b/src/cmd/link/internal/ppc64/l.go
new file mode 100644
index 0000000..e8d3b68
--- /dev/null
+++ b/src/cmd/link/internal/ppc64/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ppc64
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 16
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 1
+ dwarfRegLR = 65
+)
diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go
new file mode 100644
index 0000000..f580c55
--- /dev/null
+++ b/src/cmd/link/internal/ppc64/obj.go
@@ -0,0 +1,113 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ppc64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "internal/buildcfg"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchPPC64LE
+ dynld := "/lib64/ld64.so.2"
+ musl := "/lib/ld-musl-powerpc64le.so.1"
+
+ if buildcfg.GOARCH == "ppc64" {
+ arch = sys.ArchPPC64
+ dynld = "/lib64/ld64.so.1"
+ musl = "/lib/ld-musl-powerpc64.so.1"
+ }
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ TrampLimit: 0x1c00000,
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Trampoline: trampoline,
+ Machoreloc1: machoreloc1,
+ Xcoffreloc1: xcoffreloc1,
+
+ Linuxdynld: dynld,
+ LinuxdynldMusl: musl,
+
+ Freebsddynld: "XXX",
+ Openbsddynld: "XXX",
+ Netbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4128
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hlinux: /* ppc64 elf */
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+
+ case objabi.Haix:
+ ld.Xcoffinit(ctxt)
+ }
+}
diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go
new file mode 100644
index 0000000..cb53a60
--- /dev/null
+++ b/src/cmd/link/internal/riscv64/asm.go
@@ -0,0 +1,361 @@
+// Copyright 2019 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.
+
+package riscv64
+
+import (
+ "cmd/internal/obj/riscv"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "fmt"
+ "log"
+ "sort"
+)
+
+// fakeLabelName matches the RISCV_FAKE_LABEL_NAME from binutils.
+const fakeLabelName = ".L0 "
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+}
+
+func genSymsLate(ctxt *ld.Link, ldr *loader.Loader) {
+ if ctxt.LinkMode != ld.LinkExternal {
+ return
+ }
+
+ // Generate a local text symbol for each relocation target, as the
+ // R_RISCV_PCREL_LO12_* relocations generated by elfreloc1 need it.
+ if ctxt.Textp == nil {
+ log.Fatal("genSymsLate called before Textp has been assigned")
+ }
+ var hi20Syms []loader.Sym
+ for _, s := range ctxt.Textp {
+ relocs := ldr.Relocs(s)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Type() != objabi.R_RISCV_PCREL_ITYPE && r.Type() != objabi.R_RISCV_PCREL_STYPE &&
+ r.Type() != objabi.R_RISCV_TLS_IE_ITYPE && r.Type() != objabi.R_RISCV_TLS_IE_STYPE {
+ continue
+ }
+ if r.Off() == 0 && ldr.SymType(s) == sym.STEXT {
+ // Use the symbol for the function instead of creating
+ // an overlapping symbol.
+ continue
+ }
+
+ // TODO(jsing): Consider generating ELF symbols without needing
+ // loader symbols, in order to reduce memory consumption. This
+ // would require changes to genelfsym so that it called
+ // putelfsym and putelfsyment as appropriate.
+ sb := ldr.MakeSymbolBuilder(fakeLabelName)
+ sb.SetType(sym.STEXT)
+ sb.SetValue(ldr.SymValue(s) + int64(r.Off()))
+ sb.SetLocal(true)
+ sb.SetReachable(true)
+ sb.SetVisibilityHidden(true)
+ sb.SetSect(ldr.SymSect(s))
+ if outer := ldr.OuterSym(s); outer != 0 {
+ ldr.AddInteriorSym(outer, sb.Sym())
+ }
+ hi20Syms = append(hi20Syms, sb.Sym())
+ }
+ }
+ ctxt.Textp = append(ctxt.Textp, hi20Syms...)
+ ldr.SortSyms(ctxt.Textp)
+}
+
+func findHI20Symbol(ctxt *ld.Link, ldr *loader.Loader, val int64) loader.Sym {
+ idx := sort.Search(len(ctxt.Textp), func(i int) bool { return ldr.SymValue(ctxt.Textp[i]) >= val })
+ if idx >= len(ctxt.Textp) {
+ return 0
+ }
+ if s := ctxt.Textp[idx]; ldr.SymValue(s) == val && ldr.SymType(s) == sym.STEXT {
+ return s
+ }
+ return 0
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ switch r.Type {
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ out.Write64(uint64(sectoff))
+ switch r.Size {
+ case 4:
+ out.Write64(uint64(elf.R_RISCV_32) | uint64(elfsym)<<32)
+ case 8:
+ out.Write64(uint64(elf.R_RISCV_64) | uint64(elfsym)<<32)
+ default:
+ ld.Errorf(nil, "unknown size %d for %v relocation", r.Size, r.Type)
+ return false
+ }
+ out.Write64(uint64(r.Xadd))
+
+ case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(elf.R_RISCV_JAL) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+
+ case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
+ // Find the text symbol for the AUIPC instruction targeted
+ // by this relocation.
+ relocs := ldr.Relocs(s)
+ offset := int64(relocs.At(ri).Off())
+ hi20Sym := findHI20Symbol(ctxt, ldr, ldr.SymValue(s)+offset)
+ if hi20Sym == 0 {
+ ld.Errorf(nil, "failed to find text symbol for HI20 relocation at %d (%x)", sectoff, ldr.SymValue(s)+offset)
+ return false
+ }
+ hi20ElfSym := ld.ElfSymForReloc(ctxt, hi20Sym)
+
+ // Emit two relocations - a R_RISCV_PCREL_HI20 relocation and a
+ // corresponding R_RISCV_PCREL_LO12_I or R_RISCV_PCREL_LO12_S relocation.
+ // Note that the LO12 relocation must point to a target that has a valid
+ // HI20 PC-relative relocation text symbol, which in turn points to the
+ // given symbol. For further details see the ELF specification for RISC-V:
+ //
+ // https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#pc-relative-symbol-addresses
+ //
+ var hiRel, loRel elf.R_RISCV
+ switch r.Type {
+ case objabi.R_RISCV_PCREL_ITYPE:
+ hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_I
+ case objabi.R_RISCV_PCREL_STYPE:
+ hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_S
+ case objabi.R_RISCV_TLS_IE_ITYPE:
+ hiRel, loRel = elf.R_RISCV_TLS_GOT_HI20, elf.R_RISCV_PCREL_LO12_I
+ case objabi.R_RISCV_TLS_IE_STYPE:
+ hiRel, loRel = elf.R_RISCV_TLS_GOT_HI20, elf.R_RISCV_PCREL_LO12_S
+ }
+ out.Write64(uint64(sectoff))
+ out.Write64(uint64(hiRel) | uint64(elfsym)<<32)
+ out.Write64(uint64(r.Xadd))
+ out.Write64(uint64(sectoff + 4))
+ out.Write64(uint64(loRel) | uint64(hi20ElfSym)<<32)
+ out.Write64(uint64(0))
+
+ default:
+ return false
+ }
+
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
+ log.Fatalf("elfsetupplt")
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ log.Fatalf("machoreloc1 not implemented")
+ return false
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ rs := r.Sym()
+ pc := ldr.SymValue(s) + int64(r.Off())
+
+ // If the call points to a trampoline, see if we can reach the symbol
+ // directly. This situation can occur when the relocation symbol is
+ // not assigned an address until after the trampolines are generated.
+ if r.Type() == objabi.R_RISCV_CALL_TRAMP {
+ relocs := ldr.Relocs(rs)
+ if relocs.Count() != 1 {
+ ldr.Errorf(s, "trampoline %v has %d relocations", ldr.SymName(rs), relocs.Count())
+ }
+ tr := relocs.At(0)
+ if tr.Type() != objabi.R_RISCV_PCREL_ITYPE {
+ ldr.Errorf(s, "trampoline %v has unexpected relocation %v", ldr.SymName(rs), tr.Type())
+ }
+ trs := tr.Sym()
+ if ldr.SymValue(trs) != 0 && ldr.SymType(trs) != sym.SDYNIMPORT && ldr.SymType(trs) != sym.SUNDEFEXT {
+ trsOff := ldr.SymValue(trs) + tr.Add() - pc
+ if trsOff >= -(1<<20) && trsOff < (1<<20) {
+ r.SetType(objabi.R_RISCV_CALL)
+ r.SetSym(trs)
+ r.SetAdd(tr.Add())
+ rs = trs
+ }
+ }
+
+ }
+
+ if target.IsExternal() {
+ switch r.Type() {
+ case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
+ return val, 1, true
+
+ case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
+ return val, 2, true
+ }
+
+ return val, 0, false
+ }
+
+ off := ldr.SymValue(rs) + r.Add() - pc
+
+ switch r.Type() {
+ case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
+ // Generate instruction immediates.
+ imm, err := riscv.EncodeJImmediate(off)
+ if err != nil {
+ ldr.Errorf(s, "cannot encode R_RISCV_CALL relocation offset for %s: %v", ldr.SymName(rs), err)
+ }
+ immMask := int64(riscv.JTypeImmMask)
+
+ val = (val &^ immMask) | int64(imm)
+
+ return val, 0, true
+
+ case objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
+ // TLS relocations are not currently handled for internal linking.
+ // For now, TLS is only used when cgo is in use and cgo currently
+ // requires external linking. However, we need to accept these
+ // relocations so that code containing TLS variables will link,
+ // even when they're not being used. For now, replace these
+ // instructions with EBREAK to detect accidental use.
+ const ebreakIns = 0x00100073
+ return ebreakIns<<32 | ebreakIns, 0, true
+
+ case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
+ // Generate AUIPC and second instruction immediates.
+ low, high, err := riscv.Split32BitImmediate(off)
+ if err != nil {
+ ldr.Errorf(s, "R_RISCV_PCREL_ relocation does not fit in 32 bits: %d", off)
+ }
+
+ auipcImm, err := riscv.EncodeUImmediate(high)
+ if err != nil {
+ ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ AUIPC relocation offset for %s: %v", ldr.SymName(rs), err)
+ }
+
+ var secondImm, secondImmMask int64
+ switch r.Type() {
+ case objabi.R_RISCV_PCREL_ITYPE:
+ secondImmMask = riscv.ITypeImmMask
+ secondImm, err = riscv.EncodeIImmediate(low)
+ if err != nil {
+ ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ITYPE I-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
+ }
+ case objabi.R_RISCV_PCREL_STYPE:
+ secondImmMask = riscv.STypeImmMask
+ secondImm, err = riscv.EncodeSImmediate(low)
+ if err != nil {
+ ldr.Errorf(s, "cannot encode R_RISCV_PCREL_STYPE S-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
+ }
+ default:
+ panic(fmt.Sprintf("Unknown relocation type: %v", r.Type()))
+ }
+
+ auipc := int64(uint32(val))
+ second := int64(uint32(val >> 32))
+
+ auipc = (auipc &^ riscv.UTypeImmMask) | int64(uint32(auipcImm))
+ second = (second &^ secondImmMask) | int64(uint32(secondImm))
+
+ return second<<32 | auipc, 0, true
+ }
+
+ return val, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
+ log.Fatalf("archrelocvariant")
+ return -1
+}
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+ switch r.Type() {
+ case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
+ return ld.ExtrelocSimple(ldr, r), true
+
+ case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
+ return ld.ExtrelocViaOuterSym(ldr, r, s), true
+ }
+ return loader.ExtReloc{}, false
+}
+
+func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
+ relocs := ldr.Relocs(s)
+ r := relocs.At(ri)
+
+ switch r.Type() {
+ case objabi.R_RISCV_CALL:
+ pc := ldr.SymValue(s) + int64(r.Off())
+ off := ldr.SymValue(rs) + r.Add() - pc
+
+ // Relocation symbol has an address and is directly reachable,
+ // therefore there is no need for a trampoline.
+ if ldr.SymValue(rs) != 0 && off >= -(1<<20) && off < (1<<20) && (*ld.FlagDebugTramp <= 1 || ldr.SymPkg(s) == ldr.SymPkg(rs)) {
+ break
+ }
+
+ // Relocation symbol is too far for a direct call or has not
+ // yet been given an address. See if an existing trampoline is
+ // reachable and if so, reuse it. Otherwise we need to create
+ // a new trampoline.
+ var tramp loader.Sym
+ for i := 0; ; i++ {
+ oName := ldr.SymName(rs)
+ name := fmt.Sprintf("%s-tramp%d", oName, i)
+ if r.Add() != 0 {
+ name = fmt.Sprintf("%s%+x-tramp%d", oName, r.Add(), i)
+ }
+ tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
+ ldr.SetAttrReachable(tramp, true)
+ if ldr.SymType(tramp) == sym.SDYNIMPORT {
+ // Do not reuse trampoline defined in other module.
+ continue
+ }
+ if oName == "runtime.deferreturn" {
+ ldr.SetIsDeferReturnTramp(tramp, true)
+ }
+ if ldr.SymValue(tramp) == 0 {
+ // Either trampoline does not exist or we found one
+ // that does not have an address assigned and will be
+ // laid down immediately after the current function.
+ break
+ }
+
+ trampOff := ldr.SymValue(tramp) - (ldr.SymValue(s) + int64(r.Off()))
+ if trampOff >= -(1<<20) && trampOff < (1<<20) {
+ // An existing trampoline that is reachable.
+ break
+ }
+ }
+ if ldr.SymType(tramp) == 0 {
+ trampb := ldr.MakeSymbolUpdater(tramp)
+ ctxt.AddTramp(trampb)
+ genCallTramp(ctxt.Arch, ctxt.LinkMode, ldr, trampb, rs, int64(r.Add()))
+ }
+ sb := ldr.MakeSymbolUpdater(s)
+ if ldr.SymValue(rs) == 0 {
+ // In this case the target symbol has not yet been assigned an
+ // address, so we have to assume a trampoline is required. Mark
+ // this as a call via a trampoline so that we can potentially
+ // switch to a direct call during relocation.
+ sb.SetRelocType(ri, objabi.R_RISCV_CALL_TRAMP)
+ }
+ relocs := sb.Relocs()
+ r := relocs.At(ri)
+ r.SetSym(tramp)
+ r.SetAdd(0)
+
+ default:
+ ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
+ }
+}
+
+func genCallTramp(arch *sys.Arch, linkmode ld.LinkMode, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
+ tramp.AddUint32(arch, 0x00000f97) // AUIPC $0, X31
+ tramp.AddUint32(arch, 0x000f8067) // JALR X0, (X31)
+
+ r, _ := tramp.AddRel(objabi.R_RISCV_PCREL_ITYPE)
+ r.SetSiz(8)
+ r.SetSym(target)
+ r.SetAdd(offset)
+}
diff --git a/src/cmd/link/internal/riscv64/l.go b/src/cmd/link/internal/riscv64/l.go
new file mode 100644
index 0000000..a302657
--- /dev/null
+++ b/src/cmd/link/internal/riscv64/l.go
@@ -0,0 +1,14 @@
+// Copyright 2019 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.
+
+package riscv64
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1
+ funcAlign = 8
+
+ dwarfRegLR = 1
+ dwarfRegSP = 2
+)
diff --git a/src/cmd/link/internal/riscv64/obj.go b/src/cmd/link/internal/riscv64/obj.go
new file mode 100644
index 0000000..8e4e41f
--- /dev/null
+++ b/src/cmd/link/internal/riscv64/obj.go
@@ -0,0 +1,68 @@
+// Copyright 2019 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.
+
+package riscv64
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchRISCV64
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+
+ // TrampLimit is set such that we always run the trampoline
+ // generation code. This is necessary since calls to external
+ // symbols require the use of trampolines, regardless of the
+ // text size.
+ TrampLimit: 1,
+ Trampoline: trampoline,
+
+ Gentext: gentext,
+ GenSymsLate: genSymsLate,
+ Machoreloc1: machoreloc1,
+
+ Linuxdynld: "/lib/ld.so.1",
+
+ Freebsddynld: "/usr/libexec/ld-elf.so.1",
+ Netbsddynld: "XXX",
+ Openbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ case objabi.Hlinux, objabi.Hfreebsd:
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+ }
+}
diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go
new file mode 100644
index 0000000..2d9f750
--- /dev/null
+++ b/src/cmd/link/internal/s390x/asm.go
@@ -0,0 +1,449 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package s390x
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+)
+
+// gentext generates assembly to append the local moduledata to the global
+// 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
+//
+// The job of appending the moduledata is delegated to runtime.addmoduledata.
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ // larl %r2, <local.moduledata>
+ initfunc.AddUint8(0xc0)
+ initfunc.AddUint8(0x20)
+ initfunc.AddSymRef(ctxt.Arch, ctxt.Moduledata, 6, objabi.R_PCREL, 4)
+ r1 := initfunc.Relocs()
+ ldr.SetRelocVariant(initfunc.Sym(), r1.Count()-1, sym.RV_390_DBL)
+
+ // jg <runtime.addmoduledata[@plt]>
+ initfunc.AddUint8(0xc0)
+ initfunc.AddUint8(0xf4)
+ initfunc.AddSymRef(ctxt.Arch, addmoduledata, 6, objabi.R_CALL, 4)
+ r2 := initfunc.Relocs()
+ ldr.SetRelocVariant(initfunc.Sym(), r2.Count()-1, sym.RV_390_DBL)
+
+ // undef (for debugging)
+ initfunc.AddUint32(ctxt.Arch, 0)
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d", r.Type())
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_12),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT12):
+ ldr.Errorf(s, "s390x 12-bit relocations have not been implemented (relocation type %d)", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_8),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_16),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_390_nn relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC64):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_390_PCnn relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT16),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT64):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT16DBL),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32DBL):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ r.SetSym(syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT64):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ r.SetSym(syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_COPY):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GLOB_DAT):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_JMP_SLOT):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_RELATIVE):
+ ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset)
+ return false
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTOFF):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_390_GOTOFF relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_GOTOFF)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPC):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ r.SetSym(syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16DBL),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32DBL):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_390_PCnnDBL relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPCDBL):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL)
+ r.SetSym(syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz()))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT):
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_390_GLOB_DAT))
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL)
+ r.SetSym(syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))+int64(r.Siz()))
+ return true
+ }
+ // Handle references to ELF symbols from our own object files.
+ return targType != sym.SDYNIMPORT
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write64(uint64(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_TLS_LE:
+ switch siz {
+ default:
+ return false
+ case 4:
+ // WARNING - silently ignored by linker in ELF64
+ out.Write64(uint64(elf.R_390_TLS_LE32) | uint64(elfsym)<<32)
+ case 8:
+ // WARNING - silently ignored by linker in ELF32
+ out.Write64(uint64(elf.R_390_TLS_LE64) | uint64(elfsym)<<32)
+ }
+ case objabi.R_TLS_IE:
+ switch siz {
+ default:
+ return false
+ case 4:
+ out.Write64(uint64(elf.R_390_TLS_IEENT) | uint64(elfsym)<<32)
+ }
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ switch siz {
+ default:
+ return false
+ case 4:
+ out.Write64(uint64(elf.R_390_32) | uint64(elfsym)<<32)
+ case 8:
+ out.Write64(uint64(elf.R_390_64) | uint64(elfsym)<<32)
+ }
+ case objabi.R_GOTPCREL:
+ if siz == 4 {
+ out.Write64(uint64(elf.R_390_GOTENT) | uint64(elfsym)<<32)
+ } else {
+ return false
+ }
+ case objabi.R_PCREL, objabi.R_PCRELDBL, objabi.R_CALL:
+ elfrel := elf.R_390_NONE
+ rVariant := ldr.RelocVariant(s, ri)
+ isdbl := rVariant&sym.RV_TYPE_MASK == sym.RV_390_DBL
+ // TODO(mundaym): all DBL style relocations should be
+ // signalled using the variant - see issue 14218.
+ switch r.Type {
+ case objabi.R_PCRELDBL, objabi.R_CALL:
+ isdbl = true
+ }
+ if ldr.SymType(r.Xsym) == sym.SDYNIMPORT && (ldr.SymElfType(r.Xsym) == elf.STT_FUNC || r.Type == objabi.R_CALL) {
+ if isdbl {
+ switch siz {
+ case 2:
+ elfrel = elf.R_390_PLT16DBL
+ case 4:
+ elfrel = elf.R_390_PLT32DBL
+ }
+ } else {
+ switch siz {
+ case 4:
+ elfrel = elf.R_390_PLT32
+ case 8:
+ elfrel = elf.R_390_PLT64
+ }
+ }
+ } else {
+ if isdbl {
+ switch siz {
+ case 2:
+ elfrel = elf.R_390_PC16DBL
+ case 4:
+ elfrel = elf.R_390_PC32DBL
+ }
+ } else {
+ switch siz {
+ case 2:
+ elfrel = elf.R_390_PC16
+ case 4:
+ elfrel = elf.R_390_PC32
+ case 8:
+ elfrel = elf.R_390_PC64
+ }
+ }
+ }
+ if elfrel == elf.R_390_NONE {
+ return false // unsupported size/dbl combination
+ }
+ out.Write64(uint64(elfrel) | uint64(elfsym)<<32)
+ }
+
+ out.Write64(uint64(r.Xadd))
+ return true
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // stg %r1,56(%r15)
+ plt.AddUint8(0xe3)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0xf0)
+ plt.AddUint8(0x38)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x24)
+ // larl %r1,_GLOBAL_OFFSET_TABLE_
+ plt.AddUint8(0xc0)
+ plt.AddUint8(0x10)
+ plt.AddSymRef(ctxt.Arch, got.Sym(), 6, objabi.R_PCRELDBL, 4)
+ // mvc 48(8,%r15),8(%r1)
+ plt.AddUint8(0xd2)
+ plt.AddUint8(0x07)
+ plt.AddUint8(0xf0)
+ plt.AddUint8(0x30)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x08)
+ // lg %r1,16(%r1)
+ plt.AddUint8(0xe3)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x04)
+ // br %r1
+ plt.AddUint8(0x07)
+ plt.AddUint8(0xf1)
+ // nopr %r0
+ plt.AddUint8(0x07)
+ plt.AddUint8(0x00)
+ // nopr %r0
+ plt.AddUint8(0x07)
+ plt.AddUint8(0x00)
+ // nopr %r0
+ plt.AddUint8(0x07)
+ plt.AddUint8(0x00)
+
+ // assume got->size == 0 too
+ got.AddAddrPlus(ctxt.Arch, dynamic, 0)
+
+ got.AddUint64(ctxt.Arch, 0)
+ got.AddUint64(ctxt.Arch, 0)
+ }
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
+ return val, 0, false
+}
+
+func archrelocvariant(target *ld.Target, ldr *loader.Loader, r loader.Reloc, rv sym.RelocVariant, s loader.Sym, t int64, p []byte) int64 {
+ switch rv & sym.RV_TYPE_MASK {
+ default:
+ ldr.Errorf(s, "unexpected relocation variant %d", rv)
+ return t
+
+ case sym.RV_NONE:
+ return t
+
+ case sym.RV_390_DBL:
+ if t&1 != 0 {
+ ldr.Errorf(s, "%s+%v is not 2-byte aligned", ldr.SymName(r.Sym()), ldr.SymValue(r.Sym()))
+ }
+ return t >> 1
+ }
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ got := ldr.MakeSymbolUpdater(syms.GOT)
+ rela := ldr.MakeSymbolUpdater(syms.RelaPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+ // larl %r1,_GLOBAL_OFFSET_TABLE_+index
+
+ plt.AddUint8(0xc0)
+ plt.AddUint8(0x10)
+ plt.AddPCRelPlus(target.Arch, got.Sym(), got.Size()+6)
+ pltrelocs := plt.Relocs()
+ ldr.SetRelocVariant(plt.Sym(), pltrelocs.Count()-1, sym.RV_390_DBL)
+
+ // add to got: pointer to current pos in plt
+ got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size()+8) // weird but correct
+ // lg %r1,0(%r1)
+ plt.AddUint8(0xe3)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x04)
+ // br %r1
+ plt.AddUint8(0x07)
+ plt.AddUint8(0xf1)
+ // basr %r1,%r0
+ plt.AddUint8(0x0d)
+ plt.AddUint8(0x10)
+ // lgf %r1,12(%r1)
+ plt.AddUint8(0xe3)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x10)
+ plt.AddUint8(0x0c)
+ plt.AddUint8(0x00)
+ plt.AddUint8(0x14)
+ // jg .plt
+ plt.AddUint8(0xc0)
+ plt.AddUint8(0xf4)
+
+ plt.AddUint32(target.Arch, uint32(-((plt.Size() - 2) >> 1))) // roll-your-own relocation
+ //.plt index
+ plt.AddUint32(target.Arch, uint32(rela.Size())) // rela size before current entry
+
+ // rela
+ rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8)
+
+ sDynid := ldr.SymDynid(s)
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_390_JMP_SLOT)))
+ rela.AddUint64(target.Arch, 0)
+
+ ldr.SetPlt(s, int32(plt.Size()-32))
+
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
diff --git a/src/cmd/link/internal/s390x/l.go b/src/cmd/link/internal/s390x/l.go
new file mode 100644
index 0000000..f040587
--- /dev/null
+++ b/src/cmd/link/internal/s390x/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package s390x
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 2 // min data alignment
+ funcAlign = 16
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 15
+ dwarfRegLR = 14
+)
diff --git a/src/cmd/link/internal/s390x/obj.go b/src/cmd/link/internal/s390x/obj.go
new file mode 100644
index 0000000..3aa8948
--- /dev/null
+++ b/src/cmd/link/internal/s390x/obj.go
@@ -0,0 +1,88 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package s390x
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.ArchS390X
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+
+ Linuxdynld: "/lib64/ld64.so.1",
+ LinuxdynldMusl: "/lib/ld-musl-s390x.so.1",
+
+ // not relevant for s390x
+ Freebsddynld: "XXX",
+ Openbsddynld: "XXX",
+ Netbsddynld: "XXX",
+ Dragonflydynld: "XXX",
+ Solarisdynld: "XXX",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hlinux: // s390x ELF
+ ld.Elfinit(ctxt)
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 0x10000
+ }
+ }
+}
diff --git a/src/cmd/link/internal/sym/compilation_unit.go b/src/cmd/link/internal/sym/compilation_unit.go
new file mode 100644
index 0000000..3bad5bf
--- /dev/null
+++ b/src/cmd/link/internal/sym/compilation_unit.go
@@ -0,0 +1,35 @@
+// Copyright 2019 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.
+
+package sym
+
+import "cmd/internal/dwarf"
+
+// LoaderSym holds a loader.Sym value. We can't refer to this
+// type from the sym package since loader imports sym.
+type LoaderSym int
+
+// A CompilationUnit represents a set of source files that are compiled
+// together. Since all Go sources in a Go package are compiled together,
+// there's one CompilationUnit per package that represents all Go sources in
+// that package, plus one for each assembly file.
+//
+// Equivalently, there's one CompilationUnit per object file in each Library
+// loaded by the linker.
+//
+// These are used for both DWARF and pclntab generation.
+type CompilationUnit struct {
+ Lib *Library // Our library
+ PclnIndex int // Index of this CU in pclntab
+ PCs []dwarf.Range // PC ranges, relative to Textp[0]
+ DWInfo *dwarf.DWDie // CU root DIE
+ FileTable []string // The file table used in this compilation unit.
+
+ Consts LoaderSym // Package constants DIEs
+ FuncDIEs []LoaderSym // Function DIE subtrees
+ VarDIEs []LoaderSym // Global variable DIEs
+ AbsFnDIEs []LoaderSym // Abstract function DIE subtrees
+ RangeSyms []LoaderSym // Symbols for debug_range
+ Textp []LoaderSym // Text symbols in this CU
+}
diff --git a/src/cmd/link/internal/sym/library.go b/src/cmd/link/internal/sym/library.go
new file mode 100644
index 0000000..876b5ff
--- /dev/null
+++ b/src/cmd/link/internal/sym/library.go
@@ -0,0 +1,27 @@
+// Copyright 2017 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.
+
+package sym
+
+import "cmd/internal/goobj"
+
+type Library struct {
+ Objref string
+ Srcref string
+ File string
+ Pkg string
+ Shlib string
+ Fingerprint goobj.FingerprintType
+ Autolib []goobj.ImportedPkg
+ Imports []*Library
+ Main bool
+ Units []*CompilationUnit
+
+ Textp []LoaderSym // text syms defined in this library
+ DupTextSyms []LoaderSym // dupok text syms defined in this library
+}
+
+func (l Library) String() string {
+ return l.Pkg
+}
diff --git a/src/cmd/link/internal/sym/reloc.go b/src/cmd/link/internal/sym/reloc.go
new file mode 100644
index 0000000..a44dcdd
--- /dev/null
+++ b/src/cmd/link/internal/sym/reloc.go
@@ -0,0 +1,76 @@
+// Copyright 2017 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.
+
+package sym
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "debug/elf"
+)
+
+// RelocVariant is a linker-internal variation on a relocation.
+type RelocVariant uint8
+
+const (
+ RV_NONE RelocVariant = iota
+ RV_POWER_LO
+ RV_POWER_HI
+ RV_POWER_HA
+ RV_POWER_DS
+
+ // RV_390_DBL is a s390x-specific relocation variant that indicates that
+ // the value to be placed into the relocatable field should first be
+ // divided by 2.
+ RV_390_DBL
+
+ RV_CHECK_OVERFLOW RelocVariant = 1 << 7
+ RV_TYPE_MASK RelocVariant = RV_CHECK_OVERFLOW - 1
+)
+
+func RelocName(arch *sys.Arch, r objabi.RelocType) string {
+ // We didn't have some relocation types at Go1.4.
+ // Uncomment code when we include those in bootstrap code.
+
+ switch {
+ case r >= objabi.MachoRelocOffset: // Mach-O
+ // nr := (r - objabi.MachoRelocOffset)>>1
+ // switch ctxt.Arch.Family {
+ // case sys.AMD64:
+ // return macho.RelocTypeX86_64(nr).String()
+ // case sys.ARM:
+ // return macho.RelocTypeARM(nr).String()
+ // case sys.ARM64:
+ // return macho.RelocTypeARM64(nr).String()
+ // case sys.I386:
+ // return macho.RelocTypeGeneric(nr).String()
+ // default:
+ // panic("unreachable")
+ // }
+ case r >= objabi.ElfRelocOffset: // ELF
+ nr := r - objabi.ElfRelocOffset
+ switch arch.Family {
+ case sys.AMD64:
+ return elf.R_X86_64(nr).String()
+ case sys.ARM:
+ return elf.R_ARM(nr).String()
+ case sys.ARM64:
+ return elf.R_AARCH64(nr).String()
+ case sys.I386:
+ return elf.R_386(nr).String()
+ case sys.Loong64:
+ return elf.R_LARCH(nr).String()
+ case sys.MIPS, sys.MIPS64:
+ return elf.R_MIPS(nr).String()
+ case sys.PPC64:
+ return elf.R_PPC64(nr).String()
+ case sys.S390X:
+ return elf.R_390(nr).String()
+ default:
+ panic("unreachable")
+ }
+ }
+
+ return r.String()
+}
diff --git a/src/cmd/link/internal/sym/segment.go b/src/cmd/link/internal/sym/segment.go
new file mode 100644
index 0000000..c889e71
--- /dev/null
+++ b/src/cmd/link/internal/sym/segment.go
@@ -0,0 +1,68 @@
+// Inferno utils/8l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package sym
+
+// Terrible but standard terminology.
+// A segment describes a block of file to load into memory.
+// A section further describes the pieces of that block for
+// use in debuggers and such.
+
+type Segment struct {
+ Rwx uint8 // permission as usual unix bits (5 = r-x etc)
+ Vaddr uint64 // virtual address
+ Length uint64 // length in memory
+ Fileoff uint64 // file offset
+ Filelen uint64 // length on disk
+ Sections []*Section
+}
+
+type Section struct {
+ Rwx uint8
+ Extnum int16
+ Align int32
+ Name string
+ Vaddr uint64
+ Length uint64
+ Seg *Segment
+ Elfsect interface{} // an *ld.ElfShdr
+ Reloff uint64
+ Rellen uint64
+ // Relcount is the number of *host* relocations applied to this section
+ // (when external linking).
+ // Incremented atomically on multiple goroutines.
+ // Note: this may differ from number of Go relocations, as one Go relocation
+ // may turn into multiple host relocations.
+ Relcount uint32
+ Sym LoaderSym // symbol for the section, if any
+ Index uint16 // each section has a unique index, used internally
+
+ Compressed bool
+}
diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go
new file mode 100644
index 0000000..2f2c839
--- /dev/null
+++ b/src/cmd/link/internal/sym/symbol.go
@@ -0,0 +1,42 @@
+// Copyright 2017 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.
+
+package sym
+
+import (
+ "cmd/internal/obj"
+ "internal/buildcfg"
+)
+
+const (
+ SymVerABI0 = 0
+ SymVerABIInternal = 1
+ SymVerABICount = 2 // Number of internal ABIs
+ SymVerStatic = 10 // Minimum version used by static (file-local) syms
+)
+
+func ABIToVersion(abi obj.ABI) int {
+ switch abi {
+ case obj.ABI0:
+ return SymVerABI0
+ case obj.ABIInternal:
+ if !buildcfg.Experiment.RegabiWrappers {
+ // If wrappers are not enabled, ABI0 and ABIInternal are actually same
+ // so we normalize everything to ABI0.
+ return SymVerABI0
+ }
+ return SymVerABIInternal
+ }
+ return -1
+}
+
+func VersionToABI(v int) (obj.ABI, bool) {
+ switch v {
+ case SymVerABI0:
+ return obj.ABI0, true
+ case SymVerABIInternal:
+ return obj.ABIInternal, true
+ }
+ return ^obj.ABI(0), false
+}
diff --git a/src/cmd/link/internal/sym/symkind.go b/src/cmd/link/internal/sym/symkind.go
new file mode 100644
index 0000000..2f8e8fe
--- /dev/null
+++ b/src/cmd/link/internal/sym/symkind.go
@@ -0,0 +1,181 @@
+// Derived from Inferno utils/6l/l.h and related files.
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package sym
+
+import "cmd/internal/objabi"
+
+// A SymKind describes the kind of memory represented by a symbol.
+type SymKind uint8
+
+// Defined SymKind values.
+//
+// TODO(rsc): Give idiomatic Go names.
+//
+//go:generate stringer -type=SymKind
+const (
+ Sxxx SymKind = iota
+ STEXT
+ SELFRXSECT
+ SMACHOPLT
+
+ // Read-only sections.
+ STYPE
+ SSTRING
+ SGOSTRING
+ SGOFUNC
+ SGCBITS
+ SRODATA
+ SFUNCTAB
+
+ SELFROSECT
+
+ // Read-only sections with relocations.
+ //
+ // Types STYPE-SFUNCTAB above are written to the .rodata section by default.
+ // When linking a shared object, some conceptually "read only" types need to
+ // be written to by relocations and putting them in a section called
+ // ".rodata" interacts poorly with the system linkers. The GNU linkers
+ // support this situation by arranging for sections of the name
+ // ".data.rel.ro.XXX" to be mprotected read only by the dynamic linker after
+ // relocations have applied, so when the Go linker is creating a shared
+ // object it checks all objects of the above types and bumps any object that
+ // has a relocation to it to the corresponding type below, which are then
+ // written to sections with appropriate magic names.
+ STYPERELRO
+ SSTRINGRELRO
+ SGOSTRINGRELRO
+ SGOFUNCRELRO
+ SGCBITSRELRO
+ SRODATARELRO
+ SFUNCTABRELRO
+
+ // Part of .data.rel.ro if it exists, otherwise part of .rodata.
+ STYPELINK
+ SITABLINK
+ SSYMTAB
+ SPCLNTAB
+
+ // Writable sections.
+ SFirstWritable
+ SBUILDINFO
+ SELFSECT
+ SMACHO
+ SMACHOGOT
+ SWINDOWS
+ SELFGOT
+ SNOPTRDATA
+ SINITARR
+ SDATA
+ SXCOFFTOC
+ SBSS
+ SNOPTRBSS
+ SLIBFUZZER_8BIT_COUNTER
+ SCOVERAGE_COUNTER
+ SCOVERAGE_AUXVAR
+ STLSBSS
+ SXREF
+ SMACHOSYMSTR
+ SMACHOSYMTAB
+ SMACHOINDIRECTPLT
+ SMACHOINDIRECTGOT
+ SFILEPATH
+ SDYNIMPORT
+ SHOSTOBJ
+ SUNDEFEXT // Undefined symbol for resolution by external linker
+
+ // Sections for debugging information
+ SDWARFSECT
+ // DWARF symbol types
+ SDWARFCUINFO
+ SDWARFCONST
+ SDWARFFCN
+ SDWARFABSFCN
+ SDWARFTYPE
+ SDWARFVAR
+ SDWARFRANGE
+ SDWARFLOC
+ SDWARFLINES
+)
+
+// AbiSymKindToSymKind maps values read from object files (which are
+// of type cmd/internal/objabi.SymKind) to values of type SymKind.
+var AbiSymKindToSymKind = [...]SymKind{
+ objabi.Sxxx: Sxxx,
+ objabi.STEXT: STEXT,
+ objabi.SRODATA: SRODATA,
+ objabi.SNOPTRDATA: SNOPTRDATA,
+ objabi.SDATA: SDATA,
+ objabi.SBSS: SBSS,
+ objabi.SNOPTRBSS: SNOPTRBSS,
+ objabi.STLSBSS: STLSBSS,
+ objabi.SDWARFCUINFO: SDWARFCUINFO,
+ objabi.SDWARFCONST: SDWARFCONST,
+ objabi.SDWARFFCN: SDWARFFCN,
+ objabi.SDWARFABSFCN: SDWARFABSFCN,
+ objabi.SDWARFTYPE: SDWARFTYPE,
+ objabi.SDWARFVAR: SDWARFVAR,
+ objabi.SDWARFRANGE: SDWARFRANGE,
+ objabi.SDWARFLOC: SDWARFLOC,
+ objabi.SDWARFLINES: SDWARFLINES,
+ objabi.SLIBFUZZER_8BIT_COUNTER: SLIBFUZZER_8BIT_COUNTER,
+ objabi.SCOVERAGE_COUNTER: SCOVERAGE_COUNTER,
+ objabi.SCOVERAGE_AUXVAR: SCOVERAGE_AUXVAR,
+}
+
+// ReadOnly are the symbol kinds that form read-only sections. In some
+// cases, if they will require relocations, they are transformed into
+// rel-ro sections using relROMap.
+var ReadOnly = []SymKind{
+ STYPE,
+ SSTRING,
+ SGOSTRING,
+ SGOFUNC,
+ SGCBITS,
+ SRODATA,
+ SFUNCTAB,
+}
+
+// RelROMap describes the transformation of read-only symbols to rel-ro
+// symbols.
+var RelROMap = map[SymKind]SymKind{
+ STYPE: STYPERELRO,
+ SSTRING: SSTRINGRELRO,
+ SGOSTRING: SGOSTRINGRELRO,
+ SGOFUNC: SGOFUNCRELRO,
+ SGCBITS: SGCBITSRELRO,
+ SRODATA: SRODATARELRO,
+ SFUNCTAB: SFUNCTABRELRO,
+}
+
+// IsData returns true if the type is a data type.
+func (t SymKind) IsData() bool {
+ return t == SDATA || t == SNOPTRDATA || t == SBSS || t == SNOPTRBSS
+}
diff --git a/src/cmd/link/internal/sym/symkind_string.go b/src/cmd/link/internal/sym/symkind_string.go
new file mode 100644
index 0000000..1cd7ab1
--- /dev/null
+++ b/src/cmd/link/internal/sym/symkind_string.go
@@ -0,0 +1,81 @@
+// Code generated by "stringer -type=SymKind symkind.go"; DO NOT EDIT.
+
+package sym
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[Sxxx-0]
+ _ = x[STEXT-1]
+ _ = x[SELFRXSECT-2]
+ _ = x[SMACHOPLT-3]
+ _ = x[STYPE-4]
+ _ = x[SSTRING-5]
+ _ = x[SGOSTRING-6]
+ _ = x[SGOFUNC-7]
+ _ = x[SGCBITS-8]
+ _ = x[SRODATA-9]
+ _ = x[SFUNCTAB-10]
+ _ = x[SELFROSECT-11]
+ _ = x[STYPERELRO-12]
+ _ = x[SSTRINGRELRO-13]
+ _ = x[SGOSTRINGRELRO-14]
+ _ = x[SGOFUNCRELRO-15]
+ _ = x[SGCBITSRELRO-16]
+ _ = x[SRODATARELRO-17]
+ _ = x[SFUNCTABRELRO-18]
+ _ = x[STYPELINK-19]
+ _ = x[SITABLINK-20]
+ _ = x[SSYMTAB-21]
+ _ = x[SPCLNTAB-22]
+ _ = x[SFirstWritable-23]
+ _ = x[SBUILDINFO-24]
+ _ = x[SELFSECT-25]
+ _ = x[SMACHO-26]
+ _ = x[SMACHOGOT-27]
+ _ = x[SWINDOWS-28]
+ _ = x[SELFGOT-29]
+ _ = x[SNOPTRDATA-30]
+ _ = x[SINITARR-31]
+ _ = x[SDATA-32]
+ _ = x[SXCOFFTOC-33]
+ _ = x[SBSS-34]
+ _ = x[SNOPTRBSS-35]
+ _ = x[SLIBFUZZER_8BIT_COUNTER-36]
+ _ = x[SCOVERAGE_COUNTER-37]
+ _ = x[SCOVERAGE_AUXVAR-38]
+ _ = x[STLSBSS-39]
+ _ = x[SXREF-40]
+ _ = x[SMACHOSYMSTR-41]
+ _ = x[SMACHOSYMTAB-42]
+ _ = x[SMACHOINDIRECTPLT-43]
+ _ = x[SMACHOINDIRECTGOT-44]
+ _ = x[SFILEPATH-45]
+ _ = x[SDYNIMPORT-46]
+ _ = x[SHOSTOBJ-47]
+ _ = x[SUNDEFEXT-48]
+ _ = x[SDWARFSECT-49]
+ _ = x[SDWARFCUINFO-50]
+ _ = x[SDWARFCONST-51]
+ _ = x[SDWARFFCN-52]
+ _ = x[SDWARFABSFCN-53]
+ _ = x[SDWARFTYPE-54]
+ _ = x[SDWARFVAR-55]
+ _ = x[SDWARFRANGE-56]
+ _ = x[SDWARFLOC-57]
+ _ = x[SDWARFLINES-58]
+}
+
+const _SymKind_name = "SxxxSTEXTSELFRXSECTSMACHOPLTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVARSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINES"
+
+var _SymKind_index = [...]uint16{0, 4, 9, 19, 28, 33, 40, 49, 56, 63, 70, 78, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 336, 353, 369, 376, 381, 393, 405, 422, 439, 448, 458, 466, 475, 485, 497, 508, 517, 529, 539, 548, 559, 568, 579}
+
+func (i SymKind) String() string {
+ if i >= SymKind(len(_SymKind_index)-1) {
+ return "SymKind(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _SymKind_name[_SymKind_index[i]:_SymKind_index[i+1]]
+}
diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go
new file mode 100644
index 0000000..99018c8
--- /dev/null
+++ b/src/cmd/link/internal/wasm/asm.go
@@ -0,0 +1,604 @@
+// 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.
+
+package wasm
+
+import (
+ "bytes"
+ "cmd/internal/objabi"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "internal/buildcfg"
+ "io"
+ "regexp"
+)
+
+const (
+ I32 = 0x7F
+ I64 = 0x7E
+ F32 = 0x7D
+ F64 = 0x7C
+)
+
+const (
+ sectionCustom = 0
+ sectionType = 1
+ sectionImport = 2
+ sectionFunction = 3
+ sectionTable = 4
+ sectionMemory = 5
+ sectionGlobal = 6
+ sectionExport = 7
+ sectionStart = 8
+ sectionElement = 9
+ sectionCode = 10
+ sectionData = 11
+)
+
+// funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly
+const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+}
+
+type wasmFunc struct {
+ Name string
+ Type uint32
+ Code []byte
+}
+
+type wasmFuncType struct {
+ Params []byte
+ Results []byte
+}
+
+var wasmFuncTypes = map[string]*wasmFuncType{
+ "_rt0_wasm_js": {Params: []byte{}}, //
+ "wasm_export_run": {Params: []byte{I32, I32}}, // argc, argv
+ "wasm_export_resume": {Params: []byte{}}, //
+ "wasm_export_getsp": {Results: []byte{I32}}, // sp
+ "wasm_pc_f_loop": {Params: []byte{}}, //
+ "runtime.wasmDiv": {Params: []byte{I64, I64}, Results: []byte{I64}}, // x, y -> x/y
+ "runtime.wasmTruncS": {Params: []byte{F64}, Results: []byte{I64}}, // x -> int(x)
+ "runtime.wasmTruncU": {Params: []byte{F64}, Results: []byte{I64}}, // x -> uint(x)
+ "runtime.gcWriteBarrier": {Params: []byte{I64, I64}}, // ptr, val
+ "cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1
+ "memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1
+ "memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0
+ "memchr": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // s, c, len -> index
+}
+
+func assignAddress(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) {
+ // WebAssembly functions do not live in the same address space as the linear memory.
+ // Instead, WebAssembly automatically assigns indices. Imported functions (section "import")
+ // have indices 0 to n. They are followed by native functions (sections "function" and "code")
+ // with indices n+1 and following.
+ //
+ // The following rules describe how wasm handles function indices and addresses:
+ // PC_F = funcValueOffset + WebAssembly function index (not including the imports)
+ // s.Value = PC = PC_F<<16 + PC_B
+ //
+ // The funcValueOffset is necessary to avoid conflicts with expectations
+ // that the Go runtime has about function addresses.
+ // The field "s.Value" corresponds to the concept of PC at runtime.
+ // However, there is no PC register, only PC_F and PC_B. PC_F denotes the function,
+ // PC_B the resume point inside of that function. The entry of the function has PC_B = 0.
+ ldr.SetSymSect(s, sect)
+ ldr.SetSymValue(s, int64(funcValueOffset+va/ld.MINFUNC)<<16) // va starts at zero
+ va += uint64(ld.MINFUNC)
+ return sect, n, va
+}
+
+type wasmDataSect struct {
+ sect *sym.Section
+ data []byte
+}
+
+var dataSects []wasmDataSect
+
+func asmb(ctxt *ld.Link, ldr *loader.Loader) {
+ sections := []*sym.Section{
+ ldr.SymSect(ldr.Lookup("runtime.rodata", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.typelink", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.itablink", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.symtab", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.pclntab", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.noptrdata", 0)),
+ ldr.SymSect(ldr.Lookup("runtime.data", 0)),
+ }
+
+ dataSects = make([]wasmDataSect, len(sections))
+ for i, sect := range sections {
+ data := ld.DatblkBytes(ctxt, int64(sect.Vaddr), int64(sect.Length))
+ dataSects[i] = wasmDataSect{sect, data}
+ }
+}
+
+// asmb writes the final WebAssembly module binary.
+// Spec: https://webassembly.github.io/spec/core/binary/modules.html
+func asmb2(ctxt *ld.Link, ldr *loader.Loader) {
+ types := []*wasmFuncType{
+ // For normal Go functions, the single parameter is PC_B,
+ // the return value is
+ // 0 if the function returned normally or
+ // 1 if the stack needs to be unwound.
+ {Params: []byte{I32}, Results: []byte{I32}},
+ }
+
+ // collect host imports (functions that get imported from the WebAssembly host, usually JavaScript)
+ hostImports := []*wasmFunc{
+ {
+ Name: "debug",
+ Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
+ },
+ }
+ hostImportMap := make(map[loader.Sym]int64)
+ for _, fn := range ctxt.Textp {
+ relocs := ldr.Relocs(fn)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Type() == objabi.R_WASMIMPORT {
+ hostImportMap[r.Sym()] = int64(len(hostImports))
+ hostImports = append(hostImports, &wasmFunc{
+ Name: ldr.SymName(r.Sym()),
+ Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
+ })
+ }
+ }
+ }
+
+ // collect functions with WebAssembly body
+ var buildid []byte
+ fns := make([]*wasmFunc, len(ctxt.Textp))
+ for i, fn := range ctxt.Textp {
+ wfn := new(bytes.Buffer)
+ if ldr.SymName(fn) == "go:buildid" {
+ writeUleb128(wfn, 0) // number of sets of locals
+ writeI32Const(wfn, 0)
+ wfn.WriteByte(0x0b) // end
+ buildid = ldr.Data(fn)
+ } else {
+ // Relocations have variable length, handle them here.
+ relocs := ldr.Relocs(fn)
+ P := ldr.Data(fn)
+ off := int32(0)
+ for ri := 0; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if r.Siz() == 0 {
+ continue // skip marker relocations
+ }
+ wfn.Write(P[off:r.Off()])
+ off = r.Off()
+ rs := r.Sym()
+ switch r.Type() {
+ case objabi.R_ADDR:
+ writeSleb128(wfn, ldr.SymValue(rs)+r.Add())
+ case objabi.R_CALL:
+ writeSleb128(wfn, int64(len(hostImports))+ldr.SymValue(rs)>>16-funcValueOffset)
+ case objabi.R_WASMIMPORT:
+ writeSleb128(wfn, hostImportMap[rs])
+ default:
+ ldr.Errorf(fn, "bad reloc type %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
+ continue
+ }
+ }
+ wfn.Write(P[off:])
+ }
+
+ typ := uint32(0)
+ if sig, ok := wasmFuncTypes[ldr.SymName(fn)]; ok {
+ typ = lookupType(sig, &types)
+ }
+
+ name := nameRegexp.ReplaceAllString(ldr.SymName(fn), "_")
+ fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()}
+ }
+
+ ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic
+ ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version
+
+ // Add any buildid early in the binary:
+ if len(buildid) != 0 {
+ writeBuildID(ctxt, buildid)
+ }
+
+ writeTypeSec(ctxt, types)
+ writeImportSec(ctxt, hostImports)
+ writeFunctionSec(ctxt, fns)
+ writeTableSec(ctxt, fns)
+ writeMemorySec(ctxt, ldr)
+ writeGlobalSec(ctxt)
+ writeExportSec(ctxt, ldr, len(hostImports))
+ writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns)))
+ writeCodeSec(ctxt, fns)
+ writeDataSec(ctxt)
+ writeProducerSec(ctxt)
+ if !*ld.FlagS {
+ writeNameSec(ctxt, len(hostImports), fns)
+ }
+}
+
+func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 {
+ for i, t := range *types {
+ if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) {
+ return uint32(i)
+ }
+ }
+ *types = append(*types, sig)
+ return uint32(len(*types) - 1)
+}
+
+func writeSecHeader(ctxt *ld.Link, id uint8) int64 {
+ ctxt.Out.WriteByte(id)
+ sizeOffset := ctxt.Out.Offset()
+ ctxt.Out.Write(make([]byte, 5)) // placeholder for length
+ return sizeOffset
+}
+
+func writeSecSize(ctxt *ld.Link, sizeOffset int64) {
+ endOffset := ctxt.Out.Offset()
+ ctxt.Out.SeekSet(sizeOffset)
+ writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5)
+ ctxt.Out.SeekSet(endOffset)
+}
+
+func writeBuildID(ctxt *ld.Link, buildid []byte) {
+ sizeOffset := writeSecHeader(ctxt, sectionCustom)
+ writeName(ctxt.Out, "go:buildid")
+ ctxt.Out.Write(buildid)
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeTypeSec writes the section that declares all function types
+// so they can be referenced by index.
+func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) {
+ sizeOffset := writeSecHeader(ctxt, sectionType)
+
+ writeUleb128(ctxt.Out, uint64(len(types)))
+
+ for _, t := range types {
+ ctxt.Out.WriteByte(0x60) // functype
+ writeUleb128(ctxt.Out, uint64(len(t.Params)))
+ for _, v := range t.Params {
+ ctxt.Out.WriteByte(byte(v))
+ }
+ writeUleb128(ctxt.Out, uint64(len(t.Results)))
+ for _, v := range t.Results {
+ ctxt.Out.WriteByte(byte(v))
+ }
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeImportSec writes the section that lists the functions that get
+// imported from the WebAssembly host, usually JavaScript.
+func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionImport)
+
+ writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports
+ for _, fn := range hostImports {
+ writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js
+ writeName(ctxt.Out, fn.Name)
+ ctxt.Out.WriteByte(0x00) // func import
+ writeUleb128(ctxt.Out, uint64(fn.Type))
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeFunctionSec writes the section that declares the types of functions.
+// The bodies of these functions will later be provided in the "code" section.
+func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionFunction)
+
+ writeUleb128(ctxt.Out, uint64(len(fns)))
+ for _, fn := range fns {
+ writeUleb128(ctxt.Out, uint64(fn.Type))
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeTableSec writes the section that declares tables. Currently there is only a single table
+// that is used by the CallIndirect operation to dynamically call any function.
+// The contents of the table get initialized by the "element" section.
+func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionTable)
+
+ numElements := uint64(funcValueOffset + len(fns))
+ writeUleb128(ctxt.Out, 1) // number of tables
+ ctxt.Out.WriteByte(0x70) // type: anyfunc
+ ctxt.Out.WriteByte(0x00) // no max
+ writeUleb128(ctxt.Out, numElements) // min
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used.
+// Linear memory always starts at address zero. More memory can be requested with the GrowMemory instruction.
+func writeMemorySec(ctxt *ld.Link, ldr *loader.Loader) {
+ sizeOffset := writeSecHeader(ctxt, sectionMemory)
+
+ dataSection := ldr.SymSect(ldr.Lookup("runtime.data", 0))
+ dataEnd := dataSection.Vaddr + dataSection.Length
+ var initialSize = dataEnd + 16<<20 // 16MB, enough for runtime init without growing
+
+ const wasmPageSize = 64 << 10 // 64KB
+
+ writeUleb128(ctxt.Out, 1) // number of memories
+ ctxt.Out.WriteByte(0x00) // no maximum memory size
+ writeUleb128(ctxt.Out, initialSize/wasmPageSize) // minimum (initial) memory size
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeGlobalSec writes the section that declares global variables.
+func writeGlobalSec(ctxt *ld.Link) {
+ sizeOffset := writeSecHeader(ctxt, sectionGlobal)
+
+ globalRegs := []byte{
+ I32, // 0: SP
+ I64, // 1: CTXT
+ I64, // 2: g
+ I64, // 3: RET0
+ I64, // 4: RET1
+ I64, // 5: RET2
+ I64, // 6: RET3
+ I32, // 7: PAUSE
+ }
+
+ writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals
+
+ for _, typ := range globalRegs {
+ ctxt.Out.WriteByte(typ)
+ ctxt.Out.WriteByte(0x01) // var
+ switch typ {
+ case I32:
+ writeI32Const(ctxt.Out, 0)
+ case I64:
+ writeI64Const(ctxt.Out, 0)
+ }
+ ctxt.Out.WriteByte(0x0b) // end
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeExportSec writes the section that declares exports.
+// Exports can be accessed by the WebAssembly host, usually JavaScript.
+// The wasm_export_* functions and the linear memory get exported.
+func writeExportSec(ctxt *ld.Link, ldr *loader.Loader, lenHostImports int) {
+ sizeOffset := writeSecHeader(ctxt, sectionExport)
+
+ writeUleb128(ctxt.Out, 4) // number of exports
+
+ for _, name := range []string{"run", "resume", "getsp"} {
+ s := ldr.Lookup("wasm_export_"+name, 0)
+ idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
+ writeName(ctxt.Out, name) // inst.exports.run/resume/getsp in wasm_exec.js
+ ctxt.Out.WriteByte(0x00) // func export
+ writeUleb128(ctxt.Out, uint64(idx)) // funcidx
+ }
+
+ writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js
+ ctxt.Out.WriteByte(0x02) // mem export
+ writeUleb128(ctxt.Out, 0) // memidx
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeElementSec writes the section that initializes the tables declared by the "table" section.
+// The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value)
+// maps linearly to the function index (numImports + PC_F).
+func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
+ sizeOffset := writeSecHeader(ctxt, sectionElement)
+
+ writeUleb128(ctxt.Out, 1) // number of element segments
+
+ writeUleb128(ctxt.Out, 0) // tableidx
+ writeI32Const(ctxt.Out, funcValueOffset)
+ ctxt.Out.WriteByte(0x0b) // end
+
+ writeUleb128(ctxt.Out, numFns) // number of entries
+ for i := uint64(0); i < numFns; i++ {
+ writeUleb128(ctxt.Out, numImports+i)
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeCodeSec writes the section that provides the function bodies for the functions
+// declared by the "func" section.
+func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionCode)
+
+ writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries
+ for _, fn := range fns {
+ writeUleb128(ctxt.Out, uint64(len(fn.Code)))
+ ctxt.Out.Write(fn.Code)
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeDataSec writes the section that provides data that will be used to initialize the linear memory.
+func writeDataSec(ctxt *ld.Link) {
+ sizeOffset := writeSecHeader(ctxt, sectionData)
+
+ type dataSegment struct {
+ offset int32
+ data []byte
+ }
+
+ // Omit blocks of zeroes and instead emit data segments with offsets skipping the zeroes.
+ // This reduces the size of the WebAssembly binary. We use 8 bytes as an estimate for the
+ // overhead of adding a new segment (same as wasm-opt's memory-packing optimization uses).
+ const segmentOverhead = 8
+
+ // Generate at most this many segments. A higher number of segments gets rejected by some WebAssembly runtimes.
+ const maxNumSegments = 100000
+
+ var segments []*dataSegment
+ for secIndex, ds := range dataSects {
+ data := ds.data
+ offset := int32(ds.sect.Vaddr)
+
+ // skip leading zeroes
+ for len(data) > 0 && data[0] == 0 {
+ data = data[1:]
+ offset++
+ }
+
+ for len(data) > 0 {
+ dataLen := int32(len(data))
+ var segmentEnd, zeroEnd int32
+ if len(segments)+(len(dataSects)-secIndex) == maxNumSegments {
+ segmentEnd = dataLen
+ zeroEnd = dataLen
+ } else {
+ for {
+ // look for beginning of zeroes
+ for segmentEnd < dataLen && data[segmentEnd] != 0 {
+ segmentEnd++
+ }
+ // look for end of zeroes
+ zeroEnd = segmentEnd
+ for zeroEnd < dataLen && data[zeroEnd] == 0 {
+ zeroEnd++
+ }
+ // emit segment if omitting zeroes reduces the output size
+ if zeroEnd-segmentEnd >= segmentOverhead || zeroEnd == dataLen {
+ break
+ }
+ segmentEnd = zeroEnd
+ }
+ }
+
+ segments = append(segments, &dataSegment{
+ offset: offset,
+ data: data[:segmentEnd],
+ })
+ data = data[zeroEnd:]
+ offset += zeroEnd
+ }
+ }
+
+ writeUleb128(ctxt.Out, uint64(len(segments))) // number of data entries
+ for _, seg := range segments {
+ writeUleb128(ctxt.Out, 0) // memidx
+ writeI32Const(ctxt.Out, seg.offset)
+ ctxt.Out.WriteByte(0x0b) // end
+ writeUleb128(ctxt.Out, uint64(len(seg.data)))
+ ctxt.Out.Write(seg.data)
+ }
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+// writeProducerSec writes an optional section that reports the source language and compiler version.
+func writeProducerSec(ctxt *ld.Link) {
+ sizeOffset := writeSecHeader(ctxt, sectionCustom)
+ writeName(ctxt.Out, "producers")
+
+ writeUleb128(ctxt.Out, 2) // number of fields
+
+ writeName(ctxt.Out, "language") // field name
+ writeUleb128(ctxt.Out, 1) // number of values
+ writeName(ctxt.Out, "Go") // value: name
+ writeName(ctxt.Out, buildcfg.Version) // value: version
+
+ writeName(ctxt.Out, "processed-by") // field name
+ writeUleb128(ctxt.Out, 1) // number of values
+ writeName(ctxt.Out, "Go cmd/compile") // value: name
+ writeName(ctxt.Out, buildcfg.Version) // value: version
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+var nameRegexp = regexp.MustCompile(`[^\w.]`)
+
+// writeNameSec writes an optional section that assigns names to the functions declared by the "func" section.
+// The names are only used by WebAssembly stack traces, debuggers and decompilers.
+// TODO(neelance): add symbol table of DATA symbols
+func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) {
+ sizeOffset := writeSecHeader(ctxt, sectionCustom)
+ writeName(ctxt.Out, "name")
+
+ sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names
+ writeUleb128(ctxt.Out, uint64(len(fns)))
+ for i, fn := range fns {
+ writeUleb128(ctxt.Out, uint64(firstFnIndex+i))
+ writeName(ctxt.Out, fn.Name)
+ }
+ writeSecSize(ctxt, sizeOffset2)
+
+ writeSecSize(ctxt, sizeOffset)
+}
+
+type nameWriter interface {
+ io.ByteWriter
+ io.Writer
+}
+
+func writeI32Const(w io.ByteWriter, v int32) {
+ w.WriteByte(0x41) // i32.const
+ writeSleb128(w, int64(v))
+}
+
+func writeI64Const(w io.ByteWriter, v int64) {
+ w.WriteByte(0x42) // i64.const
+ writeSleb128(w, v)
+}
+
+func writeName(w nameWriter, name string) {
+ writeUleb128(w, uint64(len(name)))
+ w.Write([]byte(name))
+}
+
+func writeUleb128(w io.ByteWriter, v uint64) {
+ if v < 128 {
+ w.WriteByte(uint8(v))
+ return
+ }
+ more := true
+ for more {
+ c := uint8(v & 0x7f)
+ v >>= 7
+ more = v != 0
+ if more {
+ c |= 0x80
+ }
+ w.WriteByte(c)
+ }
+}
+
+func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) {
+ for i := 0; i < length; i++ {
+ c := uint8(v & 0x7f)
+ v >>= 7
+ if i < length-1 {
+ c |= 0x80
+ }
+ w.WriteByte(c)
+ }
+ if v != 0 {
+ panic("writeUleb128FixedLength: length too small")
+ }
+}
+
+func writeSleb128(w io.ByteWriter, v int64) {
+ more := true
+ for more {
+ c := uint8(v & 0x7f)
+ s := uint8(v & 0x40)
+ v >>= 7
+ more = !((v == 0 && s == 0) || (v == -1 && s != 0))
+ if more {
+ c |= 0x80
+ }
+ w.WriteByte(c)
+ }
+}
diff --git a/src/cmd/link/internal/wasm/obj.go b/src/cmd/link/internal/wasm/obj.go
new file mode 100644
index 0000000..f8090a3
--- /dev/null
+++ b/src/cmd/link/internal/wasm/obj.go
@@ -0,0 +1,35 @@
+// 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.
+
+package wasm
+
+import (
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ theArch := ld.Arch{
+ Funcalign: 16,
+ Maxalign: 32,
+ Minalign: 1,
+
+ Archinit: archinit,
+ AssignAddress: assignAddress,
+ Asmb: asmb,
+ Asmb2: asmb2,
+ Gentext: gentext,
+ }
+
+ return sys.ArchWasm, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0
+ }
+}
diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go
new file mode 100644
index 0000000..3a33201
--- /dev/null
+++ b/src/cmd/link/internal/x86/asm.go
@@ -0,0 +1,490 @@
+// Inferno utils/8l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package x86
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "log"
+)
+
+func gentext(ctxt *ld.Link, ldr *loader.Loader) {
+ if ctxt.DynlinkingGo() {
+ // We need get_pc_thunk.
+ } else {
+ switch ctxt.BuildMode {
+ case ld.BuildModeCArchive:
+ if !ctxt.IsELF {
+ return
+ }
+ case ld.BuildModePIE, ld.BuildModeCShared, ld.BuildModePlugin:
+ // We need get_pc_thunk.
+ default:
+ return
+ }
+ }
+
+ // Generate little thunks that load the PC of the next instruction into a register.
+ thunks := make([]loader.Sym, 0, 7+len(ctxt.Textp))
+ for _, r := range [...]struct {
+ name string
+ num uint8
+ }{
+ {"ax", 0},
+ {"cx", 1},
+ {"dx", 2},
+ {"bx", 3},
+ // sp
+ {"bp", 5},
+ {"si", 6},
+ {"di", 7},
+ } {
+ thunkfunc := ldr.CreateSymForUpdate("__x86.get_pc_thunk."+r.name, 0)
+ thunkfunc.SetType(sym.STEXT)
+ ldr.SetAttrLocal(thunkfunc.Sym(), true)
+ o := func(op ...uint8) {
+ for _, op1 := range op {
+ thunkfunc.AddUint8(op1)
+ }
+ }
+ // 8b 04 24 mov (%esp),%eax
+ // Destination register is in bits 3-5 of the middle byte, so add that in.
+ o(0x8b, 0x04+r.num<<3, 0x24)
+ // c3 ret
+ o(0xc3)
+
+ thunks = append(thunks, thunkfunc.Sym())
+ }
+ ctxt.Textp = append(thunks, ctxt.Textp...) // keep Textp in dependency order
+
+ initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
+ if initfunc == nil {
+ return
+ }
+
+ o := func(op ...uint8) {
+ for _, op1 := range op {
+ initfunc.AddUint8(op1)
+ }
+ }
+
+ // go.link.addmoduledata:
+ // 53 push %ebx
+ // e8 00 00 00 00 call __x86.get_pc_thunk.cx + R_CALL __x86.get_pc_thunk.cx
+ // 8d 81 00 00 00 00 lea 0x0(%ecx), %eax + R_PCREL ctxt.Moduledata
+ // 8d 99 00 00 00 00 lea 0x0(%ecx), %ebx + R_GOTPC _GLOBAL_OFFSET_TABLE_
+ // e8 00 00 00 00 call runtime.addmoduledata@plt + R_CALL runtime.addmoduledata
+ // 5b pop %ebx
+ // c3 ret
+
+ o(0x53)
+
+ o(0xe8)
+ initfunc.AddSymRef(ctxt.Arch, ldr.Lookup("__x86.get_pc_thunk.cx", 0), 0, objabi.R_CALL, 4)
+
+ o(0x8d, 0x81)
+ initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 6)
+
+ o(0x8d, 0x99)
+ gotsym := ldr.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0)
+ initfunc.AddSymRef(ctxt.Arch, gotsym, 12, objabi.R_PCREL, 4)
+ o(0xe8)
+ initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_CALL, 4)
+
+ o(0x5b)
+
+ o(0xc3)
+}
+
+func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
+ targ := r.Sym()
+ var targType sym.SymKind
+ if targ != 0 {
+ targType = ldr.SymType(targ)
+ }
+
+ switch r.Type() {
+ default:
+ if r.Type() >= objabi.ElfRelocOffset {
+ ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
+ return false
+ }
+
+ // Handle relocations found in ELF object files.
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PC32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ if targType == 0 || targType == sym.SXREF {
+ ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PLT32):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
+ }
+
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32),
+ objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32X):
+ su := ldr.MakeSymbolUpdater(s)
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ sData := ldr.Data(s)
+
+ if r.Off() >= 2 && sData[r.Off()-2] == 0x8b {
+ su.MakeWritable()
+
+ // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT.
+ writeableData := su.Data()
+ writeableData[r.Off()-2] = 0x8d
+ su.SetRelocType(rIdx, objabi.R_GOTOFF)
+ return true
+ }
+
+ if r.Off() >= 2 && sData[r.Off()-2] == 0xff && sData[r.Off()-1] == 0xb3 {
+ su.MakeWritable()
+ // turn PUSHL of GOT entry into PUSHL of symbol itself.
+ // use unnecessary SS prefix to keep instruction same length.
+ writeableData := su.Data()
+ writeableData[r.Off()-2] = 0x36
+ writeableData[r.Off()-1] = 0x68
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ return true
+ }
+
+ ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT))
+ su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
+ su.SetRelocSym(rIdx, 0)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTOFF):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_GOTOFF)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC):
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+4)
+ return true
+
+ case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_32):
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected R_386_32 relocation for dynamic symbol %s", ldr.SymName(targ))
+ }
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0:
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_ADDR)
+ if targType == sym.SDYNIMPORT {
+ ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ))
+ }
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1:
+ su := ldr.MakeSymbolUpdater(s)
+ if targType == sym.SDYNIMPORT {
+ addpltsym(target, ldr, syms, targ)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+ }
+
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+
+ case objabi.MachoRelocOffset + ld.MACHO_FAKE_GOTPCREL:
+ su := ldr.MakeSymbolUpdater(s)
+ if targType != sym.SDYNIMPORT {
+ // have symbol
+ // turn MOVL of GOT entry into LEAL of symbol itself
+ sData := ldr.Data(s)
+ if r.Off() < 2 || sData[r.Off()-2] != 0x8b {
+ ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
+ return false
+ }
+
+ su.MakeWritable()
+ writeableData := su.Data()
+ writeableData[r.Off()-2] = 0x8d
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+ }
+
+ ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT))
+ su.SetRelocSym(rIdx, syms.GOT)
+ su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
+ su.SetRelocType(rIdx, objabi.R_PCREL)
+ return true
+ }
+
+ // Handle references to ELF symbols from our own object files.
+ if targType != sym.SDYNIMPORT {
+ return true
+ }
+
+ // Reread the reloc to incorporate any changes in type above.
+ relocs := ldr.Relocs(s)
+ r = relocs.At(rIdx)
+
+ switch r.Type() {
+ case objabi.R_CALL,
+ objabi.R_PCREL:
+ if target.IsExternal() {
+ // External linker will do this relocation.
+ return true
+ }
+ addpltsym(target, ldr, syms, targ)
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocSym(rIdx, syms.PLT)
+ su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+ return true
+
+ case objabi.R_ADDR:
+ if ldr.SymType(s) != sym.SDATA {
+ break
+ }
+ if target.IsElf() {
+ ld.Adddynsym(ldr, target, syms, targ)
+ rel := ldr.MakeSymbolUpdater(syms.Rel)
+ rel.AddAddrPlus(target.Arch, s, int64(r.Off()))
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(targ)), uint32(elf.R_386_32)))
+ su := ldr.MakeSymbolUpdater(s)
+ su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
+ su.SetRelocSym(rIdx, 0)
+ return true
+ }
+ }
+
+ return false
+}
+
+func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
+ out.Write32(uint32(sectoff))
+
+ elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+ siz := r.Size
+ switch r.Type {
+ default:
+ return false
+ case objabi.R_ADDR, objabi.R_DWARFSECREF:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_32) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_GOTPCREL:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_GOTPC))
+ if ldr.SymName(r.Xsym) != "_GLOBAL_OFFSET_TABLE_" {
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(elf.R_386_GOT32) | uint32(elfsym)<<8)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_CALL:
+ if siz == 4 {
+ if ldr.SymType(r.Xsym) == sym.SDYNIMPORT {
+ out.Write32(uint32(elf.R_386_PLT32) | uint32(elfsym)<<8)
+ } else {
+ out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8)
+ }
+ } else {
+ return false
+ }
+ case objabi.R_PCREL:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_TLS_LE:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_TLS_LE) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ case objabi.R_TLS_IE:
+ if siz == 4 {
+ out.Write32(uint32(elf.R_386_GOTPC))
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(elf.R_386_TLS_GOTIE) | uint32(elfsym)<<8)
+ } else {
+ return false
+ }
+ }
+
+ return true
+}
+
+func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
+ return false
+}
+
+func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
+ var v uint32
+
+ rs := r.Xsym
+ rt := r.Type
+
+ if ldr.SymDynid(rs) < 0 {
+ ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
+ return false
+ }
+
+ out.Write32(uint32(sectoff))
+ out.Write32(uint32(ldr.SymDynid(rs)))
+
+ switch rt {
+ default:
+ return false
+
+ case objabi.R_DWARFSECREF:
+ v = ld.IMAGE_REL_I386_SECREL
+
+ case objabi.R_ADDR:
+ v = ld.IMAGE_REL_I386_DIR32
+
+ case objabi.R_CALL,
+ objabi.R_PCREL:
+ v = ld.IMAGE_REL_I386_REL32
+ }
+
+ out.Write16(uint16(v))
+
+ return true
+}
+
+func archreloc(*ld.Target, *loader.Loader, *ld.ArchSyms, loader.Reloc, loader.Sym, int64) (int64, int, bool) {
+ return -1, 0, false
+}
+
+func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
+ log.Fatalf("unexpected relocation variant")
+ return -1
+}
+
+func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
+ if plt.Size() == 0 {
+ // pushl got+4
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x35)
+ plt.AddAddrPlus(ctxt.Arch, got.Sym(), 4)
+
+ // jmp *got+8
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x25)
+ plt.AddAddrPlus(ctxt.Arch, got.Sym(), 8)
+
+ // zero pad
+ plt.AddUint32(ctxt.Arch, 0)
+
+ // assume got->size == 0 too
+ got.AddAddrPlus(ctxt.Arch, dynamic, 0)
+
+ got.AddUint32(ctxt.Arch, 0)
+ got.AddUint32(ctxt.Arch, 0)
+ }
+}
+
+func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
+ if ldr.SymPlt(s) >= 0 {
+ return
+ }
+
+ ld.Adddynsym(ldr, target, syms, s)
+
+ if target.IsElf() {
+ plt := ldr.MakeSymbolUpdater(syms.PLT)
+ got := ldr.MakeSymbolUpdater(syms.GOTPLT)
+ rel := ldr.MakeSymbolUpdater(syms.RelPLT)
+ if plt.Size() == 0 {
+ panic("plt is not set up")
+ }
+
+ // jmpq *got+size
+ plt.AddUint8(0xff)
+
+ plt.AddUint8(0x25)
+ plt.AddAddrPlus(target.Arch, got.Sym(), got.Size())
+
+ // add to got: pointer to current pos in plt
+ got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size())
+
+ // pushl $x
+ plt.AddUint8(0x68)
+
+ plt.AddUint32(target.Arch, uint32(rel.Size()))
+
+ // jmp .plt
+ plt.AddUint8(0xe9)
+
+ plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4)))
+
+ // rel
+ rel.AddAddrPlus(target.Arch, got.Sym(), got.Size()-4)
+
+ sDynid := ldr.SymDynid(s)
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(sDynid), uint32(elf.R_386_JMP_SLOT)))
+
+ ldr.SetPlt(s, int32(plt.Size()-16))
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}
diff --git a/src/cmd/link/internal/x86/l.go b/src/cmd/link/internal/x86/l.go
new file mode 100644
index 0000000..5875d45
--- /dev/null
+++ b/src/cmd/link/internal/x86/l.go
@@ -0,0 +1,43 @@
+// Inferno utils/8l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/l.h
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package x86
+
+const (
+ maxAlign = 32 // max data alignment
+ minAlign = 1 // min data alignment
+ funcAlign = 16
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+ dwarfRegSP = 4
+ dwarfRegLR = 8
+)
diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go
new file mode 100644
index 0000000..b0a129e
--- /dev/null
+++ b/src/cmd/link/internal/x86/obj.go
@@ -0,0 +1,117 @@
+// Inferno utils/8l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/obj.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package x86
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/ld"
+)
+
+func Init() (*sys.Arch, ld.Arch) {
+ arch := sys.Arch386
+
+ theArch := ld.Arch{
+ Funcalign: funcAlign,
+ Maxalign: maxAlign,
+ Minalign: minAlign,
+ Dwarfregsp: dwarfRegSP,
+ Dwarfreglr: dwarfRegLR,
+ // 0xCC is INT $3 - breakpoint instruction
+ CodePad: []byte{0xCC},
+
+ Plan9Magic: uint32(4*11*11 + 7),
+
+ Adddynrel: adddynrel,
+ Archinit: archinit,
+ Archreloc: archreloc,
+ Archrelocvariant: archrelocvariant,
+ Elfreloc1: elfreloc1,
+ ElfrelocSize: 8,
+ Elfsetupplt: elfsetupplt,
+ Gentext: gentext,
+ Machoreloc1: machoreloc1,
+ PEreloc1: pereloc1,
+
+ Linuxdynld: "/lib/ld-linux.so.2",
+ LinuxdynldMusl: "/lib/ld-musl-i386.so.1",
+ Freebsddynld: "/usr/libexec/ld-elf.so.1",
+ Openbsddynld: "/usr/libexec/ld.so",
+ Netbsddynld: "/usr/libexec/ld.elf_so",
+ Solarisdynld: "/lib/ld.so.1",
+ }
+
+ return arch, theArch
+}
+
+func archinit(ctxt *ld.Link) {
+ switch ctxt.HeadType {
+ default:
+ ld.Exitf("unknown -H option: %v", ctxt.HeadType)
+
+ case objabi.Hplan9: /* plan 9 */
+ ld.HEADR = 32
+
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hdarwin: /* apple MACH */
+ ld.HEADR = ld.INITIAL_MACHO_HEADR
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hlinux, /* elf32 executable */
+ objabi.Hfreebsd,
+ objabi.Hnetbsd,
+ objabi.Hopenbsd:
+ ld.Elfinit(ctxt)
+
+ ld.HEADR = ld.ELFRESERVE
+ if *ld.FlagTextAddr == -1 {
+ *ld.FlagTextAddr = 0x08048000 + int64(ld.HEADR)
+ }
+ if *ld.FlagRound == -1 {
+ *ld.FlagRound = 4096
+ }
+
+ case objabi.Hwindows: /* PE executable */
+ // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit
+ return
+ }
+}