aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link
diff options
context:
space:
mode:
authorKatie Hockman <katie@golang.org>2020-12-14 10:03:05 -0500
committerKatie Hockman <katie@golang.org>2020-12-14 10:06:13 -0500
commit0345ede87ee12698988973884cfc0fd3d499dffd (patch)
tree7123cff141ee5661208d2f5f437b8f5252ac7f6a /src/cmd/link
parent4651d6b267818b0e0d128a5443289717c4bb8cbc (diff)
parent0a02371b0576964e81c3b40d328db9a3ef3b031b (diff)
downloadgo-0345ede87ee12698988973884cfc0fd3d499dffd.tar.xz
[dev.fuzz] all: merge master into dev.fuzz
Change-Id: I5d8c8329ccc9d747bd81ade6b1cb7cb8ae2e94b2
Diffstat (limited to 'src/cmd/link')
-rw-r--r--src/cmd/link/dwarf_test.go10
-rw-r--r--src/cmd/link/internal/amd64/asm.go75
-rw-r--r--src/cmd/link/internal/amd64/l.go2
-rw-r--r--src/cmd/link/internal/amd64/obj.go7
-rw-r--r--src/cmd/link/internal/arm/asm.go4
-rw-r--r--src/cmd/link/internal/arm64/asm.go188
-rw-r--r--src/cmd/link/internal/arm64/obj.go2
-rw-r--r--src/cmd/link/internal/ld/ar.go2
-rw-r--r--src/cmd/link/internal/ld/config.go26
-rw-r--r--src/cmd/link/internal/ld/data.go49
-rw-r--r--src/cmd/link/internal/ld/deadcode.go96
-rw-r--r--src/cmd/link/internal/ld/deadcode_test.go2
-rw-r--r--src/cmd/link/internal/ld/dwarf.go111
-rw-r--r--src/cmd/link/internal/ld/dwarf_test.go17
-rw-r--r--src/cmd/link/internal/ld/elf.go1144
-rw-r--r--src/cmd/link/internal/ld/elf_test.go3
-rw-r--r--src/cmd/link/internal/ld/go.go63
-rw-r--r--src/cmd/link/internal/ld/go_test.go121
-rw-r--r--src/cmd/link/internal/ld/ld_test.go77
-rw-r--r--src/cmd/link/internal/ld/lib.go80
-rw-r--r--src/cmd/link/internal/ld/link.go1
-rw-r--r--src/cmd/link/internal/ld/macho.go564
-rw-r--r--src/cmd/link/internal/ld/main.go18
-rw-r--r--src/cmd/link/internal/ld/outbuf.go23
-rw-r--r--src/cmd/link/internal/ld/outbuf_darwin.go9
-rw-r--r--src/cmd/link/internal/ld/outbuf_mmap.go2
-rw-r--r--src/cmd/link/internal/ld/outbuf_nofallocate.go4
-rw-r--r--src/cmd/link/internal/ld/outbuf_notdarwin.go9
-rw-r--r--src/cmd/link/internal/ld/outbuf_test.go2
-rw-r--r--src/cmd/link/internal/ld/outbuf_windows.go5
-rw-r--r--src/cmd/link/internal/ld/pcln.go939
-rw-r--r--src/cmd/link/internal/ld/symtab.go97
-rw-r--r--src/cmd/link/internal/ld/target.go6
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go9
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go29
-rw-r--r--src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go23
-rw-r--r--src/cmd/link/internal/ld/testdata/issue39256/x.go2
-rw-r--r--src/cmd/link/internal/loadelf/ldelf.go48
-rw-r--r--src/cmd/link/internal/loader/loader.go161
-rw-r--r--src/cmd/link/internal/loader/symbolbuilder.go27
-rw-r--r--src/cmd/link/internal/loadmacho/ldmacho.go69
-rw-r--r--src/cmd/link/internal/mips64/obj.go5
-rw-r--r--src/cmd/link/internal/ppc64/asm.go7
-rw-r--r--src/cmd/link/internal/riscv64/asm.go163
-rw-r--r--src/cmd/link/internal/riscv64/obj.go3
-rw-r--r--src/cmd/link/internal/s390x/asm.go2
-rw-r--r--src/cmd/link/internal/sym/symbol.go4
-rw-r--r--src/cmd/link/internal/wasm/asm.go3
-rw-r--r--src/cmd/link/internal/x86/asm.go4
-rw-r--r--src/cmd/link/link_test.go133
-rw-r--r--src/cmd/link/testdata/testRO/x.go22
51 files changed, 2886 insertions, 1586 deletions
diff --git a/src/cmd/link/dwarf_test.go b/src/cmd/link/dwarf_test.go
index 88480064dd..db710bed6a 100644
--- a/src/cmd/link/dwarf_test.go
+++ b/src/cmd/link/dwarf_test.go
@@ -195,14 +195,18 @@ func TestDWARFiOS(t *testing.T) {
}
// Check to see if the ios tools are installed. It's possible to have the command line tools
// installed without the iOS sdk.
- if output, err := exec.Command("xcodebuild -showsdks").CombinedOutput(); err != nil {
+ if output, err := exec.Command("xcodebuild", "-showsdks").CombinedOutput(); err != nil {
t.Skipf("error running xcodebuild, required for iOS cross build: %v", err)
} else if !strings.Contains(string(output), "iOS SDK") {
t.Skipf("iOS SDK not detected.")
}
cc := "CC=" + runtime.GOROOT() + "/misc/ios/clangwrap.sh"
// iOS doesn't allow unmapped segments, so iOS executables don't have DWARF.
- testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
+ t.Run("exe", func(t *testing.T) {
+ testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=ios", "GOARCH=arm64")
+ })
// However, c-archive iOS objects have embedded DWARF.
- testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
+ t.Run("c-archive", func(t *testing.T) {
+ testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=ios", "GOARCH=arm64")
+ })
}
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go
index e5a6ef51b0..2d09a6160a 100644
--- a/src/cmd/link/internal/amd64/asm.go
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -76,9 +76,9 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
targType = ldr.SymType(targ)
}
- switch r.Type() {
+ switch rt := r.Type(); rt {
default:
- if r.Type() >= objabi.ElfRelocOffset {
+ if rt >= objabi.ElfRelocOffset {
ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
return false
}
@@ -167,13 +167,24 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
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:
- // TODO: What is the difference between all these?
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:
@@ -223,7 +234,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
if targType != sym.SDYNIMPORT {
ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
}
- ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_X86_64_GLOB_DAT))
+ ld.AddGotSym(target, ldr, syms, targ, 0)
su := ldr.MakeSymbolUpdater(s)
su.SetRelocType(rIdx, objabi.R_PCREL)
su.SetRelocSym(rIdx, syms.GOT)
@@ -343,7 +354,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
rela := ldr.MakeSymbolUpdater(syms.Rela)
rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
if r.Siz() == 8 {
- rela.AddUint64(target.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_X86_64_RELATIVE)))
+ 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))
}
@@ -355,28 +366,15 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
return true
}
- if target.IsDarwin() && ldr.SymSize(s) == int64(target.Arch.PtrSize) && r.Off() == 0 {
+ if target.IsDarwin() {
// Mach-O relocations are a royal pain to lay out.
- // They use a compact stateful bytecode representation
- // that is too much bother to deal with.
- // Instead, interpret the C declaration
- // void *_Cvar_stderr = &stderr;
- // as making _Cvar_stderr the name of a GOT entry
- // for stderr. This is separate from the usual GOT entry,
- // just in case the C code assigns to the variable,
- // and of course it only works for single pointers,
- // but we only need to support cgo and that's all it needs.
- ld.Adddynsym(ldr, target, syms, targ)
-
- got := ldr.MakeSymbolUpdater(syms.GOT)
- su := ldr.MakeSymbolUpdater(s)
- su.SetType(got.Type())
- got.AddInteriorSym(s)
- su.SetValue(got.Size())
- got.AddUint64(target.Arch, 0)
- leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT)
- leg.AddUint32(target.Arch, uint32(ldr.SymDynid(targ)))
- su.SetRelocType(rIdx, objabi.ElfRelocOffset) // ignore during relocsym
+ // 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
}
}
@@ -415,11 +413,7 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
case objabi.R_CALL:
if siz == 4 {
if ldr.SymType(r.Xsym) == sym.SDYNIMPORT {
- if ctxt.DynlinkingGo() {
- out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32)
- } else {
- out.Write64(uint64(elf.R_X86_64_GOTPCREL) | uint64(elfsym)<<32)
- }
+ out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32)
} else {
out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32)
}
@@ -622,31 +616,21 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8)
sDynid := ldr.SymDynid(s)
- rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(sDynid), uint32(elf.R_X86_64_JMP_SLOT)))
+ 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() {
- // To do lazy symbol lookup right, we're supposed
- // to tell the dynamic loader which library each
- // symbol comes from and format the link info
- // section just so. I'm too lazy (ha!) to do that
- // so for now we'll just use non-lazy pointers,
- // which don't need to be told which library to use.
- //
- // https://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html
- // has details about what we're avoiding.
-
- ld.AddGotSym(target, ldr, syms, s, uint32(elf.R_X86_64_GLOB_DAT))
- plt := ldr.MakeSymbolUpdater(syms.PLT)
+ ld.AddGotSym(target, ldr, syms, s, 0)
sDynid := ldr.SymDynid(s)
lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT)
lep.AddUint32(target.Arch, uint32(sDynid))
- // jmpq *got+size(IP)
+ 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)))
@@ -654,6 +638,7 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
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,
diff --git a/src/cmd/link/internal/amd64/l.go b/src/cmd/link/internal/amd64/l.go
index a9afb3a39f..c9ea90a9c8 100644
--- a/src/cmd/link/internal/amd64/l.go
+++ b/src/cmd/link/internal/amd64/l.go
@@ -33,7 +33,7 @@ package amd64
const (
maxAlign = 32 // max data alignment
minAlign = 1 // min data alignment
- funcAlign = 16
+ funcAlign = 32
)
/* Used by ../internal/ld/dwarf.go */
diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go
index 777f99dbe2..d09c90ea28 100644
--- a/src/cmd/link/internal/amd64/obj.go
+++ b/src/cmd/link/internal/amd64/obj.go
@@ -39,13 +39,8 @@ import (
func Init() (*sys.Arch, ld.Arch) {
arch := sys.ArchAMD64
- fa := funcAlign
- if objabi.GOAMD64 == "alignedjumps" {
- fa = 32
- }
-
theArch := ld.Arch{
- Funcalign: fa,
+ Funcalign: funcAlign,
Maxalign: maxAlign,
Minalign: minAlign,
Dwarfregsp: dwarfRegSP,
diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go
index 611c96ce35..755b472694 100644
--- a/src/cmd/link/internal/arm/asm.go
+++ b/src/cmd/link/internal/arm/asm.go
@@ -231,7 +231,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
ld.Adddynsym(ldr, target, syms, targ)
rel := ldr.MakeSymbolUpdater(syms.Rel)
rel.AddAddrPlus(target.Arch, s, int64(r.Off()))
- rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc
+ 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)
@@ -629,7 +629,7 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
// rel
rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
- rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_ARM_JUMP_SLOT)))
+ 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")
}
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
index 945b83822c..30819db4c6 100644
--- a/src/cmd/link/internal/arm64/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -71,13 +71,13 @@ 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 {
-
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 {
@@ -177,6 +177,14 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
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))
@@ -201,6 +209,75 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
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.
@@ -219,6 +296,16 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
// 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() {
@@ -302,7 +389,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
rela := ldr.MakeSymbolUpdater(syms.Rela)
rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
if r.Siz() == 8 {
- rela.AddUint64(target.Arch, ld.ELF64_R_INFO(0, uint32(elf.R_AARCH64_RELATIVE)))
+ 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))
}
@@ -313,6 +400,18 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
// (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
}
@@ -364,6 +463,9 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
return true
}
+// sign-extends from 24-bit.
+func signext24(x int64) int64 { return x << 40 >> 40 }
+
func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
var v uint32
@@ -371,7 +473,7 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
rt := r.Type
siz := r.Size
- if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 {
+ if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || 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
@@ -387,6 +489,10 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
}
}
+ if r.Xadd != signext24(r.Xadd) {
+ ldr.Errorf(s, "relocation addend overflow: %s+0x%x", ldr.SymName(rs), r.Xadd)
+ }
+
switch rt {
default:
return false
@@ -415,6 +521,22 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
}
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(r.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(r.Xadd&0xffffff))
+ }
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28
}
switch siz {
@@ -457,7 +579,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
}
nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1
- if target.IsDarwin() && rt == objabi.R_ADDRARM64 && xadd != 0 {
+ if target.IsDarwin() && xadd != 0 {
nExtReloc = 4 // need another two relocations for non-zero addend
}
@@ -633,14 +755,28 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
}
o0 := (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5)
return val | int64(o0), noExtReloc, isOk
- } else if (val>>24)&0x91 == 0x91 {
- // R_AARCH64_ADD_ABS_LO12_NC
+ } 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_PCRELARM64", val)
+ ldr.Errorf(s, "unsupported instruction for %x R_ARM64_PCREL", val)
}
case objabi.R_ARM64_LDST8:
@@ -648,6 +784,14 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
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 {
@@ -792,10 +936,38 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
rela.AddAddrPlus(target.Arch, gotplt.Sym(), gotplt.Size()-8)
sDynid := ldr.SymDynid(s)
- rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(sDynid), uint32(elf.R_AARCH64_JUMP_SLOT)))
+ 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")
}
diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go
index a980cfee52..ab3dfd99f7 100644
--- a/src/cmd/link/internal/arm64/obj.go
+++ b/src/cmd/link/internal/arm64/obj.go
@@ -102,7 +102,7 @@ func archinit(ctxt *ld.Link) {
case objabi.Hdarwin: /* apple MACH */
ld.HEADR = ld.INITIAL_MACHO_HEADR
if *ld.FlagTextAddr == -1 {
- *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+ *ld.FlagTextAddr = 1<<32 + int64(ld.HEADR)
}
if *ld.FlagRound == -1 {
*ld.FlagRound = 16384 // 16K page alignment
diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
index 52adbc3bab..e4fd591676 100644
--- a/src/cmd/link/internal/ld/ar.go
+++ b/src/cmd/link/internal/ld/ar.go
@@ -170,7 +170,7 @@ func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap {
// For Mach-O and PE/386 files we strip a leading
// underscore from the symbol name.
- if objabi.GOOS == "darwin" || (objabi.GOOS == "windows" && objabi.GOARCH == "386") {
+ if objabi.GOOS == "darwin" || objabi.GOOS == "ios" || (objabi.GOOS == "windows" && objabi.GOARCH == "386") {
if name[0] == '_' && len(name) > 1 {
name = name[1:]
}
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
index 2373b500e3..d1e06239a5 100644
--- a/src/cmd/link/internal/ld/config.go
+++ b/src/cmd/link/internal/ld/config.go
@@ -35,11 +35,16 @@ func (mode *BuildMode) Set(s string) error {
default:
return fmt.Errorf("invalid buildmode: %q", s)
case "exe":
- *mode = BuildModeExe
+ switch objabi.GOOS + "/" + objabi.GOARCH {
+ case "darwin/arm64", "windows/arm": // On these platforms, everything is PIE
+ *mode = BuildModePIE
+ default:
+ *mode = BuildModeExe
+ }
case "pie":
switch objabi.GOOS {
- case "aix", "android", "linux", "windows":
- case "darwin", "freebsd":
+ case "aix", "android", "linux", "windows", "darwin", "ios":
+ case "freebsd":
switch objabi.GOARCH {
case "amd64":
default:
@@ -51,7 +56,7 @@ func (mode *BuildMode) Set(s string) error {
*mode = BuildModePIE
case "c-archive":
switch objabi.GOOS {
- case "aix", "darwin", "linux":
+ case "aix", "darwin", "ios", "linux":
case "freebsd":
switch objabi.GOARCH {
case "amd64":
@@ -95,7 +100,13 @@ func (mode *BuildMode) Set(s string) error {
default:
return badmode()
}
- case "darwin", "freebsd":
+ case "darwin":
+ switch objabi.GOARCH {
+ case "amd64", "arm64":
+ default:
+ return badmode()
+ }
+ case "freebsd":
switch objabi.GOARCH {
case "amd64":
default:
@@ -186,7 +197,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
// Internally linking cgo is incomplete on some architectures.
// https://golang.org/issue/14449
// https://golang.org/issue/21961
- if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64) {
+ if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) {
return true, objabi.GOARCH + " does not support internal cgo"
}
if iscgo && objabi.GOOS == "android" {
@@ -210,6 +221,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
switch objabi.GOOS + "/" + objabi.GOARCH {
case "linux/amd64", "linux/arm64", "android/arm64":
case "windows/386", "windows/amd64", "windows/arm":
+ case "darwin/amd64", "darwin/arm64":
default:
// Internal linking does not support TLS_IE.
return true, "buildmode=pie"
@@ -263,8 +275,6 @@ func determineLinkMode(ctxt *Link) {
}
case LinkExternal:
switch {
- case objabi.GOARCH == "riscv64":
- Exitf("external linking not supported for %s/riscv64", objabi.GOOS)
case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix":
Exitf("external linking not supported for %s/ppc64", objabi.GOOS)
}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 8324a98a26..00130044ab 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -390,6 +390,12 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
o = ldr.SymValue(rs) + r.Add() - int64(ldr.SymSect(rs).Vaddr)
case objabi.R_WEAKADDROFF, objabi.R_METHODOFF:
if !ldr.AttrReachable(rs) {
+ if rt == objabi.R_METHODOFF {
+ // Set it to a sentinel value. The runtime knows this is not pointing to
+ // anything valid.
+ o = -1
+ break
+ }
continue
}
fallthrough
@@ -698,6 +704,9 @@ func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) {
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
@@ -775,6 +784,9 @@ func dynrelocsym(ctxt *Link, s loader.Sym) {
relocs := ldr.Relocs(s)
for ri := 0; ri < relocs.Count(); ri++ {
r := relocs.At(ri)
+ if r.IsMarker() {
+ continue // skip marker relocations
+ }
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
@@ -930,7 +942,7 @@ func writeBlock(ctxt *Link, out *OutBuf, ldr *loader.Loader, syms []loader.Sym,
break
}
if val < addr {
- ldr.Errorf(s, "phase error: addr=%#x but sym=%#x type=%d", addr, val, ldr.SymType(s))
+ 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 {
@@ -939,6 +951,9 @@ func writeBlock(ctxt *Link, out *OutBuf, ldr *loader.Loader, syms []loader.Sym,
}
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 {
@@ -1308,9 +1323,9 @@ func (state *dodataState) makeRelroForSharedLib(target *Link) {
// relro Type before it reaches here.
isRelro = true
case sym.SFUNCTAB:
- if target.IsAIX() && ldr.SymName(s) == "runtime.etypes" {
+ if ldr.SymName(s) == "runtime.etypes" {
// runtime.etypes must be at the end of
- // the relro datas.
+ // the relro data.
isRelro = true
}
}
@@ -1706,7 +1721,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ebss", 0), sect)
bssGcEnd := state.datsize - int64(sect.Vaddr)
- // Emit gcdata for bcc symbols now that symbol values have been assigned.
+ // Emit gcdata for bss symbols now that symbol values have been assigned.
gcsToEmit := []struct {
symName string
symKind sym.SymKind
@@ -1826,13 +1841,16 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
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.HeadType != objabi.Haix {
+ 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
@@ -1845,9 +1863,12 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
state.datsize = 0
}
- genrelrosecname = func(suffix string) string {
- return ".data.rel.ro" + suffix
+ 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]
@@ -1923,7 +1944,10 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
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.pclntab_old", 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)
if ctxt.HeadType == objabi.Haix {
xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB)
@@ -2167,7 +2191,7 @@ func (ctxt *Link) textaddress() {
ctxt.Textp[0] = text
}
- va := uint64(*FlagTextAddr)
+ va := uint64(Rnd(*FlagTextAddr, int64(Funcalign)))
n := 1
sect.Vaddr = va
ntramps := 0
@@ -2193,7 +2217,7 @@ func (ctxt *Link) textaddress() {
// 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, *FlagTextAddr)
+ ldr.SetSymValue(text, int64(Segtext.Sections[0].Vaddr))
}
// merge tramps into Textp, keeping Textp in address order
@@ -2507,7 +2531,10 @@ func (ctxt *Link) address() []*sym.Segment {
ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr))
ctxt.defineInternal("runtime.pcheader", sym.SRODATA)
ctxt.defineInternal("runtime.funcnametab", sym.SRODATA)
- ctxt.defineInternal("runtime.pclntab_old", 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))
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 0269429723..d8813fa936 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -62,6 +62,12 @@ func (d *deadcodePass) init() {
}
}
names = append(names, *flagEntrySymbol)
+ if !d.ctxt.linkShared && d.ctxt.BuildMode != BuildModePlugin {
+ // runtime.buildVersion and runtime.modinfo are referenced in .go.buildinfo section
+ // (see function buildinfo in data.go). They should normally be reachable from the
+ // runtime. Just make it explicit, in case.
+ names = append(names, "runtime.buildVersion", "runtime.modinfo")
+ }
if d.ctxt.BuildMode == BuildModePlugin {
names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs")
@@ -106,39 +112,67 @@ func (d *deadcodePass) flood() {
if isgotype {
usedInIface = d.ldr.AttrUsedInIface(symIdx)
- p := d.ldr.Data(symIdx)
- if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface {
- for _, sig := range d.decodeIfaceMethods(d.ldr, d.ctxt.Arch, symIdx, &relocs) {
- if d.ctxt.Debugvlog > 1 {
- d.ctxt.Logf("reached iface method: %v\n", sig)
- }
- d.ifaceMethod[sig] = true
- }
- }
}
methods = methods[:0]
for i := 0; i < relocs.Count(); i++ {
r := relocs.At(i)
t := r.Type()
- if t == objabi.R_WEAKADDROFF {
+ switch t {
+ case objabi.R_WEAKADDROFF:
continue
- }
- if t == objabi.R_METHODOFF {
+ 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
- }
- if t == objabi.R_USETYPE {
+ 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.ldr.SymType(rs) != sym.SDYNIMPORT { // don't decode DYNIMPORT symbol (we'll mark all exported methods anyway)
+ 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
}
rs := r.Sym()
if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) {
@@ -161,13 +195,9 @@ func (d *deadcodePass) flood() {
naux := d.ldr.NAux(symIdx)
for i := 0; i < naux; i++ {
a := d.ldr.Aux(symIdx, i)
- if a.Type() == goobj.AuxGotype && !d.ctxt.linkShared {
+ if a.Type() == goobj.AuxGotype {
// A symbol being reachable doesn't imply we need its
// type descriptor. Don't mark it.
- // TODO: when -linkshared, the GCProg generation code
- // seems to need it. I'm not sure why. I think it could
- // just reach to the type descriptor's data without
- // requiring to mark it reachable.
continue
}
d.mark(a.Sym(), symIdx)
@@ -209,15 +239,21 @@ 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 objabi.Fieldtrack_enabled != 0 {
+ if objabi.Fieldtrack_enabled != 0 && 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)
}
@@ -349,23 +385,17 @@ func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symId
return methods
}
-func (d *deadcodePass) decodeIfaceMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
+// 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 decodetypeKind(arch, p)&kindMask != kindInterface {
panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
}
- rel := decodeReloc(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize))
- s := rel.Sym()
- if s == 0 {
- return nil
- }
- if s != symIdx {
- panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx)))
- }
- off := int(rel.Add()) // array of reflect.imethod values
- numMethods := int(decodetypeIfaceMethodCount(arch, p))
- sizeofIMethod := 4 + 4
- return d.decodeMethodSig(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods)
+ 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
}
func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
diff --git a/src/cmd/link/internal/ld/deadcode_test.go b/src/cmd/link/internal/ld/deadcode_test.go
index 59122e9603..b756091613 100644
--- a/src/cmd/link/internal/ld/deadcode_test.go
+++ b/src/cmd/link/internal/ld/deadcode_test.go
@@ -32,6 +32,8 @@ func TestDeadcode(t *testing.T) {
{"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
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index d1f2ac583d..2ab9a55e96 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -23,6 +23,7 @@ import (
"cmd/link/internal/sym"
"fmt"
"log"
+ "path"
"runtime"
"sort"
"strings"
@@ -1173,13 +1174,81 @@ func expandFile(fname string) string {
return expandGoroot(fname)
}
-// writelines collects up and chai,ns together the symbols needed to
+// 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 {
+ 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 prolog (with
+// 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 prolog and epilog symbols are passed
+// 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.
@@ -1220,39 +1289,11 @@ func (d *dwctxt) writelines(unit *sym.CompilationUnit, lineProlog loader.Sym) []
lsu.AddUint8(0) // standard_opcode_lengths[8]
lsu.AddUint8(1) // standard_opcode_lengths[9]
lsu.AddUint8(0) // standard_opcode_lengths[10]
- lsu.AddUint8(0) // include_directories (empty)
-
- // Copy over the file table.
- fileNums := make(map[string]int)
- 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)
- }
- fileNums[name] = i + 1
- d.AddString(lsDwsym, name)
- lsu.AddUint8(0)
- lsu.AddUint8(0)
- lsu.AddUint8(0)
- // 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 {
- d.dwmu.Lock()
- if gdbscript == "" {
- k := strings.Index(name, "runtime/proc.go")
- gdbscript = name[:k] + "runtime/runtime-gdb.py"
- }
- d.dwmu.Unlock()
- }
- }
+ // Call helper to emit dir and file sections.
+ d.writeDirFileTables(unit, lsu)
- // 4 zeros: the string termination + 3 fields.
- lsu.AddUint8(0)
- // terminate file_names.
+ // capture length at end of file names.
headerend = lsu.Size()
unitlen := lsu.Size() - unitstart
@@ -1421,7 +1462,7 @@ func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo {
deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
}
- for pcsp.Init(fpcsp); !pcsp.Done; pcsp.Next() {
+ for pcsp.Init(d.linkctxt.loader.Data(fpcsp)); !pcsp.Done; pcsp.Next() {
nextpc := pcsp.NextPC
// pciterinit goes up to the end of the function,
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
index 22948521f5..a66506d392 100644
--- a/src/cmd/link/internal/ld/dwarf_test.go
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -238,6 +238,10 @@ 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.
@@ -919,6 +923,7 @@ func TestAbstractOriginSanityIssue26237(t *testing.T) {
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")
@@ -1018,6 +1023,9 @@ func main() {
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)
}
@@ -1203,6 +1211,15 @@ func main() {
}
}
+ // 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)
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index 2862f65f9f..37b2dc640d 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -10,8 +10,10 @@ import (
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"crypto/sha1"
+ "debug/elf"
"encoding/binary"
"encoding/hex"
+ "fmt"
"path/filepath"
"sort"
"strings"
@@ -74,255 +76,6 @@ type elfNote struct {
nType uint32
}
-const (
- EI_MAG0 = 0
- EI_MAG1 = 1
- EI_MAG2 = 2
- EI_MAG3 = 3
- EI_CLASS = 4
- EI_DATA = 5
- EI_VERSION = 6
- EI_OSABI = 7
- EI_ABIVERSION = 8
- OLD_EI_BRAND = 8
- EI_PAD = 9
- EI_NIDENT = 16
- ELFMAG0 = 0x7f
- ELFMAG1 = 'E'
- ELFMAG2 = 'L'
- ELFMAG3 = 'F'
- SELFMAG = 4
- EV_NONE = 0
- EV_CURRENT = 1
- ELFCLASSNONE = 0
- ELFCLASS32 = 1
- ELFCLASS64 = 2
- ELFDATANONE = 0
- ELFDATA2LSB = 1
- ELFDATA2MSB = 2
- ELFOSABI_NONE = 0
- ELFOSABI_HPUX = 1
- ELFOSABI_NETBSD = 2
- ELFOSABI_LINUX = 3
- ELFOSABI_HURD = 4
- ELFOSABI_86OPEN = 5
- ELFOSABI_SOLARIS = 6
- ELFOSABI_AIX = 7
- ELFOSABI_IRIX = 8
- ELFOSABI_FREEBSD = 9
- ELFOSABI_TRU64 = 10
- ELFOSABI_MODESTO = 11
- ELFOSABI_OPENBSD = 12
- ELFOSABI_OPENVMS = 13
- ELFOSABI_NSK = 14
- ELFOSABI_ARM = 97
- ELFOSABI_STANDALONE = 255
- ELFOSABI_SYSV = ELFOSABI_NONE
- ELFOSABI_MONTEREY = ELFOSABI_AIX
- ET_NONE = 0
- ET_REL = 1
- ET_EXEC = 2
- ET_DYN = 3
- ET_CORE = 4
- ET_LOOS = 0xfe00
- ET_HIOS = 0xfeff
- ET_LOPROC = 0xff00
- ET_HIPROC = 0xffff
- EM_NONE = 0
- EM_M32 = 1
- EM_SPARC = 2
- EM_386 = 3
- EM_68K = 4
- EM_88K = 5
- EM_860 = 7
- EM_MIPS = 8
- EM_S370 = 9
- EM_MIPS_RS3_LE = 10
- EM_PARISC = 15
- EM_VPP500 = 17
- EM_SPARC32PLUS = 18
- EM_960 = 19
- EM_PPC = 20
- EM_PPC64 = 21
- EM_S390 = 22
- EM_V800 = 36
- EM_FR20 = 37
- EM_RH32 = 38
- EM_RCE = 39
- EM_ARM = 40
- EM_SH = 42
- EM_SPARCV9 = 43
- EM_TRICORE = 44
- EM_ARC = 45
- EM_H8_300 = 46
- EM_H8_300H = 47
- EM_H8S = 48
- EM_H8_500 = 49
- EM_IA_64 = 50
- EM_MIPS_X = 51
- EM_COLDFIRE = 52
- EM_68HC12 = 53
- EM_MMA = 54
- EM_PCP = 55
- EM_NCPU = 56
- EM_NDR1 = 57
- EM_STARCORE = 58
- EM_ME16 = 59
- EM_ST100 = 60
- EM_TINYJ = 61
- EM_X86_64 = 62
- EM_AARCH64 = 183
- EM_486 = 6
- EM_MIPS_RS4_BE = 10
- EM_ALPHA_STD = 41
- EM_ALPHA = 0x9026
- EM_RISCV = 243
- SHN_UNDEF = 0
- SHN_LORESERVE = 0xff00
- SHN_LOPROC = 0xff00
- SHN_HIPROC = 0xff1f
- SHN_LOOS = 0xff20
- SHN_HIOS = 0xff3f
- SHN_ABS = 0xfff1
- SHN_COMMON = 0xfff2
- SHN_XINDEX = 0xffff
- SHN_HIRESERVE = 0xffff
- SHT_NULL = 0
- SHT_PROGBITS = 1
- SHT_SYMTAB = 2
- SHT_STRTAB = 3
- SHT_RELA = 4
- SHT_HASH = 5
- SHT_DYNAMIC = 6
- SHT_NOTE = 7
- SHT_NOBITS = 8
- SHT_REL = 9
- SHT_SHLIB = 10
- SHT_DYNSYM = 11
- SHT_INIT_ARRAY = 14
- SHT_FINI_ARRAY = 15
- SHT_PREINIT_ARRAY = 16
- SHT_GROUP = 17
- SHT_SYMTAB_SHNDX = 18
- SHT_LOOS = 0x60000000
- SHT_HIOS = 0x6fffffff
- SHT_GNU_VERDEF = 0x6ffffffd
- SHT_GNU_VERNEED = 0x6ffffffe
- SHT_GNU_VERSYM = 0x6fffffff
- SHT_LOPROC = 0x70000000
- SHT_ARM_ATTRIBUTES = 0x70000003
- SHT_HIPROC = 0x7fffffff
- SHT_LOUSER = 0x80000000
- SHT_HIUSER = 0xffffffff
- SHF_WRITE = 0x1
- SHF_ALLOC = 0x2
- SHF_EXECINSTR = 0x4
- SHF_MERGE = 0x10
- SHF_STRINGS = 0x20
- SHF_INFO_LINK = 0x40
- SHF_LINK_ORDER = 0x80
- SHF_OS_NONCONFORMING = 0x100
- SHF_GROUP = 0x200
- SHF_TLS = 0x400
- SHF_MASKOS = 0x0ff00000
- SHF_MASKPROC = 0xf0000000
- PT_NULL = 0
- PT_LOAD = 1
- PT_DYNAMIC = 2
- PT_INTERP = 3
- PT_NOTE = 4
- PT_SHLIB = 5
- PT_PHDR = 6
- PT_TLS = 7
- PT_LOOS = 0x60000000
- PT_HIOS = 0x6fffffff
- PT_LOPROC = 0x70000000
- PT_HIPROC = 0x7fffffff
- PT_GNU_STACK = 0x6474e551
- PT_GNU_RELRO = 0x6474e552
- PT_PAX_FLAGS = 0x65041580
- PT_SUNWSTACK = 0x6ffffffb
- PF_X = 0x1
- PF_W = 0x2
- PF_R = 0x4
- PF_MASKOS = 0x0ff00000
- PF_MASKPROC = 0xf0000000
- DT_NULL = 0
- DT_NEEDED = 1
- DT_PLTRELSZ = 2
- DT_PLTGOT = 3
- DT_HASH = 4
- DT_STRTAB = 5
- DT_SYMTAB = 6
- DT_RELA = 7
- DT_RELASZ = 8
- DT_RELAENT = 9
- DT_STRSZ = 10
- DT_SYMENT = 11
- DT_INIT = 12
- DT_FINI = 13
- DT_SONAME = 14
- DT_RPATH = 15
- DT_SYMBOLIC = 16
- DT_REL = 17
- DT_RELSZ = 18
- DT_RELENT = 19
- DT_PLTREL = 20
- DT_DEBUG = 21
- DT_TEXTREL = 22
- DT_JMPREL = 23
- DT_BIND_NOW = 24
- DT_INIT_ARRAY = 25
- DT_FINI_ARRAY = 26
- DT_INIT_ARRAYSZ = 27
- DT_FINI_ARRAYSZ = 28
- DT_RUNPATH = 29
- DT_FLAGS = 30
- DT_ENCODING = 32
- DT_PREINIT_ARRAY = 32
- DT_PREINIT_ARRAYSZ = 33
- DT_LOOS = 0x6000000d
- DT_HIOS = 0x6ffff000
- DT_LOPROC = 0x70000000
- DT_HIPROC = 0x7fffffff
- DT_VERNEED = 0x6ffffffe
- DT_VERNEEDNUM = 0x6fffffff
- DT_VERSYM = 0x6ffffff0
- DT_PPC64_GLINK = DT_LOPROC + 0
- DT_PPC64_OPT = DT_LOPROC + 3
- DF_ORIGIN = 0x0001
- DF_SYMBOLIC = 0x0002
- DF_TEXTREL = 0x0004
- DF_BIND_NOW = 0x0008
- DF_STATIC_TLS = 0x0010
- NT_PRSTATUS = 1
- NT_FPREGSET = 2
- NT_PRPSINFO = 3
- STB_LOCAL = 0
- STB_GLOBAL = 1
- STB_WEAK = 2
- STB_LOOS = 10
- STB_HIOS = 12
- STB_LOPROC = 13
- STB_HIPROC = 15
- STT_NOTYPE = 0
- STT_OBJECT = 1
- STT_FUNC = 2
- STT_SECTION = 3
- STT_FILE = 4
- STT_COMMON = 5
- STT_TLS = 6
- STT_LOOS = 10
- STT_HIOS = 12
- STT_LOPROC = 13
- STT_HIPROC = 15
- STV_DEFAULT = 0x0
- STV_INTERNAL = 0x1
- STV_HIDDEN = 0x2
- STV_PROTECTED = 0x3
- STN_UNDEF = 0
-)
-
/* For accessing the fields of r_info. */
/* For constructing r_info from field values. */
@@ -347,53 +100,20 @@ const (
/*
* ELF header.
*/
-type ElfEhdr struct {
- ident [EI_NIDENT]uint8
- type_ uint16
- machine uint16
- version uint32
- entry uint64
- phoff uint64
- shoff uint64
- flags uint32
- ehsize uint16
- phentsize uint16
- phnum uint16
- shentsize uint16
- shnum uint16
- shstrndx uint16
-}
+type ElfEhdr elf.Header64
/*
* Section header.
*/
type ElfShdr struct {
- name uint32
- type_ uint32
- flags uint64
- addr uint64
- off uint64
- size uint64
- link uint32
- info uint32
- addralign uint64
- entsize uint64
- shnum int
+ elf.Section64
+ shnum elf.SectionIndex
}
/*
* Program header.
*/
-type ElfPhdr struct {
- type_ uint32
- flags uint32
- off uint64
- vaddr uint64
- paddr uint64
- filesz uint64
- memsz uint64
- align uint64
-}
+type ElfPhdr elf.ProgHeader
/* For accessing the fields of r_info. */
@@ -496,22 +216,25 @@ func Elfinit(ctxt *Link) {
// 64-bit architectures
case sys.PPC64, sys.S390X:
if ctxt.Arch.ByteOrder == binary.BigEndian {
- ehdr.flags = 1 /* Version 1 ABI */
+ ehdr.Flags = 1 /* Version 1 ABI */
} else {
- ehdr.flags = 2 /* Version 2 ABI */
+ ehdr.Flags = 2 /* Version 2 ABI */
}
fallthrough
case sys.AMD64, sys.ARM64, sys.MIPS64, sys.RISCV64:
if ctxt.Arch.Family == sys.MIPS64 {
- ehdr.flags = 0x20000004 /* MIPS 3 CPIC */
+ ehdr.Flags = 0x20000004 /* MIPS 3 CPIC */
+ }
+ 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 */
+ 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:
@@ -526,19 +249,19 @@ func Elfinit(ctxt *Link) {
// 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
+ ehdr.Flags = 0x5000002 // has entry point, Version5 EABI
}
} else if ctxt.Arch.Family == sys.MIPS {
- ehdr.flags = 0x50001004 /* MIPS 32 CPIC O32*/
+ ehdr.Flags = 0x50001004 /* MIPS 32 CPIC O32*/
}
fallthrough
default:
- ehdr.phoff = ELF32HDRSIZE
+ 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 */
+ 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 */
}
}
@@ -548,83 +271,83 @@ func Elfinit(ctxt *Link) {
// 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))
+ 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)
+ 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_ == PT_LOAD {
+ if e.Type == elf.PT_LOAD {
fixElfPhdr(e)
}
- out.Write32(e.type_)
- out.Write32(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)
+ 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_ == PT_LOAD {
+ if e.Type == elf.PT_LOAD {
fixElfPhdr(e)
}
- out.Write32(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(e.flags)
- out.Write32(uint32(e.align))
+ 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(e.type_)
- out.Write64(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)
+ 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(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))
+ 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++ {
+ for i := 0; i < int(ehdr.Shnum); i++ {
elf64shdr(out, shdr[i])
}
- return uint32(ehdr.shnum) * ELF64SHDRSIZE
+ return uint32(ehdr.Shnum) * ELF64SHDRSIZE
}
- for i := 0; i < int(ehdr.shnum); i++ {
+ for i := 0; i < int(ehdr.Shnum); i++ {
elf32shdr(out, shdr[i])
}
- return uint32(ehdr.shnum) * ELF32SHDRSIZE
+ return uint32(ehdr.Shnum) * ELF32SHDRSIZE
}
func elfsetstring(ctxt *Link, s loader.Sym, str string, off int) {
@@ -640,43 +363,43 @@ func elfsetstring(ctxt *Link, s loader.Sym, str string, off int) {
func elfwritephdrs(out *OutBuf) uint32 {
if elf64 {
- for i := 0; i < int(ehdr.phnum); i++ {
+ for i := 0; i < int(ehdr.Phnum); i++ {
elf64phdr(out, phdr[i])
}
- return uint32(ehdr.phnum) * ELF64PHDRSIZE
+ return uint32(ehdr.Phnum) * ELF64PHDRSIZE
}
- for i := 0; i < int(ehdr.phnum); i++ {
+ for i := 0; i < int(ehdr.Phnum); i++ {
elf32phdr(out, phdr[i])
}
- return uint32(ehdr.phnum) * ELF32PHDRSIZE
+ return uint32(ehdr.Phnum) * ELF32PHDRSIZE
}
func newElfPhdr() *ElfPhdr {
e := new(ElfPhdr)
- if ehdr.phnum >= NSECT {
+ if ehdr.Phnum >= NSECT {
Errorf(nil, "too many phdrs")
} else {
- phdr[ehdr.phnum] = e
- ehdr.phnum++
+ phdr[ehdr.Phnum] = e
+ ehdr.Phnum++
}
if elf64 {
- ehdr.shoff += ELF64PHDRSIZE
+ ehdr.Shoff += ELF64PHDRSIZE
} else {
- ehdr.shoff += ELF32PHDRSIZE
+ ehdr.Shoff += ELF32PHDRSIZE
}
return e
}
func newElfShdr(name int64) *ElfShdr {
e := new(ElfShdr)
- e.name = uint32(name)
- e.shnum = int(ehdr.shnum)
- if ehdr.shnum >= NSECT {
+ 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++
+ shdr[ehdr.Shnum] = e
+ ehdr.Shnum++
}
return e
@@ -687,38 +410,38 @@ func getElfEhdr() *ElfEhdr {
}
func elf64writehdr(out *OutBuf) uint32 {
- out.Write(ehdr.ident[:])
- out.Write16(ehdr.type_)
- out.Write16(ehdr.machine)
- out.Write32(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)
+ 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(ehdr.type_)
- out.Write16(ehdr.machine)
- out.Write32(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)
+ 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
}
@@ -742,11 +465,11 @@ func elfhash(name string) uint32 {
return h
}
-func elfWriteDynEntSym(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) {
+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 int, val uint64) {
+func Elfwritedynent(arch *sys.Arch, s *loader.SymbolBuilder, tag elf.DynTag, val uint64) {
if elf64 {
s.AddUint64(arch, uint64(tag))
s.AddUint64(arch, val)
@@ -756,11 +479,11 @@ func Elfwritedynent(arch *sys.Arch, s *loader.SymbolBuilder, tag int, val uint64
}
}
-func elfwritedynentsym(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) {
+func elfwritedynentsym(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) {
Elfwritedynentsymplus(ctxt, s, tag, t, 0)
}
-func Elfwritedynentsymplus(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym, add int64) {
+func Elfwritedynentsymplus(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym, add int64) {
if elf64 {
s.AddUint64(ctxt.Arch, uint64(tag))
} else {
@@ -769,7 +492,7 @@ func Elfwritedynentsymplus(ctxt *Link, s *loader.SymbolBuilder, tag int, t loade
s.AddAddrPlus(ctxt.Arch, t, add)
}
-func elfwritedynentsymsize(ctxt *Link, s *loader.SymbolBuilder, tag int, t loader.Sym) {
+func elfwritedynentsymsize(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) {
if elf64 {
s.AddUint64(ctxt.Arch, uint64(tag))
} else {
@@ -781,30 +504,30 @@ func elfwritedynentsymsize(ctxt *Link, s *loader.SymbolBuilder, tag int, t loade
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)
+ 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.SeekSet(int64(sh.Off))
out.WriteString(interp)
out.Write8(0)
- return int(sh.size)
+ return int(sh.Size)
}
func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sz int) int {
n := 3*4 + uint64(sz) + resoff%4
- sh.type_ = SHT_NOTE
- sh.flags = SHF_ALLOC
- sh.addralign = 4
- sh.addr = startva + resoff - n
- sh.off = resoff - n
- sh.size = n - resoff%4
+ 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)
}
@@ -813,7 +536,7 @@ func elfwritenotehdr(out *OutBuf, str string, namesz uint32, descsz uint32, tag
sh := elfshname(str)
// Write Elf_Note header.
- out.SeekSet(int64(sh.off))
+ out.SeekSet(int64(sh.Off))
out.Write32(namesz)
out.Write32(descsz)
@@ -850,7 +573,7 @@ func elfwritenetbsdsig(out *OutBuf) int {
out.Write8(0)
out.Write32(ELF_NOTE_NETBSD_VERSION)
- return int(sh.size)
+ return int(sh.Size)
}
// The race detector can't handle ASLR (address space layout randomization).
@@ -869,7 +592,7 @@ func elfwritenetbsdpax(out *OutBuf) int {
}
out.Write([]byte("PaX\x00"))
out.Write32(0x20) // 0x20 = Force disable ASLR
- return int(sh.size)
+ return int(sh.Size)
}
// OpenBSD Signature
@@ -900,7 +623,7 @@ func elfwriteopenbsdsig(out *OutBuf) int {
out.Write32(ELF_NOTE_OPENBSD_VERSION)
- return int(sh.size)
+ return int(sh.Size)
}
func addbuildinfo(val string) {
@@ -959,7 +682,7 @@ func elfwritebuildinfo(out *OutBuf) int {
var zero = make([]byte, 4)
out.Write(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))])
- return int(sh.size)
+ return int(sh.Size)
}
func elfwritegobuildid(out *OutBuf) int {
@@ -973,7 +696,7 @@ func elfwritegobuildid(out *OutBuf) int {
var zero = make([]byte, 4)
out.Write(zero[:int(Rnd(int64(len(*flagBuildid)), 4)-int64(len(*flagBuildid)))])
- return int(sh.size)
+ return int(sh.Size)
}
// Go specific notes
@@ -1144,56 +867,56 @@ func elfdynhash(ctxt *Link) {
s = ldr.CreateSymForUpdate(".dynamic", 0)
elfverneed = nfile
if elfverneed != 0 {
- elfWriteDynEntSym(ctxt, s, DT_VERNEED, gnuVersionR.Sym())
- Elfwritedynent(ctxt.Arch, s, DT_VERNEEDNUM, uint64(nfile))
- elfWriteDynEntSym(ctxt, s, DT_VERSYM, gnuVersion.Sym())
+ 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, DT_PLTREL, DT_RELA)
+ Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_RELA))
} else {
- Elfwritedynent(ctxt.Arch, s, DT_PLTREL, DT_REL)
+ Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_REL))
}
- elfwritedynentsymsize(ctxt, s, DT_PLTRELSZ, sy.Sym())
- elfWriteDynEntSym(ctxt, s, DT_JMPREL, sy.Sym())
+ elfwritedynentsymsize(ctxt, s, elf.DT_PLTRELSZ, sy.Sym())
+ elfWriteDynEntSym(ctxt, s, elf.DT_JMPREL, sy.Sym())
}
- Elfwritedynent(ctxt.Arch, s, DT_NULL, 0)
+ Elfwritedynent(ctxt.Arch, s, elf.DT_NULL, 0)
}
func elfphload(seg *sym.Segment) *ElfPhdr {
ph := newElfPhdr()
- ph.type_ = PT_LOAD
+ ph.Type = elf.PT_LOAD
if seg.Rwx&4 != 0 {
- ph.flags |= PF_R
+ ph.Flags |= elf.PF_R
}
if seg.Rwx&2 != 0 {
- ph.flags |= PF_W
+ ph.Flags |= elf.PF_W
}
if seg.Rwx&1 != 0 {
- ph.flags |= PF_X
+ 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)
+ 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_ = 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)
+ 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 {
@@ -1202,9 +925,9 @@ func elfshname(name string) *ElfShdr {
continue
}
off := elfstr[i].off
- for i = 0; i < int(ehdr.shnum); i++ {
+ for i = 0; i < int(ehdr.Shnum); i++ {
sh := shdr[i]
- if sh.name == uint32(off) {
+ if sh.Name == uint32(off) {
return sh
}
}
@@ -1249,7 +972,7 @@ func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr {
// 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_ == SHT_NOTE {
+ 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
@@ -1258,44 +981,44 @@ func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr {
// 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")
+ 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
+ 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 {
+ if sh.Type > 0 {
return sh
}
if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
- sh.type_ = SHT_PROGBITS
+ sh.Type = uint32(elf.SHT_PROGBITS)
} else {
- sh.type_ = SHT_NOBITS
+ sh.Type = uint32(elf.SHT_NOBITS)
}
- sh.flags = SHF_ALLOC
+ sh.Flags = uint64(elf.SHF_ALLOC)
if sect.Rwx&1 != 0 {
- sh.flags |= SHF_EXECINSTR
+ sh.Flags |= uint64(elf.SHF_EXECINSTR)
}
if sect.Rwx&2 != 0 {
- sh.flags |= SHF_WRITE
+ sh.Flags |= uint64(elf.SHF_WRITE)
}
if sect.Name == ".tbss" {
- sh.flags |= SHF_TLS
- sh.type_ = SHT_NOBITS
+ sh.Flags |= uint64(elf.SHF_TLS)
+ sh.Type = uint32(elf.SHT_NOBITS)
}
if strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") {
- sh.flags = 0
+ sh.Flags = 0
}
if linkmode != LinkExternal {
- sh.addr = sect.Vaddr
+ sh.Addr = sect.Vaddr
}
- sh.addralign = uint64(sect.Align)
- sh.size = sect.Length
+ sh.Addralign = uint64(sect.Align)
+ sh.Size = sect.Length
if sect.Name != ".tbss" {
- sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
+ sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
}
return sh
@@ -1310,13 +1033,13 @@ func elfshreloc(arch *sys.Arch, sect *sym.Section) *ElfShdr {
if sect.Name == ".shstrtab" || sect.Name == ".tbss" {
return nil
}
- if sect.Elfsect.(*ElfShdr).type_ == SHT_NOTE {
+ if sect.Elfsect.(*ElfShdr).Type == uint32(elf.SHT_NOTE) {
return nil
}
- typ := SHT_REL
+ typ := elf.SHT_REL
if elfRelType == ".rela" {
- typ = SHT_RELA
+ typ = elf.SHT_RELA
}
sh := elfshname(elfRelType + sect.Name)
@@ -1324,21 +1047,21 @@ func elfshreloc(arch *sys.Arch, sect *sym.Section) *ElfShdr {
// its own .rela.text.
if sect.Name == ".text" {
- if sh.info != 0 && sh.info != uint32(sect.Elfsect.(*ElfShdr).shnum) {
+ 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 == SHT_RELA {
- sh.entsize += uint64(arch.RegSize)
+ 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)
+ 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
}
@@ -1400,7 +1123,7 @@ func elfrelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym)
// sanity check
if uint64(out.Offset()) != sect.Reloff+sect.Rellen {
- panic("elfrelocsect: size mismatch")
+ panic(fmt.Sprintf("elfrelocsect: size mismatch %d != %d + %d", out.Offset(), sect.Reloff, sect.Rellen))
}
}
@@ -1651,47 +1374,47 @@ func (ctxt *Link) doelf() {
/*
* .dynamic table
*/
- elfwritedynentsym(ctxt, dynamic, DT_HASH, hash.Sym())
+ elfwritedynentsym(ctxt, dynamic, elf.DT_HASH, hash.Sym())
- elfwritedynentsym(ctxt, dynamic, DT_SYMTAB, dynsym.Sym())
+ elfwritedynentsym(ctxt, dynamic, elf.DT_SYMTAB, dynsym.Sym())
if elf64 {
- Elfwritedynent(ctxt.Arch, dynamic, DT_SYMENT, ELF64SYMSIZE)
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF64SYMSIZE)
} else {
- Elfwritedynent(ctxt.Arch, dynamic, DT_SYMENT, ELF32SYMSIZE)
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF32SYMSIZE)
}
- elfwritedynentsym(ctxt, dynamic, DT_STRTAB, dynstr.Sym())
- elfwritedynentsymsize(ctxt, dynamic, DT_STRSZ, dynstr.Sym())
+ 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, DT_RELA, rela)
- elfwritedynentsymsize(ctxt, dynamic, DT_RELASZ, rela)
- Elfwritedynent(ctxt.Arch, dynamic, DT_RELAENT, ELF64RELASIZE)
+ 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, DT_REL, rel)
- elfwritedynentsymsize(ctxt, dynamic, DT_RELSZ, rel)
- Elfwritedynent(ctxt.Arch, dynamic, DT_RELENT, ELF32RELSIZE)
+ 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, DT_RUNPATH, uint64(dynstr.Addstring(rpath.val)))
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RUNPATH, uint64(dynstr.Addstring(rpath.val)))
}
if ctxt.IsPPC64() {
- elfwritedynentsym(ctxt, dynamic, DT_PLTGOT, plt.Sym())
+ elfwritedynentsym(ctxt, dynamic, elf.DT_PLTGOT, plt.Sym())
} else {
- elfwritedynentsym(ctxt, dynamic, DT_PLTGOT, gotplt.Sym())
+ elfwritedynentsym(ctxt, dynamic, elf.DT_PLTGOT, gotplt.Sym())
}
if ctxt.IsPPC64() {
- Elfwritedynent(ctxt.Arch, dynamic, DT_PPC64_OPT, 0)
+ 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 DT_PLTREL,
- // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the
+ // 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, DT_DEBUG, 0)
+ Elfwritedynent(ctxt.Arch, dynamic, elf.DT_DEBUG, 0)
}
if ctxt.IsShared() {
@@ -1730,20 +1453,20 @@ func shsym(sh *ElfShdr, ldr *loader.Loader, s loader.Sym) {
panic("bad symbol in shsym2")
}
addr := ldr.SymValue(s)
- if sh.flags&SHF_ALLOC != 0 {
- sh.addr = uint64(addr)
+ 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))
+ 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
+ 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() {
@@ -1795,21 +1518,21 @@ func asmbElf(ctxt *Link) {
default:
Exitf("unknown architecture in asmbelf: %v", ctxt.Arch.Family)
case sys.MIPS, sys.MIPS64:
- eh.machine = EM_MIPS
+ eh.Machine = uint16(elf.EM_MIPS)
case sys.ARM:
- eh.machine = EM_ARM
+ eh.Machine = uint16(elf.EM_ARM)
case sys.AMD64:
- eh.machine = EM_X86_64
+ eh.Machine = uint16(elf.EM_X86_64)
case sys.ARM64:
- eh.machine = EM_AARCH64
+ eh.Machine = uint16(elf.EM_AARCH64)
case sys.I386:
- eh.machine = EM_386
+ eh.Machine = uint16(elf.EM_386)
case sys.PPC64:
- eh.machine = EM_PPC64
+ eh.Machine = uint16(elf.EM_PPC64)
case sys.RISCV64:
- eh.machine = EM_RISCV
+ eh.Machine = uint16(elf.EM_RISCV)
case sys.S390X:
- eh.machine = EM_S390
+ eh.Machine = uint16(elf.EM_S390)
}
elfreserve := int64(ELFRESERVE)
@@ -1839,30 +1562,30 @@ func asmbElf(ctxt *Link) {
sh := elfshname(".note.netbsd.pax")
resoff -= int64(elfnetbsdpax(sh, uint64(startva), uint64(resoff)))
pnote = newElfPhdr()
- pnote.type_ = PT_NOTE
- pnote.flags = PF_R
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
phsh(pnote, sh)
}
if ctxt.LinkMode == LinkExternal {
/* skip program headers */
- eh.phoff = 0
+ eh.Phoff = 0
- eh.phentsize = 0
+ eh.Phentsize = 0
if ctxt.BuildMode == BuildModeShared {
sh := elfshname(".note.go.pkg-list")
- sh.type_ = SHT_NOTE
+ sh.Type = uint32(elf.SHT_NOTE)
sh = elfshname(".note.go.abihash")
- sh.type_ = SHT_NOTE
- sh.flags = SHF_ALLOC
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh.Flags = uint64(elf.SHF_ALLOC)
sh = elfshname(".note.go.deps")
- sh.type_ = SHT_NOTE
+ sh.Type = uint32(elf.SHT_NOTE)
}
if *flagBuildid != "" {
sh := elfshname(".note.go.buildid")
- sh.type_ = SHT_NOTE
- sh.flags = SHF_ALLOC
+ sh.Type = uint32(elf.SHT_NOTE)
+ sh.Flags = uint64(elf.SHF_ALLOC)
}
goto elfobj
@@ -1871,22 +1594,22 @@ func asmbElf(ctxt *Link) {
/* program header info */
pph = newElfPhdr()
- pph.type_ = PT_PHDR
- pph.flags = 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)
+ 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)
+ o := int64(Segtext.Vaddr - pph.Vaddr)
Segtext.Vaddr -= uint64(o)
Segtext.Length += uint64(o)
- o = int64(Segtext.Fileoff - pph.off)
+ o = int64(Segtext.Fileoff - pph.Off)
Segtext.Fileoff -= uint64(o)
Segtext.Filelen += uint64(o)
}
@@ -1895,9 +1618,9 @@ func asmbElf(ctxt *Link) {
/* interpreter */
sh := elfshname(".interp")
- sh.type_ = SHT_PROGBITS
- sh.flags = SHF_ALLOC
- sh.addralign = 1
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Flags = uint64(elf.SHF_ALLOC)
+ sh.Addralign = 1
if interpreter == "" && objabi.GO_LDSO != "" {
interpreter = objabi.GO_LDSO
@@ -1935,8 +1658,8 @@ func asmbElf(ctxt *Link) {
resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter))
ph := newElfPhdr()
- ph.type_ = PT_INTERP
- ph.flags = PF_R
+ ph.Type = elf.PT_INTERP
+ ph.Flags = elf.PF_R
phsh(ph, sh)
}
@@ -1954,8 +1677,8 @@ func asmbElf(ctxt *Link) {
}
pnote = newElfPhdr()
- pnote.type_ = PT_NOTE
- pnote.flags = PF_R
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
phsh(pnote, sh)
}
@@ -1965,8 +1688,8 @@ func asmbElf(ctxt *Link) {
if pnote == nil {
pnote = newElfPhdr()
- pnote.type_ = PT_NOTE
- pnote.flags = PF_R
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
}
phsh(pnote, sh)
@@ -1977,8 +1700,8 @@ func asmbElf(ctxt *Link) {
resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff)))
pnote := newElfPhdr()
- pnote.type_ = PT_NOTE
- pnote.flags = PF_R
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
phsh(pnote, sh)
}
@@ -1997,15 +1720,15 @@ func asmbElf(ctxt *Link) {
/* Dynamic linking sections */
if !*FlagD {
sh := elfshname(".dynsym")
- sh.type_ = SHT_DYNSYM
- sh.flags = SHF_ALLOC
+ sh.Type = uint32(elf.SHT_DYNSYM)
+ sh.Flags = uint64(elf.SHF_ALLOC)
if elf64 {
- sh.entsize = ELF64SYMSIZE
+ sh.Entsize = ELF64SYMSIZE
} else {
- sh.entsize = ELF32SYMSIZE
+ sh.Entsize = ELF32SYMSIZE
}
- sh.addralign = uint64(ctxt.Arch.RegSize)
- sh.link = uint32(elfshname(".dynstr").shnum)
+ 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)
@@ -2016,134 +1739,134 @@ func asmbElf(ctxt *Link) {
break
}
}
- sh.info = i
+ sh.Info = i
shsym(sh, ldr, s)
sh = elfshname(".dynstr")
- sh.type_ = SHT_STRTAB
- sh.flags = SHF_ALLOC
- sh.addralign = 1
+ 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_ = SHT_GNU_VERSYM
- sh.flags = SHF_ALLOC
- sh.addralign = 2
- sh.link = uint32(elfshname(".dynsym").shnum)
- sh.entsize = 2
+ 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_ = SHT_GNU_VERNEED
- sh.flags = SHF_ALLOC
- sh.addralign = uint64(ctxt.Arch.RegSize)
- sh.info = uint32(elfverneed)
- sh.link = uint32(elfshname(".dynstr").shnum)
+ 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_ = SHT_RELA
- sh.flags = SHF_ALLOC
- sh.entsize = ELF64RELASIZE
- sh.addralign = uint64(ctxt.Arch.RegSize)
- sh.link = uint32(elfshname(".dynsym").shnum)
- sh.info = uint32(elfshname(".plt").shnum)
+ 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_ = SHT_RELA
- sh.flags = SHF_ALLOC
- sh.entsize = ELF64RELASIZE
- sh.addralign = 8
- sh.link = uint32(elfshname(".dynsym").shnum)
+ 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_ = SHT_REL
- sh.flags = SHF_ALLOC
- sh.entsize = ELF32RELSIZE
- sh.addralign = 4
- sh.link = uint32(elfshname(".dynsym").shnum)
+ 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_ = SHT_REL
- sh.flags = SHF_ALLOC
- sh.entsize = ELF32RELSIZE
- sh.addralign = 4
- sh.link = uint32(elfshname(".dynsym").shnum)
+ 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 eh.machine == EM_PPC64 {
+ if elf.Machine(eh.Machine) == elf.EM_PPC64 {
sh := elfshname(".glink")
- sh.type_ = SHT_PROGBITS
- sh.flags = SHF_ALLOC + SHF_EXECINSTR
- sh.addralign = 4
+ 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_ = SHT_PROGBITS
- sh.flags = SHF_ALLOC + SHF_EXECINSTR
- if eh.machine == EM_X86_64 {
- sh.entsize = 16
- } else if eh.machine == EM_S390 {
- sh.entsize = 32
- } else if eh.machine == EM_PPC64 {
+ 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_ = SHT_NOBITS
+ sh.Type = uint32(elf.SHT_NOBITS)
- sh.flags = SHF_ALLOC + SHF_WRITE
- sh.entsize = 8
+ sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
+ sh.Entsize = 8
} else {
- sh.entsize = 4
+ sh.Entsize = 4
}
- sh.addralign = sh.entsize
+ 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 eh.machine != EM_PPC64 {
+ if elf.Machine(eh.Machine) != elf.EM_PPC64 {
sh := elfshname(".got")
- sh.type_ = SHT_PROGBITS
- sh.flags = SHF_ALLOC + SHF_WRITE
- sh.entsize = uint64(ctxt.Arch.RegSize)
- sh.addralign = uint64(ctxt.Arch.RegSize)
+ 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_ = SHT_PROGBITS
- sh.flags = SHF_ALLOC + SHF_WRITE
- sh.entsize = uint64(ctxt.Arch.RegSize)
- sh.addralign = uint64(ctxt.Arch.RegSize)
+ 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_ = SHT_HASH
- sh.flags = SHF_ALLOC
- sh.entsize = 4
- sh.addralign = uint64(ctxt.Arch.RegSize)
- sh.link = uint32(elfshname(".dynsym").shnum)
+ 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 PT_DYNAMIC for .dynamic section */
+ /* sh and elf.PT_DYNAMIC for .dynamic section */
sh = elfshname(".dynamic")
- sh.type_ = SHT_DYNAMIC
- sh.flags = SHF_ALLOC + SHF_WRITE
- sh.entsize = 2 * uint64(ctxt.Arch.RegSize)
- sh.addralign = uint64(ctxt.Arch.RegSize)
- sh.link = uint32(elfshname(".dynstr").shnum)
+ 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_ = PT_DYNAMIC
- ph.flags = PF_R + PF_W
+ ph.Type = elf.PT_DYNAMIC
+ ph.Flags = elf.PF_R + elf.PF_W
phsh(ph, sh)
/*
@@ -2157,35 +1880,35 @@ func asmbElf(ctxt *Link) {
}
if tlssize != 0 {
ph := newElfPhdr()
- ph.type_ = PT_TLS
- ph.flags = PF_R
- ph.memsz = tlssize
- ph.align = uint64(ctxt.Arch.RegSize)
+ 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_ = PT_GNU_STACK
- ph.flags = PF_W + PF_R
- ph.align = uint64(ctxt.Arch.RegSize)
+ ph.Type = elf.PT_GNU_STACK
+ ph.Flags = elf.PF_W + elf.PF_R
+ ph.Align = uint64(ctxt.Arch.RegSize)
ph = newElfPhdr()
- ph.type_ = PT_PAX_FLAGS
- ph.flags = 0x2a00 // mprotect, randexec, emutramp disabled
- ph.align = uint64(ctxt.Arch.RegSize)
+ 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_ = PT_SUNWSTACK
- ph.flags = PF_W + PF_R
+ ph.Type = elf.PT_SUNWSTACK
+ ph.Flags = elf.PF_W + elf.PF_R
}
elfobj:
sh := elfshname(".shstrtab")
- sh.type_ = SHT_STRTAB
- sh.addralign = 1
+ sh.Type = uint32(elf.SHT_STRTAB)
+ sh.Addralign = 1
shsym(sh, ldr, ldr.Lookup(".shstrtab", 0))
- eh.shstrndx = uint16(sh.shnum)
+ eh.Shstrndx = uint16(sh.shnum)
// put these sections early in the list
if !*FlagS {
@@ -2229,72 +1952,73 @@ elfobj:
// add a .note.GNU-stack section to mark the stack as non-executable
sh := elfshname(".note.GNU-stack")
- sh.type_ = SHT_PROGBITS
- sh.addralign = 1
- sh.flags = 0
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ sh.Addralign = 1
+ sh.Flags = 0
}
if !*FlagS {
sh := elfshname(".symtab")
- sh.type_ = 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.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_ = SHT_STRTAB
- sh.off = uint64(symo) + uint64(symSize)
- sh.size = uint64(len(Elfstrdat))
- sh.addralign = 1
+ sh.Type = uint32(elf.SHT_STRTAB)
+ sh.Off = uint64(symo) + uint64(symSize)
+ sh.Size = uint64(len(Elfstrdat))
+ sh.Addralign = 1
}
/* Main header */
- eh.ident[EI_MAG0] = '\177'
+ copy(eh.Ident[:], elf.ELFMAG)
- eh.ident[EI_MAG1] = 'E'
- eh.ident[EI_MAG2] = 'L'
- eh.ident[EI_MAG3] = 'F'
- if ctxt.HeadType == objabi.Hfreebsd {
- eh.ident[EI_OSABI] = ELFOSABI_FREEBSD
- } else if ctxt.HeadType == objabi.Hnetbsd {
- eh.ident[EI_OSABI] = ELFOSABI_NETBSD
- } else if ctxt.HeadType == objabi.Hopenbsd {
- eh.ident[EI_OSABI] = ELFOSABI_OPENBSD
- } else if ctxt.HeadType == objabi.Hdragonfly {
- eh.ident[EI_OSABI] = ELFOSABI_NONE
+ 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[EI_CLASS] = ELFCLASS64
+ eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS64)
} else {
- eh.ident[EI_CLASS] = ELFCLASS32
+ eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS32)
}
if ctxt.Arch.ByteOrder == binary.BigEndian {
- eh.ident[EI_DATA] = ELFDATA2MSB
+ eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2MSB)
} else {
- eh.ident[EI_DATA] = ELFDATA2LSB
+ eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2LSB)
}
- eh.ident[EI_VERSION] = EV_CURRENT
+ eh.Ident[elf.EI_VERSION] = byte(elf.EV_CURRENT)
if ctxt.LinkMode == LinkExternal {
- eh.type_ = ET_REL
+ eh.Type = uint16(elf.ET_REL)
} else if ctxt.BuildMode == BuildModePIE {
- eh.type_ = ET_DYN
+ eh.Type = uint16(elf.ET_DYN)
} else {
- eh.type_ = ET_EXEC
+ eh.Type = uint16(elf.ET_EXEC)
}
if ctxt.LinkMode != LinkExternal {
- eh.entry = uint64(Entryvalue(ctxt))
+ eh.Entry = uint64(Entryvalue(ctxt))
}
- eh.version = EV_CURRENT
+ eh.Version = uint32(elf.EV_CURRENT)
if pph != nil {
- pph.filesz = uint64(eh.phnum) * uint64(eh.phentsize)
- pph.memsz = pph.filesz
+ pph.Filesz = uint64(eh.Phnum) * uint64(eh.Phentsize)
+ pph.Memsz = pph.Filesz
}
ctxt.Out.SeekSet(0)
@@ -2344,21 +2068,21 @@ func elfadddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.S
if elf64 {
/* type */
- t := STB_GLOBAL << 4
+ var t uint8
if cgoexp && st == sym.STEXT {
- t |= STT_FUNC
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
} else {
- t |= STT_OBJECT
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT)
}
- d.AddUint8(uint8(t))
+ d.AddUint8(t)
/* reserved */
d.AddUint8(0)
/* section where symbol is defined */
if st == sym.SDYNIMPORT {
- d.AddUint16(target.Arch, SHN_UNDEF)
+ d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF))
} else {
d.AddUint16(target.Arch, 1)
}
@@ -2377,7 +2101,7 @@ func elfadddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.S
if target.Arch.Family == sys.AMD64 && !cgoeDynamic && dil != "" && !seenlib[dil] {
du := ldr.MakeSymbolUpdater(syms.Dynamic)
- Elfwritedynent(target.Arch, du, DT_NEEDED, uint64(dstru.Addstring(dil)))
+ Elfwritedynent(target.Arch, du, elf.DT_NEEDED, uint64(dstru.Addstring(dil)))
seenlib[dil] = true
}
} else {
@@ -2393,80 +2117,24 @@ func elfadddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.S
d.AddUint32(target.Arch, uint32(len(ldr.Data(s))))
/* type */
- t := STB_GLOBAL << 4
+ 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 |= STT_FUNC
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
} else if target.Arch.Family == sys.ARM && cgoeDynamic && st == sym.STEXT {
- t |= STT_FUNC
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
} else {
- t |= STT_OBJECT
+ t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT)
}
- d.AddUint8(uint8(t))
+ d.AddUint8(t)
d.AddUint8(0)
/* shndx */
if st == sym.SDYNIMPORT {
- d.AddUint16(target.Arch, SHN_UNDEF)
+ d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF))
} else {
d.AddUint16(target.Arch, 1)
}
}
}
-
-func ELF32_R_SYM(info uint32) uint32 {
- return info >> 8
-}
-
-func ELF32_R_TYPE(info uint32) uint32 {
- return uint32(uint8(info))
-}
-
-func ELF32_R_INFO(sym uint32, type_ uint32) uint32 {
- return sym<<8 | type_
-}
-
-func ELF32_ST_BIND(info uint8) uint8 {
- return info >> 4
-}
-
-func ELF32_ST_TYPE(info uint8) uint8 {
- return info & 0xf
-}
-
-func ELF32_ST_INFO(bind uint8, type_ uint8) uint8 {
- return bind<<4 | type_&0xf
-}
-
-func ELF32_ST_VISIBILITY(oth uint8) uint8 {
- return oth & 3
-}
-
-func ELF64_R_SYM(info uint64) uint32 {
- return uint32(info >> 32)
-}
-
-func ELF64_R_TYPE(info uint64) uint32 {
- return uint32(info)
-}
-
-func ELF64_R_INFO(sym uint32, type_ uint32) uint64 {
- return uint64(sym)<<32 | uint64(type_)
-}
-
-func ELF64_ST_BIND(info uint8) uint8 {
- return info >> 4
-}
-
-func ELF64_ST_TYPE(info uint8) uint8 {
- return info & 0xf
-}
-
-func ELF64_ST_INFO(bind uint8, type_ uint8) uint8 {
- return bind<<4 | type_&0xf
-}
-
-func ELF64_ST_VISIBILITY(oth uint8) uint8 {
- return oth & 3
-}
diff --git a/src/cmd/link/internal/ld/elf_test.go b/src/cmd/link/internal/ld/elf_test.go
index 37f0e77336..776fc1b4f9 100644
--- a/src/cmd/link/internal/ld/elf_test.go
+++ b/src/cmd/link/internal/ld/elf_test.go
@@ -14,6 +14,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
+ "strings"
"testing"
)
@@ -123,7 +124,7 @@ func TestNoDuplicateNeededEntries(t *testing.T) {
var count int
for _, lib := range libs {
- if lib == "libc.so" {
+ if lib == "libc.so" || strings.HasPrefix(lib, "libc.so.") {
count++
}
}
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
index b3541c46c0..fbc7a78d0e 100644
--- a/src/cmd/link/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -13,10 +13,13 @@ import (
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
+ "debug/elf"
"encoding/json"
"fmt"
"io"
"os"
+ "sort"
+ "strconv"
"strings"
)
@@ -288,6 +291,62 @@ func setCgoAttr(ctxt *Link, lookup func(string, int) loader.Sym, file string, pk
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) {
@@ -302,7 +361,7 @@ func adddynlib(ctxt *Link, lib string) {
dsu.Addstring("")
}
du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic)
- Elfwritedynent(ctxt.Arch, du, DT_NEEDED, uint64(dsu.Addstring(lib)))
+ Elfwritedynent(ctxt.Arch, du, elf.DT_NEEDED, uint64(dsu.Addstring(lib)))
} else {
Errorf(nil, "adddynlib: unsupported binary format")
}
@@ -384,7 +443,7 @@ func (ctxt *Link) addexport() {
for _, exp := range ctxt.dynexp {
Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, exp)
}
- for _, lib := range dynlib {
+ for _, lib := range dedupLibraries(ctxt, dynlib) {
adddynlib(ctxt, lib)
}
}
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 0000000000..0197196023
--- /dev/null
+++ b/src/cmd/link/internal/ld/go_test.go
@@ -0,0 +1,121 @@
+// 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"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "testing"
+)
+
+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, err := ioutil.TempDir("", "dedup-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ // 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 := ioutil.WriteFile(srcFile, []byte(src), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ exe := filepath.Join(dir, "deduped.exe")
+ out, err := exec.Command(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 = exec.Command(exe).CombinedOutput(); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/src/cmd/link/internal/ld/ld_test.go b/src/cmd/link/internal/ld/ld_test.go
index db339b484d..cdfaadb17d 100644
--- a/src/cmd/link/internal/ld/ld_test.go
+++ b/src/cmd/link/internal/ld/ld_test.go
@@ -5,6 +5,7 @@
package ld
import (
+ "debug/pe"
"fmt"
"internal/testenv"
"io/ioutil"
@@ -17,8 +18,13 @@ import (
)
func TestUndefinedRelocErrors(t *testing.T) {
- t.Parallel()
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()
dir, err := ioutil.TempDir("", "go-build")
if err != nil {
t.Fatal(err)
@@ -167,3 +173,72 @@ func TestPPC64LargeTextSectionSplitting(t *testing.T) {
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")
+ }
+
+ 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, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ srcfile := filepath.Join(dir, "test.go")
+ objfile := filepath.Join(dir, "test.dll")
+ if err := ioutil.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 := exec.Command(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")
+ }
+}
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 4295b2a660..e1cc7184de 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -247,12 +247,16 @@ type Arch struct {
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)
+ 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.
//
@@ -294,6 +298,11 @@ func (ctxt *Link) CanUsePlugins() bool {
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
@@ -543,7 +552,7 @@ func (ctxt *Link) loadlib() {
}
// Add non-package symbols and references of externally defined symbols.
- ctxt.loader.LoadNonpkgSyms(ctxt.Arch)
+ ctxt.loader.LoadSyms(ctxt.Arch)
// Load symbols from shared libraries, after all Go object symbols are loaded.
for _, lib := range ctxt.Library {
@@ -831,7 +840,12 @@ func (ctxt *Link) mangleTypeSym() {
ldr := ctxt.loader
for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
- if !ldr.AttrReachable(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)
@@ -1249,7 +1263,9 @@ func (ctxt *Link) hostlink() {
// -headerpad is incompatible with -fembed-bitcode.
argv = append(argv, "-Wl,-headerpad,1144")
}
- if ctxt.DynlinkingGo() && !ctxt.Arch.InFamily(sys.ARM, sys.ARM64) {
+ if ctxt.DynlinkingGo() && objabi.GOOS != "ios" {
+ // -flat_namespace is deprecated on iOS.
+ // It is useful for supporting plugins. We don't support plugins on iOS.
argv = append(argv, "-Wl,-flat_namespace")
}
if !combineDwarf {
@@ -1285,6 +1301,17 @@ func (ctxt *Link) hostlink() {
argv = append(argv, "-Wl,-bbigtoc")
}
+ // Enable ASLR on Windows.
+ addASLRargs := func(argv []string) []string {
+ // Enable ASLR.
+ argv = append(argv, "-Wl,--dynamicbase")
+ // enable high-entropy ASLR on 64-bit.
+ if ctxt.Arch.PtrSize >= 8 {
+ argv = append(argv, "-Wl,--high-entropy-va")
+ }
+ return argv
+ }
+
switch ctxt.BuildMode {
case BuildModeExe:
if ctxt.HeadType == objabi.Hdarwin {
@@ -1297,15 +1324,7 @@ func (ctxt *Link) hostlink() {
switch ctxt.HeadType {
case objabi.Hdarwin, objabi.Haix:
case objabi.Hwindows:
- // Enable ASLR.
- argv = append(argv, "-Wl,--dynamicbase")
- // enable high-entropy ASLR on 64-bit.
- if ctxt.Arch.PtrSize >= 8 {
- argv = append(argv, "-Wl,--high-entropy-va")
- }
- // Work around binutils limitation that strips relocation table for dynamicbase.
- // See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
- argv = append(argv, "-Wl,--export-all-symbols")
+ argv = addASLRargs(argv)
default:
// ELF.
if ctxt.UseRelro() {
@@ -1316,9 +1335,6 @@ func (ctxt *Link) hostlink() {
case BuildModeCShared:
if ctxt.HeadType == objabi.Hdarwin {
argv = append(argv, "-dynamiclib")
- if ctxt.Arch.Family != sys.AMD64 {
- argv = append(argv, "-Wl,-read_only_relocs,suppress")
- }
} else {
// ELF.
argv = append(argv, "-Wl,-Bsymbolic")
@@ -1326,7 +1342,11 @@ func (ctxt *Link) hostlink() {
argv = append(argv, "-Wl,-z,relro")
}
argv = append(argv, "-shared")
- if ctxt.HeadType != objabi.Hwindows {
+ if ctxt.HeadType == objabi.Hwindows {
+ if *flagAslr {
+ argv = addASLRargs(argv)
+ }
+ } else {
// Pass -z nodelete to mark the shared library as
// non-closeable: a dlclose will do nothing.
argv = append(argv, "-Wl,-z,nodelete")
@@ -1596,12 +1616,12 @@ func (ctxt *Link) hostlink() {
if combineDwarf {
dsym := filepath.Join(*flagTmpdir, "go.dwarf")
- if out, err := exec.Command("dsymutil", "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil {
+ if out, err := exec.Command("xcrun", "dsymutil", "-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("strip", "-S", *flagOutfile).CombinedOutput(); err != nil {
+ if out, err := exec.Command("xcrun", "strip", "-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.
@@ -1627,6 +1647,12 @@ func (ctxt *Link) hostlink() {
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
@@ -1752,12 +1778,12 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
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)
+ 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
+ ehdr.Flags = flags
ctxt.Textp = append(ctxt.Textp, textp...)
}
return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
@@ -2254,7 +2280,7 @@ func (sc *stkChk) check(up *chain, depth int) int {
var ch1 chain
pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
ri := 0
- for pcsp.Init(info.Pcsp()); !pcsp.Done; pcsp.Next() {
+ for pcsp.Init(ldr.Data(info.Pcsp())); !pcsp.Done; pcsp.Next() {
// pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
// Check stack size in effect for this span.
@@ -2502,16 +2528,22 @@ func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym,
if target.Arch.PtrSize == 8 {
rela := ldr.MakeSymbolUpdater(syms.Rela)
rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
- rela.AddUint64(target.Arch, ELF64_R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp))
+ 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, ELF32_R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp))
+ 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")
}
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index a2c8552e94..f26d051a49 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -71,7 +71,6 @@ type Link struct {
LibraryByPkg map[string]*sym.Library
Shlibs []Shlib
Textp []loader.Sym
- NumFilesyms int
Moduledata loader.Sym
PackageFile map[string]string
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index f6356729a6..4605644767 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -6,6 +6,7 @@ package ld
import (
"bytes"
+ "cmd/internal/codesign"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
@@ -17,6 +18,7 @@ import (
"os"
"sort"
"strings"
+ "unsafe"
)
type MachoHdr struct {
@@ -76,36 +78,38 @@ const (
)
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
- 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_ADDEND = 10
- MACHO_GENERIC_RELOC_VANILLA = 0
- MACHO_FAKE_GOTPCREL = 100
+ 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
+ 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 (
@@ -116,6 +120,8 @@ const (
MH_EXECUTE = 0x2
MH_NOUNDEFS = 0x1
+ MH_DYLDLINK = 0x4
+ MH_PIE = 0x200000
)
const (
@@ -191,6 +197,58 @@ const (
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
@@ -277,7 +335,7 @@ var dylib []string
var linkoff int64
-func machowrite(arch *sys.Arch, out *OutBuf, linkmode LinkMode) int {
+func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int {
o1 := out.Offset()
loadsize := 4 * 4 * ndebug
@@ -306,11 +364,14 @@ func machowrite(arch *sys.Arch, out *OutBuf, linkmode LinkMode) int {
}
out.Write32(uint32(len(load)) + uint32(nseg) + uint32(ndebug))
out.Write32(uint32(loadsize))
+ flags := uint32(0)
if nkind[SymKindUndef] == 0 {
- out.Write32(MH_NOUNDEFS) /* flags - no undefines */
- } else {
- out.Write32(0) /* flags */
+ 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 */
}
@@ -473,6 +534,18 @@ func (ctxt *Link) domacho() {
sb.SetReachable(true)
sb.AddUint8(0)
}
+
+ // Do not export C symbols dynamically in plugins, as runtime C symbols like crosscall2
+ // are in pclntab and end up pointing at the host binary, breaking unwinding.
+ // See Issue #18190.
+ if ctxt.BuildMode == BuildModePlugin {
+ for _, name := range []string{"_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2"} {
+ s := ctxt.loader.Lookup(name, 0)
+ if s != 0 {
+ ctxt.loader.SetAttrCgoExportDynamic(s, false)
+ }
+ }
+ }
}
func machoadddynlib(lib string, linkmode LinkMode) {
@@ -499,16 +572,7 @@ func machoadddynlib(lib string, linkmode LinkMode) {
func machoshbits(ctxt *Link, mseg *MachoSeg, sect *sym.Section, segname string) {
buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1)
- var msect *MachoSect
- if sect.Rwx&1 == 0 && segname != "__DWARF" && (ctxt.Arch.Family == sys.ARM64 ||
- (ctxt.Arch.Family == sys.AMD64 && ctxt.BuildMode != BuildModeExe)) {
- // Darwin external linker on arm64, and on amd64 in c-shared/c-archive buildmode
- // complains about absolute relocs in __TEXT, so if the section is not
- // executable, put it in __DATA segment.
- msect = newMachoSect(mseg, buf, "__DATA")
- } else {
- msect = newMachoSect(mseg, buf, segname)
- }
+ msect := newMachoSect(mseg, buf, segname)
if sect.Rellen > 0 {
msect.reloc = uint32(sect.Reloff)
@@ -583,6 +647,8 @@ func asmbMacho(ctxt *Link) {
}
ctxt.Out.SeekSet(0)
+ ldr := ctxt.loader
+
/* apple MACH */
va := *FlagTextAddr - int64(HEADR)
@@ -633,13 +699,28 @@ func asmbMacho(ctxt *Link) {
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 {
- w := int64(Segdata.Length)
ms = newMachoSeg("__DATA", 20)
- ms.vaddr = uint64(va) + uint64(v)
- ms.vsize = uint64(w)
- ms.fileoffset = uint64(v)
+ ms.vaddr = Segdata.Vaddr
+ ms.vsize = Segdata.Length
+ ms.fileoffset = Segdata.Fileoff
ms.filesize = Segdata.Filelen
ms.prot1 = 3
ms.prot2 = 3
@@ -676,40 +757,56 @@ func asmbMacho(ctxt *Link) {
ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32)
case sys.ARM64:
- ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 68+2)
- ml.data[0] = 6 /* thread type */
- ml.data[1] = 68 /* word count */
- ml.data[2+64] = uint32(Entryvalue(ctxt)) /* start pc */
- ml.data[2+64+1] = uint32(Entryvalue(ctxt) >> 32)
+ 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 {
- 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)
- s4 := ldr.SymSize(ldr.Lookup(".machosymstr", 0))
+ // 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(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(*FlagRound)))
- ms.vsize = uint64(s1) + uint64(s2) + uint64(s3) + uint64(s4)
+ 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 = 7
- ms.prot2 = 3
+ 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) /* symoff */
- ml.data[1] = uint32(nsortsym) /* nsyms */
- ml.data[2] = uint32(linkoff + s1 + s2 + s3) /* stroff */
- ml.data[3] = uint32(s4) /* strsize */
+ 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)
+ machodysymtab(ctxt, linkoff+s1+s2)
if ctxt.LinkMode != LinkExternal {
ml := newMachoLoad(ctxt.Arch, LC_LOAD_DYLINKER, 6)
@@ -725,12 +822,31 @@ func asmbMacho(ctxt *Link) {
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.Arch, ctxt.Out, ctxt.LinkMode)
+ 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 {
@@ -812,12 +928,10 @@ func collectmachosyms(ctxt *Link) {
switch objabi.GOARCH {
case "amd64":
ldr.SetSymExtname(s, n+"$INODE64")
- case "386":
- ldr.SetSymExtname(s, n+"$INODE64$UNIX2003")
}
case "readdir_r", "getfsstat":
switch objabi.GOARCH {
- case "amd64", "386":
+ case "amd64":
ldr.SetSymExtname(s, n+"$INODE64")
}
}
@@ -891,19 +1005,12 @@ func machosymtab(ctxt *Link) {
symtab.AddUint32(ctxt.Arch, uint32(symstr.Size()))
export := machoShouldExport(ctxt, ldr, s)
- isGoSymbol := strings.Contains(ldr.SymExtname(s), ".")
- // In normal buildmodes, only add _ to C symbols, as
- // Go symbols have dot in the name.
- //
- // Do not export C symbols in plugins, as runtime C
- // symbols like crosscall2 are in pclntab and end up
- // pointing at the host binary, breaking unwinding.
- // See Issue #18190.
- cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(ldr.SymName(s)))
- if cexport || export || isGoSymbol {
- symstr.AddUint8('_')
- }
+ // 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.
symstr.Addstring(strings.Replace(ldr.SymExtname(s), "·", ".", -1))
@@ -914,10 +1021,13 @@ func machosymtab(ctxt *Link) {
symtab.AddUint16(ctxt.Arch, 0) // desc
symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value
} else {
- if ldr.AttrCgoExport(s) || export {
- symtab.AddUint8(0x0f)
+ 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)
+ symtab.AddUint8(0x0e) // N_SECT
}
o := s
if outer := ldr.OuterSym(o); outer != 0 {
@@ -935,7 +1045,7 @@ func machosymtab(ctxt *Link) {
}
}
-func machodysymtab(ctxt *Link) {
+func machodysymtab(ctxt *Link, base int64) {
ml := newMachoLoad(ctxt.Arch, LC_DYSYMTAB, 18)
n := 0
@@ -963,7 +1073,7 @@ func machodysymtab(ctxt *Link) {
s1 := ldr.SymSize(ldr.Lookup(".machosymtab", 0))
s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT)
s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT)
- ml.data[12] = uint32(linkoff + s1) /* indirectsymoff */
+ ml.data[12] = uint32(base + s1) /* indirectsymoff */
ml.data[13] = uint32((s2 + s3) / 4) /* nindirectsyms */
ml.data[14] = 0 /* extreloff */
@@ -974,14 +1084,19 @@ func machodysymtab(ctxt *Link) {
func doMachoLink(ctxt *Link) int64 {
machosymtab(ctxt)
+ machoDyldInfo(ctxt)
ldr := ctxt.loader
// write data that will be linkedit section
- s1 := ldr.Lookup(".machosymtab", 0)
- s2 := ctxt.ArchSyms.LinkEditPLT
- s3 := ctxt.ArchSyms.LinkEditGOT
- s4 := ldr.Lookup(".machosymstr", 0)
+ 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
@@ -1000,24 +1115,31 @@ func doMachoLink(ctxt *Link) int64 {
// boundary, codesign_allocate will not need to apply
// any alignment padding itself, working around the
// issue.
- s4b := ldr.MakeSymbolUpdater(s4)
- for s4b.Size()%16 != 0 {
- s4b.AddUint8(0)
+ if size%16 != 0 {
+ n := 16 - size%16
+ s6b := ldr.MakeSymbolUpdater(s6)
+ s6b.Grow(s6b.Size() + n)
+ s6b.SetSize(s6b.Size() + n)
+ size += n
}
- size := int(ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4))
-
if size > 0 {
- linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))
+ 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(int64(size), int64(*FlagRound))
+ return Rnd(size, int64(*FlagRound))
}
func machorelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) {
@@ -1086,6 +1208,9 @@ func machoEmitReloc(ctxt *Link) {
for _, sect := range Segtext.Sections[1:] {
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)
}
@@ -1155,3 +1280,240 @@ func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) {
}
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/main.go b/src/cmd/link/internal/ld/main.go
index 6f4ccbfb7a..5c8293810f 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -65,6 +65,7 @@ var (
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")
+ 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")
@@ -157,11 +158,16 @@ func Main(arch *sys.Arch, theArch Arch) {
ctxt.HeadType.Set(objabi.GOOS)
}
+ if !*flagAslr && ctxt.BuildMode != BuildModeCShared {
+ Errorf(nil, "-aslr=false is only allowed for -buildmode=c-shared")
+ usage()
+ }
+
checkStrictDups = *FlagStrictDups
startProfile()
if ctxt.BuildMode == BuildModeUnset {
- ctxt.BuildMode = BuildModeExe
+ ctxt.BuildMode.Set("exe")
}
if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 {
@@ -323,10 +329,14 @@ func Main(arch *sys.Arch, theArch Arch) {
// will be applied directly there.
bench.Start("Asmb")
asmb(ctxt)
- // Generate large symbols.
- for s, f := range ctxt.generatorSyms {
- f(ctxt, s)
+
+ // 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)
diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go
index d696a68088..530836ef7c 100644
--- a/src/cmd/link/internal/ld/outbuf.go
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -13,11 +13,10 @@ import (
"os"
)
-// If fallocate is not supported on this platform, return this error.
-// Note this is the same error returned by filesystems that don't support
-// fallocate, and that is intentional. The error is ignored where needed, and
-// OutBuf writes to heap memory.
-const fallocateNotSupportedErr = "operation not supported"
+// 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.
@@ -114,6 +113,7 @@ func (out *OutBuf) Close() error {
}
if out.isMmapped() {
out.copyHeap()
+ out.purgeSignatureCache()
out.munmap()
}
if out.f == nil {
@@ -136,6 +136,15 @@ 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 {
@@ -184,7 +193,9 @@ func (out *OutBuf) writeLoc(lenToWrite int64) (int64, []byte) {
// 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 = 0, 0, lenToWrite
+ heapPos -= heapLen
+ lenNeeded = heapPos + lenToWrite
+ heapLen = 0
}
out.heap = append(out.heap, make([]byte, lenNeeded-heapLen)...)
}
diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go
index d7e3372230..9444b6567e 100644
--- a/src/cmd/link/internal/ld/outbuf_darwin.go
+++ b/src/cmd/link/internal/ld/outbuf_darwin.go
@@ -36,3 +36,12 @@ func (out *OutBuf) fallocate(size uint64) error {
return nil
}
+
+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.
+ syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(len(out.buf)), syscall.MS_INVALIDATE)
+ // Best effort. Ignore error.
+}
diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go
index 53b14b09cc..807fe24375 100644
--- a/src/cmd/link/internal/ld/outbuf_mmap.go
+++ b/src/cmd/link/internal/ld/outbuf_mmap.go
@@ -28,7 +28,7 @@ func (out *OutBuf) Mmap(filesize uint64) (err error) {
// 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.Error() != fallocateNotSupportedErr {
+ if err != syscall.ENOTSUP && err != syscall.EPERM && err != errNoFallocate {
return err
}
}
diff --git a/src/cmd/link/internal/ld/outbuf_nofallocate.go b/src/cmd/link/internal/ld/outbuf_nofallocate.go
index 51b4fe7aff..6bf96bcb2b 100644
--- a/src/cmd/link/internal/ld/outbuf_nofallocate.go
+++ b/src/cmd/link/internal/ld/outbuf_nofallocate.go
@@ -6,8 +6,6 @@
package ld
-import "errors"
-
func (out *OutBuf) fallocate(size uint64) error {
- return errors.New(fallocateNotSupportedErr)
+ return errNoFallocate
}
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 0000000000..8c5666f216
--- /dev/null
+++ b/src/cmd/link/internal/ld/outbuf_notdarwin.go
@@ -0,0 +1,9 @@
+// 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.
+
+// +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
index db0a92485e..e6643da396 100644
--- a/src/cmd/link/internal/ld/outbuf_test.go
+++ b/src/cmd/link/internal/ld/outbuf_test.go
@@ -17,7 +17,7 @@ func TestMMap(t *testing.T) {
switch runtime.GOOS {
default:
t.Skip("unsupported OS")
- case "aix", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "windows":
+ case "aix", "darwin", "ios", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "windows":
}
dir, err := ioutil.TempDir("", "TestMMap")
if err != nil {
diff --git a/src/cmd/link/internal/ld/outbuf_windows.go b/src/cmd/link/internal/ld/outbuf_windows.go
index 60dc1ab92d..915c72bef3 100644
--- a/src/cmd/link/internal/ld/outbuf_windows.go
+++ b/src/cmd/link/internal/ld/outbuf_windows.go
@@ -35,7 +35,10 @@ func (out *OutBuf) Mmap(filesize uint64) error {
if err != nil {
return err
}
- *(*reflect.SliceHeader)(unsafe.Pointer(&out.buf)) = reflect.SliceHeader{Data: ptr, Len: int(filesize), Cap: int(filesize)}
+ bufHdr := (*reflect.SliceHeader)(unsafe.Pointer(&out.buf))
+ bufHdr.Data = ptr
+ bufHdr.Len = int(filesize)
+ bufHdr.Cap = int(filesize)
// copy heap to new mapping
if uint64(oldlen+len(out.heap)) > filesize {
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 30e0bdc839..facb30fe15 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -6,45 +6,23 @@ package ld
import (
"cmd/internal/goobj"
- "cmd/internal/obj"
"cmd/internal/objabi"
- "cmd/internal/src"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
- "encoding/binary"
"fmt"
- "log"
- "math"
"os"
"path/filepath"
- "strings"
)
-// oldPclnState holds state information used during pclntab generation. Here
-// 'ldr' is just a pointer to the context's loader, 'deferReturnSym' is the
-// index for the symbol "runtime.deferreturn", 'nameToOffset' is a helper
-// function for capturing function names, 'numberedFiles' records the file
-// number assigned to a given file symbol, 'filepaths' is a slice of expanded
-// paths (indexed by file number).
-//
-// NB: This is deprecated, and will be eliminated when pclntab_old is
-// eliminated.
-type oldPclnState struct {
- ldr *loader.Loader
- deferReturnSym loader.Sym
- numberedFiles map[string]int64
- filepaths []string
-}
-
// pclntab holds the state needed for pclntab generation.
type pclntab struct {
+ // The size of the func object in the runtime.
+ funcSize uint32
+
// The first and last functions found.
firstFunc, lastFunc loader.Sym
- // The offset to the filetab.
- filetabOffset int32
-
// Running total size of pclntab.
size int64
@@ -54,6 +32,9 @@ type pclntab struct {
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
@@ -64,11 +45,8 @@ type pclntab struct {
// On most platforms this is the number of reachable functions.
nfunc int32
- // maps the function symbol to offset in runtime.funcnametab
- // This doesn't need to reside in the state once pclntab_old's been
- // deleted -- it can live in generateFuncnametab.
- // TODO(jfaller): Delete me!
- funcNameOffset map[loader.Sym]int32
+ // The number of filenames in runtime.filetab.
+ nfiles uint32
}
// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym.
@@ -83,40 +61,29 @@ func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f gen
return s
}
-func makeOldPclnState(ctxt *Link) *oldPclnState {
- ldr := ctxt.loader
- drs := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
- state := &oldPclnState{
- ldr: ldr,
- deferReturnSym: drs,
- numberedFiles: make(map[string]int64),
- // NB: initial entry in filepaths below is to reserve the zero value,
- // so that when we do a map lookup in numberedFiles fails, it will not
- // return a value slot in filepaths.
- filepaths: []string{""},
- }
-
- return state
-}
-
// makePclntab makes a pclntab object, and assembles all the compilation units
-// we'll need to write pclntab.
-func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit) {
+// 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 := &pclntab{
- funcNameOffset: make(map[loader.Sym]int32),
+ // This is the size of the _func object in runtime/runtime2.go.
+ funcSize: uint32(ctxt.Arch.PtrSize + 9*4),
}
// Gather some basic stats and info.
seenCUs := make(map[*sym.CompilationUnit]struct{})
prevSect := ldr.SymSect(ctxt.Textp[0])
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
@@ -142,116 +109,22 @@ func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.Compilat
compUnits = append(compUnits, cu)
}
}
- return state, compUnits
-}
-
-func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 {
- start := len(ftab.Data())
- ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL
- ftd := ftab.Data()
- copy(ftd[start:], s)
- return int32(start)
-}
-
-// numberfile assigns a file number to the file if it hasn't been assigned already.
-// This funciton looks at a CU's file at index [i], and if it's a new filename,
-// stores that filename in the global file table, and adds it to the map lookup
-// for renumbering pcfile.
-func (state *oldPclnState) numberfile(cu *sym.CompilationUnit, i goobj.CUFileIndex) int64 {
- file := cu.FileTable[i]
- if val, ok := state.numberedFiles[file]; ok {
- return val
- }
- path := file
- if strings.HasPrefix(path, src.FileSymPrefix) {
- path = file[len(src.FileSymPrefix):]
- }
- val := int64(len(state.filepaths))
- state.numberedFiles[file] = val
- state.filepaths = append(state.filepaths, expandGoroot(path))
- return val
-}
-
-func (state *oldPclnState) fileVal(cu *sym.CompilationUnit, i int32) int64 {
- file := cu.FileTable[i]
- if val, ok := state.numberedFiles[file]; ok {
- return val
- }
- panic("should have been numbered first")
-}
-
-func (state *oldPclnState) renumberfiles(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, d *sym.Pcdata) {
- // Give files numbers.
- nf := fi.NumFile()
- for i := uint32(0); i < nf; i++ {
- state.numberfile(cu, fi.File(int(i)))
- }
-
- buf := make([]byte, binary.MaxVarintLen32)
- newval := int32(-1)
- var out sym.Pcdata
- it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
- for it.Init(d.P); !it.Done; it.Next() {
- // value delta
- oldval := it.Value
-
- var val int32
- if oldval == -1 {
- val = -1
- } else {
- if oldval < 0 || oldval >= int32(len(cu.FileTable)) {
- log.Fatalf("bad pcdata %d", oldval)
- }
- val = int32(state.fileVal(cu, oldval))
- }
-
- dv := val - newval
- newval = val
-
- // value
- n := binary.PutVarint(buf, int64(dv))
- out.P = append(out.P, buf[:n]...)
-
- // pc delta
- pc := (it.NextPC - it.PC) / it.PCScale
- n = binary.PutUvarint(buf, uint64(pc))
- out.P = append(out.P, buf[:n]...)
- }
-
- // terminating value delta
- // we want to write varint-encoded 0, which is just 0
- out.P = append(out.P, 0)
-
- *d = out
-}
-
-// onlycsymbol looks at a symbol's name to report whether this is a
-// symbol that is referenced by C code
-func onlycsymbol(sname string) bool {
- switch sname {
- case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2":
- return true
- }
- if strings.HasPrefix(sname, "_cgoexp_") {
- return true
- }
- return false
+ return state, compUnits, funcs
}
func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
- if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(ctxt.loader.SymName(s)) {
- return false
- }
// We want to generate func table entries only for the "lowest
// level" symbols, not containers of subsymbols.
return !container.Has(s)
}
-func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 {
+func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 {
+ ldr := ctxt.loader
+ target := ctxt.Target
deferreturn := uint32(0)
lastWasmAddr := uint32(0)
- relocs := state.ldr.Relocs(s)
+ relocs := ldr.Relocs(s)
for ri := 0; ri < relocs.Count(); ri++ {
r := relocs.At(ri)
if target.IsWasm() && r.Type() == objabi.R_ADDR {
@@ -262,7 +135,7 @@ func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint
// set the resumption point to PC_B.
lastWasmAddr = uint32(r.Add())
}
- if r.Type().IsDirectCall() && (r.Sym() == state.deferReturnSym || state.ldr.IsDeferReturnTramp(r.Sym())) {
+ if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) {
if target.IsWasm() {
deferreturn = lastWasmAddr - 1
} else {
@@ -295,8 +168,8 @@ func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint
// genInlTreeSym generates the InlTree sym for a function with the
// specified FuncInfo.
-func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, newState *pclntab) loader.Sym {
- ldr := state.ldr
+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
@@ -308,13 +181,8 @@ func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.Func
ninl := fi.NumInlTree()
for i := 0; i < int(ninl); i++ {
call := fi.InlTree(i)
- // Usually, call.File is already numbered since the file
- // shows up in the Pcfile table. However, two inlined calls
- // might overlap exactly so that only the innermost file
- // appears in the Pcfile table. In that case, this assigns
- // the outer file a number.
- val := state.numberfile(cu, call.File)
- nameoff, ok := newState.funcNameOffset[call.Func]
+ val := call.File
+ nameoff, ok := nameOffsets[call.Func]
if !ok {
panic("couldn't find function name offset")
}
@@ -337,6 +205,22 @@ func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.Func
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) {
@@ -359,24 +243,24 @@ func (state *pclntab) generatePCHeader(ctxt *Link) {
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))
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)
}
- size := int64(8 + 3*ctxt.Arch.PtrSize)
+ size := int64(8 + 7*ctxt.Arch.PtrSize)
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
}
-// walkFuncs iterates over the Textp, calling a function for each unique
+// walkFuncs iterates over the funcs, calling a function for each unique
// function and inlined function.
-func (state *pclntab) walkFuncs(ctxt *Link, container loader.Bitmap, f func(loader.Sym)) {
+func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) {
ldr := ctxt.loader
seen := make(map[loader.Sym]struct{})
- for _, ls := range ctxt.Textp {
- s := loader.Sym(ls)
- if !emitPcln(ctxt, s, container) {
- continue
- }
+ for _, s := range funcs {
if _, ok := seen[s]; !ok {
f(s)
seen[s] = struct{}{}
@@ -397,207 +281,484 @@ func (state *pclntab) walkFuncs(ctxt *Link, container loader.Bitmap, f func(load
}
}
-// generateFuncnametab creates the function name table.
-func (state *pclntab) generateFuncnametab(ctxt *Link, container loader.Bitmap) {
+// 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)
+
// Write the null terminated strings.
writeFuncNameTab := func(ctxt *Link, s loader.Sym) {
symtab := ctxt.loader.MakeSymbolUpdater(s)
- for s, off := range state.funcNameOffset {
+ for s, off := range nameOffsets {
symtab.AddStringAt(int64(off), ctxt.loader.SymName(s))
}
}
// Loop through the CUs, and calculate the size needed.
var size int64
- state.walkFuncs(ctxt, container, func(s loader.Sym) {
- state.funcNameOffset[s] = int32(size)
+ walkFuncs(ctxt, funcs, func(s loader.Sym) {
+ nameOffsets[s] = uint32(size)
size += int64(ctxt.loader.SymNameLen(s)) + 1 // NULL terminate
})
state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
+ return nameOffsets
}
-// pclntab initializes the pclntab symbol with
-// runtime function and file name information.
+// 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
-// 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 July 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
+ // 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.
//
- // runtime.funcnametab
- // []list of null terminated function names
+ // 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.
//
- // runtime.pclntab_old
- // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
- // end PC [thearch.ptrsize bytes]
- // offset to file table [4 bytes]
- // func structures, pcdata tables.
- // filetable
+ // TOOD: 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)
- oldState := makeOldPclnState(ctxt)
- state, _ := makePclntab(ctxt, container)
+ // 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
+ }
- ldr := ctxt.loader
- state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
- ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
- ldr.SetAttrReachable(state.carrier, true)
+ // Find the maximum file index we've seen.
+ if cuEntries[cu.PclnIndex] < i+1 {
+ cuEntries[cu.PclnIndex] = i + 1 // Store max + 1
+ }
+ })
- // runtime.pclntab_old is just a placeholder,and will eventually be deleted.
- // It contains the pieces of runtime.pclntab that haven't moved to a more
- // rational form.
- state.pclntab = ldr.LookupOrCreateSym("runtime.pclntab_old", 0)
- state.generatePCHeader(ctxt)
- state.generateFuncnametab(ctxt, container)
+ // 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)
- funcdataBytes := int64(0)
- ldr.SetCarrierSym(state.pclntab, state.carrier)
- ldr.SetAttrNotInSymbolTable(state.pclntab, true)
- ftab := ldr.MakeSymbolUpdater(state.pclntab)
- ftab.SetValue(state.size)
- ftab.SetType(sym.SPCLNTAB)
- ftab.SetReachable(true)
+ 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)
- ftab.Grow(int64(state.nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
+ // Write filetab.
+ writeFiletab := func(ctxt *Link, s loader.Sym) {
+ sb := ctxt.loader.MakeSymbolUpdater(s)
- szHint := len(ctxt.Textp) * 2
- pctaboff := make(map[string]uint32, szHint)
- writepctab := func(off int32, p []byte) int32 {
- start, ok := pctaboff[string(p)]
- if !ok {
- if len(p) > 0 {
- start = uint32(len(ftab.Data()))
- ftab.AddBytes(p)
+ // 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)
}
- pctaboff[string(p)] = start
+ size += datSize
+ seen[pcSym] = struct{}{}
+ }
+ }
+ for _, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+
+ pcSyms := []loader.Sym{fi.Pcsp(), fi.Pcfile(), fi.Pcline()}
+ for _, pcSym := range pcSyms {
+ saveOffset(pcSym)
+ }
+ for _, pcSym := range fi.Pcdata() {
+ saveOffset(pcSym)
+ }
+ if fi.NumInlTree() > 0 {
+ saveOffset(fi.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(fi loader.FuncInfo) uint32 {
+ if !fi.Valid() {
+ return 0
+ }
+ numPCData := uint32(len(fi.Pcdata()))
+ if fi.NumInlTree() > 0 {
+ if numPCData < objabi.PCDATA_InlTreeIndex+1 {
+ numPCData = objabi.PCDATA_InlTreeIndex + 1
}
- newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start))
- return newoff
}
+ return numPCData
+}
+
+// Helper types for iterating pclntab.
+type pclnSetAddr func(*loader.SymbolBuilder, *sys.Arch, int64, loader.Sym, int64) int64
+type pclnSetUint func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64
- setAddr := (*loader.SymbolBuilder).SetAddrPlus
- if ctxt.IsExe() && ctxt.IsInternal() {
- // Internal linking static executable. At this point the function
- // addresses are known, so we can just use them instead of emitting
- // relocations.
- // For other cases we are generating a relocatable binary so we
- // still need to emit relocations.
- setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
- if v := ldr.SymValue(tgt); v != 0 {
- return s.SetUint(arch, off, uint64(v+add))
+// generateFunctab creates the runtime.functab
+//
+// runtime.functab contains two things:
+//
+// - pc->func look up table.
+// - array of func objects, interleaved with pcdata and funcdata
+//
+// Because of timing in the linker, generating this table takes two passes.
+// The first pass is executed early in the link, and it creates any needed
+// relocations to layout the data. The pieces that need relocations are:
+// 1) the PC->func table.
+// 2) The entry points in the func objects.
+// 3) The funcdata.
+// (1) and (2) are handled in walkPCToFunc. (3) is handled in walkFuncdata.
+//
+// After relocations, once we know where to write things in the output buffer,
+// we execute the second pass, which is actually writing the data.
+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)
+
+ // If we are internally linking a static executable, the function addresses
+ // are known, so we can just use them instead of emitting relocations. For
+ // other cases we still need to emit relocations.
+ //
+ // This boolean just helps us figure out which callback to use.
+ useSymValue := ctxt.IsExe() && ctxt.IsInternal()
+
+ writePcln := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(s)
+
+ // Create our callbacks.
+ var setAddr pclnSetAddr
+ if useSymValue {
+ // We need to write the offset.
+ setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
+ if v := ldr.SymValue(tgt); v != 0 {
+ s.SetUint(arch, off, uint64(v+add))
+ }
+ return 0
}
- return s.SetAddrPlus(arch, off, tgt, add)
+ } else {
+ // We already wrote relocations.
+ setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 { return 0 }
}
+
+ // Write the data.
+ writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, (*loader.SymbolBuilder).SetUint)
+ writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets)
+ state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, (*loader.SymbolBuilder).SetUint)
}
- pcsp := sym.Pcdata{}
- pcfile := sym.Pcdata{}
- pcline := sym.Pcdata{}
- pcdata := []sym.Pcdata{}
- funcdata := []loader.Sym{}
- funcdataoff := []int64{}
+ state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln)
- var nfunc int32
- prevFunc := ctxt.Textp[0]
- for _, s := range ctxt.Textp {
- if !emitPcln(ctxt, s, container) {
- continue
+ // Create the relocations we need.
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(state.pclntab)
+
+ var setAddr pclnSetAddr
+ if useSymValue {
+ // If we should use the symbol value, and we don't have one, write a relocation.
+ setAddr = func(sb *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
+ if v := ldr.SymValue(tgt); v == 0 {
+ sb.SetAddrPlus(arch, off, tgt, add)
+ }
+ return 0
}
+ } else {
+ // If we're externally linking, write a relocation.
+ setAddr = (*loader.SymbolBuilder).SetAddrPlus
+ }
+ setUintNOP := func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64 { return 0 }
+ writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, setUintNOP)
+ if !useSymValue {
+ // Generate relocations for funcdata when externally linking.
+ state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, setUintNOP)
+ }
+}
- thisSect := ldr.SymSect(s)
- prevSect := ldr.SymSect(prevFunc)
- if thisSect != prevSect {
- // With multiple text sections, there may be a hole here
- // in the address space (see the comment above). We use an
- // invalid funcoff value to mark the hole. See also
- // runtime/symtab.go:findfunc
- prevFuncSize := int64(ldr.SymSize(prevFunc))
- setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
- ftab.SetUint(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0))
- nfunc++
+// funcData returns the funcdata and offsets for the FuncInfo.
+// The funcdata and offsets are written into runtime.functab after each func
+// object. This is a helper function to make querying the FuncInfo object
+// cleaner.
+//
+// Note, the majority of fdOffsets are 0, meaning there is no offset between
+// the compiler's generated symbol, and what the runtime needs. They are
+// plumbed through for no loss of generality.
+//
+// NB: Preload must be called on the FuncInfo before calling.
+// NB: fdSyms and fdOffs are used as scratch space.
+func funcData(fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym, fdOffs []int64) ([]loader.Sym, []int64) {
+ fdSyms, fdOffs = fdSyms[:0], fdOffs[:0]
+ if fi.Valid() {
+ numOffsets := int(fi.NumFuncdataoff())
+ for i := 0; i < numOffsets; i++ {
+ fdOffs = append(fdOffs, fi.Funcdataoff(i))
}
- prevFunc = s
+ fdSyms = fi.Funcdata(fdSyms)
+ if fi.NumInlTree() > 0 {
+ if len(fdSyms) < objabi.FUNCDATA_InlTree+1 {
+ fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...)
+ fdOffs = append(fdOffs, make([]int64, objabi.FUNCDATA_InlTree+1-len(fdOffs))...)
+ }
+ fdSyms[objabi.FUNCDATA_InlTree] = inlSym
+ }
+ }
+ return fdSyms, fdOffs
+}
+
+// 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
+ // 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*ctxt.Arch.PtrSize + ctxt.Arch.PtrSize)
- pcsp.P = pcsp.P[:0]
- pcline.P = pcline.P[:0]
- pcfile.P = pcfile.P[:0]
- pcdata = pcdata[:0]
- funcdataoff = funcdataoff[:0]
- funcdata = funcdata[:0]
+ // Now find the space for the func objects. We do this in a running manner,
+ // so that we can find individual starting locations, and because funcdata
+ // requires alignment.
+ for i, s := range funcs {
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
+ startLocations[i] = uint32(size)
fi := ldr.FuncInfo(s)
+ size += int64(state.funcSize)
if fi.Valid() {
fi.Preload()
- npc := fi.NumPcdata()
- for i := uint32(0); i < npc; i++ {
- pcdata = append(pcdata, sym.Pcdata{P: fi.Pcdata(int(i))})
+ numFuncData := int(fi.NumFuncdataoff())
+ if fi.NumInlTree() > 0 {
+ if numFuncData < objabi.FUNCDATA_InlTree+1 {
+ numFuncData = objabi.FUNCDATA_InlTree + 1
+ }
}
- nfd := fi.NumFuncdataoff()
- for i := uint32(0); i < nfd; i++ {
- funcdataoff = append(funcdataoff, fi.Funcdataoff(int(i)))
+ size += int64(numPCData(fi) * 4)
+ if numFuncData > 0 { // Func data is aligned.
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
}
- funcdata = fi.Funcdata(funcdata)
+ size += int64(numFuncData * ctxt.Arch.PtrSize)
}
+ }
- if fi.Valid() && fi.NumInlTree() > 0 {
-
- if len(pcdata) <= objabi.PCDATA_InlTreeIndex {
- // Create inlining pcdata table.
- newpcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
- copy(newpcdata, pcdata)
- pcdata = newpcdata
- }
+ return size, startLocations
+}
- if len(funcdataoff) <= objabi.FUNCDATA_InlTree {
- // Create inline tree funcdata.
- newfuncdata := make([]loader.Sym, objabi.FUNCDATA_InlTree+1)
- newfuncdataoff := make([]int64, objabi.FUNCDATA_InlTree+1)
- copy(newfuncdata, funcdata)
- copy(newfuncdataoff, funcdataoff)
- funcdata = newfuncdata
- funcdataoff = newfuncdataoff
- }
+// writePcToFunc writes the PC->func lookup table.
+// This function walks the pc->func lookup table, executing callbacks
+// to generate relocations and writing the values for the table.
+func writePcToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) {
+ ldr := ctxt.loader
+ var prevFunc loader.Sym
+ prevSect := ldr.SymSect(funcs[0])
+ funcIndex := 0
+ for i, s := range funcs {
+ if thisSect := ldr.SymSect(s); thisSect != prevSect {
+ // With multiple text sections, there may be a hole here in the
+ // address space. We use an invalid funcoff value to mark the hole.
+ // See also runtime/symtab.go:findfunc
+ prevFuncSize := int64(ldr.SymSize(prevFunc))
+ setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
+ setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), ^uint64(0))
+ funcIndex++
+ prevSect = thisSect
}
+ prevFunc = s
+ // TODO: We don't actually need these relocations, provided we go to a
+ // module->func look-up-table like we do for filenames. We could have a
+ // single relocation for the module, and have them all laid out as
+ // offsets from the beginning of that module.
+ setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), s, 0)
+ setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), uint64(startLocations[i]))
+ funcIndex++
- dSize := len(ftab.Data())
- funcstart := int32(dSize)
- funcstart += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize
+ // Write the entry location.
+ setAddr(sb, ctxt.Arch, int64(startLocations[i]), s, 0)
+ }
- setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s, 0)
- ftab.SetUint(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart))
+ // Final entry of table is just end pc.
+ setAddr(sb, ctxt.Arch, int64(funcIndex)*2*int64(ctxt.Arch.PtrSize), prevFunc, ldr.SymSize(prevFunc))
+}
- // Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
- // and package debug/gosym.
+// writeFuncData writes the funcdata tables.
+//
+// This function executes a callback for each funcdata needed in
+// runtime.functab. It should be called once for internally linked static
+// binaries, or twice (once to generate the needed relocations) for other
+// build modes.
+//
+// Note the output of this function is interwoven with writeFuncs, but this is
+// a separate function, because it's needed in different passes in
+// generateFunctab.
+func (state *pclntab) writeFuncData(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) {
+ ldr := ctxt.loader
+ funcdata, funcdataoff := []loader.Sym{}, []int64{}
+ for i, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
- // fixed size of struct, checked below
- off := funcstart
+ // funcdata, must be pointer-aligned and we're only int32-aligned.
+ // Missing funcdata will be 0 (nil pointer).
+ funcdata, funcdataoff := funcData(fi, inlSyms[s], funcdata, funcdataoff)
+ if len(funcdata) > 0 {
+ off := int64(startLocations[i] + state.funcSize + numPCData(fi)*4)
+ off = Rnd(off, int64(ctxt.Arch.PtrSize))
+ for j := range funcdata {
+ dataoff := off + int64(ctxt.Arch.PtrSize*j)
+ if funcdata[j] == 0 {
+ setUint(sb, ctxt.Arch, dataoff, uint64(funcdataoff[j]))
+ continue
+ }
+ // TODO: Does this need deduping?
+ setAddr(sb, ctxt.Arch, dataoff, funcdata[j], funcdataoff[j])
+ }
+ }
+ }
+}
+
+// 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", sym.SymVerABIInternal)
+ funcdata, funcdataoff := []loader.Sym{}, []int64{}
- end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcdata))*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize)
- if len(funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
- end += 4
+ // Write the individual func objects.
+ for i, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if fi.Valid() {
+ fi.Preload()
}
- ftab.Grow(int64(end))
- // entry uintptr
- off = int32(setAddr(ftab, ctxt.Arch, int64(off), s, 0))
+ // Note we skip the space for the entry value -- that's handled inn
+ // walkPCToFunc. We don't write it here, because it might require a
+ // relocation.
+ off := startLocations[i] + uint32(ctxt.Arch.PtrSize) // entry
// name int32
- nameoff, ok := state.funcNameOffset[s]
+ nameoff, ok := nameOffsets[s]
if !ok {
panic("couldn't find function name offset")
}
- off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
// args int32
// TODO: Move into funcinfo.
@@ -605,118 +766,106 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
if fi.Valid() {
args = uint32(fi.Args())
}
- off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), args))
// deferreturn
- deferreturn := oldState.computeDeferReturn(&ctxt.Target, s)
- off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
+ deferreturn := computeDeferReturn(ctxt, deferReturnSym, s)
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), deferreturn))
- cu := ldr.SymUnit(s)
+ // pcdata
if fi.Valid() {
- pcsp = sym.Pcdata{P: fi.Pcsp()}
- pcfile = sym.Pcdata{P: fi.Pcfile()}
- pcline = sym.Pcdata{P: fi.Pcline()}
- oldState.renumberfiles(ctxt, cu, fi, &pcfile)
- if false {
- // Sanity check the new numbering
- it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
- for it.Init(pcfile.P); !it.Done; it.Next() {
- if it.Value < 1 || it.Value > int32(len(oldState.numberedFiles)) {
- ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(oldState.numberedFiles))
- errorexit()
- }
- }
- }
- }
-
- if fi.Valid() && fi.NumInlTree() > 0 {
- its := oldState.genInlTreeSym(cu, fi, ctxt.Arch, state)
- funcdata[objabi.FUNCDATA_InlTree] = its
- pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()}
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcsp()))))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcfile()))))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcline()))))
+ } else {
+ off += 12
}
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(numPCData(fi))))
- // pcdata
- off = writepctab(off, pcsp.P)
- off = writepctab(off, pcfile.P)
- off = writepctab(off, pcline.P)
- off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcdata))))
-
- // Store the compilation unit index.
- cuIdx := ^uint16(0)
+ // Store the offset to compilation unit's file table.
+ cuIdx := ^uint32(0)
if cu := ldr.SymUnit(s); cu != nil {
- if cu.PclnIndex > math.MaxUint16 {
- panic("cu limit reached.")
- }
- cuIdx = uint16(cu.PclnIndex)
+ cuIdx = cuOffsets[cu.PclnIndex]
}
- off = int32(ftab.SetUint16(ctxt.Arch, int64(off), cuIdx))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), cuIdx))
// funcID uint8
var funcID objabi.FuncID
if fi.Valid() {
funcID = fi.FuncID()
}
- off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
+ off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
+
+ off += 2 // pad
// nfuncdata must be the final entry.
- off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
- for i := range pcdata {
- off = writepctab(off, pcdata[i].P)
- }
+ funcdata, funcdataoff = funcData(fi, 0, funcdata, funcdataoff)
+ off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
- // funcdata, must be pointer-aligned and we're only int32-aligned.
- // Missing funcdata will be 0 (nil pointer).
- if len(funcdata) > 0 {
- if off&int32(ctxt.Arch.PtrSize-1) != 0 {
- off += 4
+ // Output the pcdata.
+ if fi.Valid() {
+ for j, pcSym := range fi.Pcdata() {
+ sb.SetUint32(ctxt.Arch, int64(off+uint32(j*4)), uint32(ldr.SymValue(pcSym)))
}
- for i := range funcdata {
- dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i)
- if funcdata[i] == 0 {
- ftab.SetUint(ctxt.Arch, dataoff, uint64(funcdataoff[i]))
- continue
- }
- // TODO: Dedup.
- funcdataBytes += int64(len(ldr.Data(funcdata[i])))
- setAddr(ftab, ctxt.Arch, dataoff, funcdata[i], funcdataoff[i])
+ if fi.NumInlTree() > 0 {
+ sb.SetUint32(ctxt.Arch, int64(off+objabi.PCDATA_InlTreeIndex*4), uint32(ldr.SymValue(fi.Pcinline())))
}
- off += int32(len(funcdata)) * int32(ctxt.Arch.PtrSize)
- }
-
- if off != end {
- ctxt.Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcdata), len(funcdata), ctxt.Arch.PtrSize)
- errorexit()
}
-
- nfunc++
}
+}
- // Final entry of table is just end pc.
- setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), state.lastFunc, ldr.SymSize(state.lastFunc))
-
- // Start file table.
- dSize := len(ftab.Data())
- start := int32(dSize)
- start += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1)
- state.filetabOffset = start
- ftab.SetUint32(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
+// pclntab initializes the pclntab symbol with
+// runtime function and file name information.
- nf := len(oldState.numberedFiles)
- ftab.Grow(int64(start) + int64((nf+1)*4))
- ftab.SetUint32(ctxt.Arch, int64(start), uint32(nf+1))
- for i := nf; i > 0; i-- {
- path := oldState.filepaths[i]
- val := int64(i)
- ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(ftabaddstring(ftab, path)))
- }
+// 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.
- ftab.SetSize(int64(len(ftab.Data())))
+ state, compUnits, funcs := makePclntab(ctxt, container)
- ctxt.NumFilesyms = len(oldState.numberedFiles)
+ ldr := ctxt.loader
+ state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
+ ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
+ ldr.SetAttrReachable(state.carrier, true)
- if ctxt.Debugvlog != 0 {
- ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes)
- }
+ 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
}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index bc880955b8..4971389613 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -34,6 +34,7 @@ import (
"cmd/internal/objabi"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
+ "debug/elf"
"fmt"
"path/filepath"
"strings"
@@ -53,10 +54,10 @@ func putelfstr(s string) int {
return off
}
-func putelfsyment(out *OutBuf, off int, addr int64, size int64, info int, shndx int, other int) {
+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(uint8(info))
+ out.Write8(info)
out.Write8(uint8(other))
out.Write16(uint16(shndx))
out.Write64(uint64(addr))
@@ -66,14 +67,14 @@ func putelfsyment(out *OutBuf, off int, addr int64, size int64, info int, shndx
out.Write32(uint32(off))
out.Write32(uint32(addr))
out.Write32(uint32(size))
- out.Write8(uint8(info))
+ out.Write8(info)
out.Write8(uint8(other))
out.Write16(uint16(shndx))
symSize += ELF32SYMSIZE
}
}
-func putelfsym(ctxt *Link, x loader.Sym, typ int, curbind int) {
+func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
ldr := ctxt.loader
addr := ldr.SymValue(x)
size := ldr.SymSize(x)
@@ -85,9 +86,9 @@ func putelfsym(ctxt *Link, x loader.Sym, typ int, curbind int) {
xot := ldr.SymType(xo)
xosect := ldr.SymSect(xo)
- var elfshnum int
+ var elfshnum elf.SectionIndex
if xot == sym.SDYNIMPORT || xot == sym.SHOSTOBJ || xot == sym.SUNDEFEXT {
- elfshnum = SHN_UNDEF
+ elfshnum = elf.SHN_UNDEF
size = 0
} else {
if xosect == nil {
@@ -101,11 +102,11 @@ func putelfsym(ctxt *Link, x loader.Sym, typ int, curbind int) {
elfshnum = xosect.Elfsect.(*ElfShdr).shnum
}
- // One pass for each binding: STB_LOCAL, STB_GLOBAL,
- // maybe one day STB_WEAK.
- bind := STB_GLOBAL
+ // 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) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
- bind = STB_LOCAL
+ bind = elf.STB_LOCAL
}
// In external linking mode, we have to invoke gcc with -rdynamic
@@ -113,23 +114,23 @@ func putelfsym(ctxt *Link, x loader.Sym, typ int, curbind int) {
// 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 != SHN_UNDEF {
- bind = STB_LOCAL
+ if !ctxt.DynlinkingGo() && ctxt.IsExternal() && !ldr.AttrCgoExportStatic(x) && elfshnum != elf.SHN_UNDEF {
+ bind = elf.STB_LOCAL
}
- if ctxt.LinkMode == LinkExternal && elfshnum != SHN_UNDEF {
+ if ctxt.LinkMode == LinkExternal && elfshnum != elf.SHN_UNDEF {
addr -= int64(xosect.Vaddr)
}
- other := STV_DEFAULT
+ 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 = STV_HIDDEN
+ other = int(elf.STV_HIDDEN)
}
- if ctxt.IsPPC64() && typ == STT_FUNC && ldr.AttrShared(x) && ldr.SymName(x) != "runtime.duffzero" && ldr.SymName(x) != "runtime.duffcopy" {
+ 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.
@@ -149,7 +150,7 @@ func putelfsym(ctxt *Link, x loader.Sym, typ int, curbind int) {
sname = strings.Replace(sname, "·", ".", -1)
}
- if ctxt.DynlinkingGo() && bind == STB_GLOBAL && curbind == STB_LOCAL && ldr.SymType(x) == sym.STEXT {
+ 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
@@ -158,7 +159,7 @@ func putelfsym(ctxt *Link, x loader.Sym, typ int, curbind int) {
// (*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, STB_LOCAL<<4|typ&0xf, elfshnum, other)
+ 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
@@ -166,23 +167,23 @@ func putelfsym(ctxt *Link, x loader.Sym, typ int, curbind int) {
return
}
- putelfsyment(ctxt.Out, putelfstr(sname), addr, size, bind<<4|typ&0xf, elfshnum, other)
+ 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 int) {
- putelfsyment(out, 0, 0, 0, STB_LOCAL<<4|STT_SECTION, shndx, 0)
+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 int) {
+func genelfsym(ctxt *Link, elfbind elf.SymBind) {
ldr := ctxt.loader
// runtime.text marker symbol(s).
s := ldr.Lookup("runtime.text", 0)
- putelfsym(ctxt, s, STT_FUNC, elfbind)
+ 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()) {
@@ -196,18 +197,18 @@ func genelfsym(ctxt *Link, elfbind int) {
if ldr.SymType(s) != sym.STEXT {
panic("unexpected type for runtime.text symbol")
}
- putelfsym(ctxt, s, STT_FUNC, elfbind)
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
}
// Text symbols.
for _, s := range ctxt.Textp {
- putelfsym(ctxt, s, STT_FUNC, elfbind)
+ 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, STT_FUNC, elfbind)
+ putelfsym(ctxt, s, elf.STT_FUNC, elfbind)
}
shouldBeInSymbolTable := func(s loader.Sym) bool {
@@ -236,12 +237,12 @@ func genelfsym(ctxt *Link, elfbind int) {
}
st := ldr.SymType(s)
if st >= sym.SELFRXSECT && st < sym.SXREF {
- typ := STT_OBJECT
+ typ := elf.STT_OBJECT
if st == sym.STLSBSS {
if ctxt.IsInternal() {
continue
}
- typ = STT_TLS
+ typ = elf.STT_TLS
}
if !shouldBeInSymbolTable(s) {
continue
@@ -250,7 +251,7 @@ func genelfsym(ctxt *Link, elfbind int) {
continue
}
if st == sym.SHOSTOBJ || st == sym.SDYNIMPORT || st == sym.SUNDEFEXT {
- putelfsym(ctxt, s, int(ldr.SymElfType(s)), elfbind)
+ putelfsym(ctxt, s, ldr.SymElfType(s), elfbind)
}
}
}
@@ -258,7 +259,7 @@ func genelfsym(ctxt *Link, elfbind int) {
func asmElfSym(ctxt *Link) {
// the first symbol entry is reserved
- putelfsyment(ctxt.Out, 0, 0, 0, STB_LOCAL<<4|STT_NOTYPE, 0, 0)
+ putelfsyment(ctxt.Out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE), 0, 0)
dwarfaddelfsectionsyms(ctxt)
@@ -266,12 +267,12 @@ func asmElfSym(ctxt *Link) {
// 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, STB_LOCAL<<4|STT_FILE, SHN_ABS, 0)
+ putelfsyment(ctxt.Out, putelfstr("go.go"), 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_FILE), elf.SHN_ABS, 0)
ctxt.numelfsym++
- bindings := []int{STB_LOCAL, STB_GLOBAL}
+ bindings := []elf.SymBind{elf.STB_LOCAL, elf.STB_GLOBAL}
for _, elfbind := range bindings {
- if elfbind == STB_GLOBAL {
+ if elfbind == elf.STB_GLOBAL {
elfglobalsymndx = ctxt.numelfsym
}
genelfsym(ctxt, elfbind)
@@ -518,7 +519,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
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) {
+ if !ctxt.IsExternal() && ldr.IsFileLocal(s) && !ldr.IsFromAssembly(s) && ldr.SymPkg(s) != "" {
ldr.SetAttrNotInSymbolTable(s, true)
}
if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || (ldr.SymType(s) != sym.SRODATA && ldr.SymType(s) != sym.SGOFUNC) {
@@ -580,8 +581,12 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
symGroupType[s] = sym.SGOFUNC
ldr.SetAttrNotInSymbolTable(s, true)
ldr.SetCarrierSym(s, symgofunc)
- const align = 4
- ldr.SetSymAlign(s, align)
+ align := int32(4)
+ if a := ldr.SymAlign(s); a < align {
+ ldr.SetSymAlign(s, align)
+ } else {
+ align = a
+ }
liveness += (ldr.SymSize(s) + int64(align) - 1) &^ (int64(align) - 1)
}
}
@@ -619,6 +624,18 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
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)))
@@ -627,10 +644,6 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
- // The filetab slice
- moduledata.AddAddrPlus(ctxt.Arch, pcln.pclntab, int64(pcln.filetabOffset))
- moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
- moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
// findfunctab
moduledata.AddAddr(ctxt.Arch, pcln.findfunctab)
// minpc, maxpc
@@ -681,8 +694,8 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
itablinkSym := ldr.Lookup("runtime.itablink", 0)
nitablinks := uint64(ldr.SymSize(itablinkSym)) / uint64(ctxt.Arch.PtrSize)
moduledata.AddAddr(ctxt.Arch, itablinkSym)
- moduledata.AddUint(ctxt.Arch, uint64(nitablinks))
- moduledata.AddUint(ctxt.Arch, uint64(nitablinks))
+ 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)
diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go
index 102b6c5436..f68de8fff1 100644
--- a/src/cmd/link/internal/ld/target.go
+++ b/src/cmd/link/internal/ld/target.go
@@ -74,8 +74,12 @@ func (t *Target) IsDynlinkingGo() bool {
func (t *Target) UseRelro() bool {
switch t.BuildMode {
case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin:
- return t.IsELF || t.HeadType == objabi.Haix
+ 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)
}
}
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
index b62f18c342..32a24cf6f0 100644
--- a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
@@ -18,6 +18,13 @@ var p *T
var e interface{}
func main() {
- p = new(T) // used T, but never converted to interface
+ 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/ifacemethod3.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go
new file mode 100644
index 0000000000..9a8dfbce5f
--- /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 0000000000..52ee2e3d86
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go
@@ -0,0 +1,23 @@
+// 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
+
+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
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue39256/x.go b/src/cmd/link/internal/ld/testdata/issue39256/x.go
index d8562ad172..97bc1cc407 100644
--- a/src/cmd/link/internal/ld/testdata/issue39256/x.go
+++ b/src/cmd/link/internal/ld/testdata/issue39256/x.go
@@ -13,6 +13,8 @@ import (
//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() {
diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go
index 5a39856a3b..c698874b32 100644
--- a/src/cmd/link/internal/loadelf/ldelf.go
+++ b/src/cmd/link/internal/loadelf/ldelf.go
@@ -372,6 +372,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
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")
@@ -946,14 +951,15 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, error) {
// performance.
const (
- AMD64 = uint32(sys.AMD64)
- ARM = uint32(sys.ARM)
- ARM64 = uint32(sys.ARM64)
- I386 = uint32(sys.I386)
- PPC64 = uint32(sys.PPC64)
- S390X = uint32(sys.S390X)
- MIPS = uint32(sys.MIPS)
- MIPS64 = uint32(sys.MIPS64)
+ AMD64 = uint32(sys.AMD64)
+ ARM = uint32(sys.ARM)
+ ARM64 = uint32(sys.ARM64)
+ I386 = uint32(sys.I386)
+ 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 {
@@ -963,6 +969,8 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, error) {
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,
@@ -970,6 +978,8 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, error) {
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,
@@ -1013,6 +1023,7 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, error) {
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,
@@ -1056,6 +1067,27 @@ func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, error) {
S390X | uint32(elf.R_390_GOT64)<<16,
S390X | uint32(elf.R_390_PLT64)<<16:
return 8, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_RVC_BRANCH)<<16,
+ RISCV64 | uint32(elf.R_RISCV_RVC_JUMP)<<16:
+ return 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_RELAX)<<16:
+ return 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, nil
}
}
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 8fd10b0848..971cc432ff 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -63,6 +63,7 @@ type Reloc struct {
func (rel Reloc) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc.Type()) + rel.typ }
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 }
func (rel Reloc) SetType(t objabi.RelocType) {
if t != objabi.RelocType(uint8(t)) {
@@ -93,11 +94,12 @@ type oReader struct {
version int // version of static symbol
flags uint32 // read from object file
pkgprefix string
- syms []Sym // Sym's global index, indexed by local index
- 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
+ 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
@@ -219,7 +221,7 @@ type Loader struct {
deferReturnTramp map[Sym]bool // whether the symbol is a trampoline of a deferreturn call
- objByPkg map[string]*oReader // map package path to its Go object reader
+ 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
@@ -328,10 +330,10 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
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, 100000), // reserve index 0 for nil symbol
+ 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]*oReader),
+ objByPkg: make(map[string]uint32),
outer: make(map[Sym]Sym),
sub: make(map[Sym]Sym),
dynimplib: make(map[Sym]string),
@@ -370,7 +372,7 @@ func (l *Loader) addObj(pkg string, r *oReader) Sym {
}
pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path
if _, ok := l.objByPkg[pkg]; !ok {
- l.objByPkg[pkg] = r
+ l.objByPkg[pkg] = r.objidx
}
i := Sym(len(l.objSyms))
l.start[r] = i
@@ -631,20 +633,42 @@ func (l *Loader) resolve(r *oReader, s goobj.SymRef) Sym {
i := int(s.SymIdx) + r.ndef + r.nhashed64def + r.nhasheddef
return r.syms[i]
case goobj.PkgIdxBuiltin:
- return l.builtinSyms[s.SymIdx]
+ 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:
- pkg := r.Pkg(int(p))
- var ok bool
- rr, ok = l.objByPkg[pkg]
- if !ok {
- log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
- }
+ 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.
@@ -1794,6 +1818,11 @@ func (l *Loader) SortSub(s Sym) Sym {
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() {
@@ -1878,19 +1907,24 @@ func (fi *FuncInfo) FuncID() objabi.FuncID {
return objabi.FuncID((*goobj.FuncInfo)(nil).ReadFuncID(fi.data))
}
-func (fi *FuncInfo) Pcsp() []byte {
- pcsp, end := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data)
- return fi.r.BytesAt(fi.r.PcdataBase()+pcsp, int(end-pcsp))
+func (fi *FuncInfo) Pcsp() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data)
+ return fi.l.resolve(fi.r, sym)
}
-func (fi *FuncInfo) Pcfile() []byte {
- pcf, end := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data)
- return fi.r.BytesAt(fi.r.PcdataBase()+pcf, int(end-pcf))
+func (fi *FuncInfo) Pcfile() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data)
+ return fi.l.resolve(fi.r, sym)
}
-func (fi *FuncInfo) Pcline() []byte {
- pcln, end := (*goobj.FuncInfo)(nil).ReadPcline(fi.data)
- return fi.r.BytesAt(fi.r.PcdataBase()+pcln, int(end-pcln))
+func (fi *FuncInfo) Pcline() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcline(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+func (fi *FuncInfo) Pcinline() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data)
+ return fi.l.resolve(fi.r, sym)
}
// Preload has to be called prior to invoking the various methods
@@ -1899,27 +1933,16 @@ func (fi *FuncInfo) Preload() {
fi.lengths = (*goobj.FuncInfo)(nil).ReadFuncInfoLengths(fi.data)
}
-func (fi *FuncInfo) Pcinline() []byte {
- if !fi.lengths.Initialized {
- panic("need to call Preload first")
- }
- pcinl, end := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data, fi.lengths.PcdataOff)
- return fi.r.BytesAt(fi.r.PcdataBase()+pcinl, int(end-pcinl))
-}
-
-func (fi *FuncInfo) NumPcdata() uint32 {
+func (fi *FuncInfo) Pcdata() []Sym {
if !fi.lengths.Initialized {
panic("need to call Preload first")
}
- return fi.lengths.NumPcdata
-}
-
-func (fi *FuncInfo) Pcdata(k int) []byte {
- if !fi.lengths.Initialized {
- panic("need to call Preload first")
+ syms := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data)
+ ret := make([]Sym, len(syms))
+ for i := range ret {
+ ret[i] = fi.l.resolve(fi.r, syms[i])
}
- pcdat, end := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data, fi.lengths.PcdataOff, uint32(k))
- return fi.r.BytesAt(fi.r.PcdataBase()+pcdat, int(end-pcdat))
+ return ret
}
func (fi *FuncInfo) NumFuncdataoff() uint32 {
@@ -2022,8 +2045,9 @@ func (l *Loader) FuncInfo(i Sym) FuncInfo {
return FuncInfo{}
}
-// Preload a package: add autolibs, add defined package symbols to the symbol table.
-// Does not add non-package symbols yet, which will be done in LoadNonpkgSyms.
+// 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 {
@@ -2066,8 +2090,6 @@ func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, u
}
l.addObj(lib.Pkg, or)
- st := loadState{l: l}
- st.preloadSyms(or, pkgDef)
// The caller expects us consuming all the data
f.MustSeek(length, os.SEEK_CUR)
@@ -2150,17 +2172,30 @@ func (st *loadState) preloadSyms(r *oReader, kind int) {
}
}
-// Add hashed (content-addressable) symbols, non-package symbols, and
+// Add syms, hashed (content-addressable) symbols, non-package symbols, and
// references to external symbols (which are always named).
-func (l *Loader) LoadNonpkgSyms(arch *sys.Arch) {
+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)
+
l.npkgsyms = l.NSym()
- // Preallocate some space (a few hundreds KB) for some symbols.
- // As of Go 1.15, linking cmd/compile has ~8000 hashed64 symbols and
- // ~13000 hashed symbols.
st := loadState{
l: l,
- hashed64Syms: make(map[uint64]symAndSize, 10000),
- hashedSyms: make(map[goobj.HashType]symAndSize, 15000),
+ hashed64Syms: make(map[uint64]symAndSize, hashed64Size),
+ hashedSyms: make(map[goobj.HashType]symAndSize, hashedSize),
+ }
+
+ for _, o := range l.objs[goObjStart:] {
+ st.preloadSyms(o.r, pkgDef)
}
for _, o := range l.objs[goObjStart:] {
st.preloadSyms(o.r, hashed64Def)
@@ -2195,6 +2230,18 @@ func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
}
}
+ // 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("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
+ }
+ r.pkg[i] = objidx
+ }
+
// load flags of package refs
for i, n := 0, r.NRefFlags(); i < n; i++ {
rf := r.RefFlags(i)
@@ -2602,11 +2649,15 @@ func (l *Loader) Dump() {
fmt.Println("Nsyms:", len(l.objSyms))
fmt.Println("syms")
for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
- pi := interface{}("")
+ pi := ""
if l.IsExternal(i) {
pi = fmt.Sprintf("<ext %d>", l.extIndex(i))
}
- fmt.Println(i, l.SymName(i), l.SymType(i), pi)
+ 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] {
diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go
index e14d89a927..5d37da8ac6 100644
--- a/src/cmd/link/internal/loader/symbolbuilder.go
+++ b/src/cmd/link/internal/loader/symbolbuilder.go
@@ -336,6 +336,15 @@ func (sb *SymbolBuilder) Addstring(str string) int64 {
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
@@ -411,3 +420,21 @@ func (sb *SymbolBuilder) MakeWritable() {
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
index 864d80835b..6d1d9bb29e 100644
--- a/src/cmd/link/internal/loadmacho/ldmacho.go
+++ b/src/cmd/link/internal/loadmacho/ldmacho.go
@@ -43,11 +43,11 @@ 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/internal/ld
+// 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_FAKE_GOTPCREL = 100
+ MACHO_ARM64_RELOC_ADDEND = 10
)
type ldMachoObj struct {
@@ -172,11 +172,12 @@ const (
LdMachoCpuVax = 1
LdMachoCpu68000 = 6
LdMachoCpu386 = 7
- LdMachoCpuAmd64 = 0x1000007
+ LdMachoCpuAmd64 = 1<<24 | 7
LdMachoCpuMips = 8
LdMachoCpu98000 = 10
LdMachoCpuHppa = 11
LdMachoCpuArm = 12
+ LdMachoCpuArm64 = 1<<24 | 12
LdMachoCpu88000 = 13
LdMachoCpuSparc = 14
LdMachoCpu860 = 15
@@ -471,11 +472,14 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
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)
@@ -633,7 +637,9 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
}
bld.SetType(l.SymType(outer))
- l.AddInteriorSym(outer, s)
+ 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) {
@@ -701,11 +707,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
}
sb := l.MakeSymbolUpdater(sect.sym)
+ var rAdd int64
for j := uint32(0); j < sect.nreloc; j++ {
var (
rOff int32
rSize uint8
- rAdd int64
rType objabi.RelocType
rSym loader.Sym
)
@@ -716,33 +722,40 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
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 && 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:])))
+ 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
@@ -774,6 +787,8 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
r.SetSiz(rSize)
r.SetSym(rSym)
r.SetAdd(rAdd)
+
+ rAdd = 0 // clear rAdd for next iteration
}
sb.SortRelocs()
diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go
index d2dc20f5c1..01d89a209c 100644
--- a/src/cmd/link/internal/mips64/obj.go
+++ b/src/cmd/link/internal/mips64/obj.go
@@ -60,7 +60,7 @@ func Init() (*sys.Arch, ld.Arch) {
Linuxdynld: "/lib64/ld64.so.1",
Freebsddynld: "XXX",
- Openbsddynld: "XXX",
+ Openbsddynld: "/usr/libexec/ld.so",
Netbsddynld: "XXX",
Dragonflydynld: "XXX",
Solarisdynld: "XXX",
@@ -84,7 +84,8 @@ func archinit(ctxt *ld.Link) {
*ld.FlagRound = 16 * 1024
}
- case objabi.Hlinux: /* mips64 elf */
+ case objabi.Hlinux, /* mips64 elf */
+ objabi.Hopenbsd:
ld.Elfinit(ctxt)
ld.HEADR = ld.ELFRESERVE
if *ld.FlagTextAddr == -1 {
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
index dc522e6a38..5bf3898eb9 100644
--- a/src/cmd/link/internal/ppc64/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -313,7 +313,7 @@ func addelfdynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s lo
rela := ldr.MakeSymbolUpdater(syms.Rela)
rela.AddAddrPlus(target.Arch, s, int64(r.Off()))
- rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_PPC64_ADDR64)))
+ 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
}
@@ -994,9 +994,10 @@ func addpltsym(ctxt *ld.Link, ldr *loader.Loader, s loader.Sym) {
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, ld.ELF64_R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_PPC64_JMP_SLOT)))
+ 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")
@@ -1052,7 +1053,7 @@ func ensureglinkresolver(ctxt *ld.Link, ldr *loader.Loader) *loader.SymbolBuilde
// 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, ld.DT_PPC64_GLINK, glink.Sym(), glink.Size()-32)
+ ld.Elfwritedynentsymplus(ctxt, du, elf.DT_PPC64_GLINK, glink.Sym(), glink.Size()-32)
return glink
}
diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go
index 1236145fb1..c18e0540d8 100644
--- a/src/cmd/link/internal/riscv64/asm.go
+++ b/src/cmd/link/internal/riscv64/asm.go
@@ -11,20 +11,143 @@ import (
"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 {
- log.Fatalf("elfreloc1")
- return false
+ 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_CALLRISCV:
+ // Call relocations are currently handled via R_RISCV_PCREL_ITYPE.
+ // TODO(jsing): Consider generating elf.R_RISCV_CALL instead of a
+ // HI20/LO12_I pair.
+
+ 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("elfsetuplt")
+ log.Fatalf("elfsetupplt")
}
func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
@@ -33,13 +156,35 @@ func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtRe
}
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()
- rs = ldr.ResolveABIAlias(rs)
+ if target.IsExternal() {
+ switch r.Type() {
+ case objabi.R_CALLRISCV:
+ return val, 0, 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
+ }
+
+ rs := ldr.ResolveABIAlias(r.Sym())
+
switch r.Type() {
case objabi.R_CALLRISCV:
// Nothing to do.
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:
pc := ldr.SymValue(s) + int64(r.Off())
off := ldr.SymValue(rs) + r.Add() - pc
@@ -89,3 +234,11 @@ func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant
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_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
+}
diff --git a/src/cmd/link/internal/riscv64/obj.go b/src/cmd/link/internal/riscv64/obj.go
index e66d3cd856..917324d922 100644
--- a/src/cmd/link/internal/riscv64/obj.go
+++ b/src/cmd/link/internal/riscv64/obj.go
@@ -23,9 +23,12 @@ func Init() (*sys.Arch, ld.Arch) {
Archinit: archinit,
Archreloc: archreloc,
Archrelocvariant: archrelocvariant,
+ Extreloc: extreloc,
Elfreloc1: elfreloc1,
+ ElfrelocSize: 24,
Elfsetupplt: elfsetupplt,
Gentext: gentext,
+ GenSymsLate: genSymsLate,
Machoreloc1: machoreloc1,
Linuxdynld: "/lib/ld.so.1",
diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go
index 645b7d4e28..78d2cc81e4 100644
--- a/src/cmd/link/internal/s390x/asm.go
+++ b/src/cmd/link/internal/s390x/asm.go
@@ -444,7 +444,7 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8)
sDynid := ldr.SymDynid(s)
- rela.AddUint64(target.Arch, ld.ELF64_R_INFO(uint32(sDynid), uint32(elf.R_390_JMP_SLOT)))
+ 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))
diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go
index 1a4165ebf7..70cf36a87e 100644
--- a/src/cmd/link/internal/sym/symbol.go
+++ b/src/cmd/link/internal/sym/symbol.go
@@ -33,7 +33,3 @@ func VersionToABI(v int) (obj.ABI, bool) {
}
return ^obj.ABI(0), false
}
-
-type Pcdata struct {
- P []byte
-}
diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go
index 3bd56a6e3a..31851fbb56 100644
--- a/src/cmd/link/internal/wasm/asm.go
+++ b/src/cmd/link/internal/wasm/asm.go
@@ -167,6 +167,9 @@ func asmb2(ctxt *ld.Link, ldr *loader.Loader) {
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 := ldr.ResolveABIAlias(r.Sym())
diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go
index 9b949ebbf8..af0ce11255 100644
--- a/src/cmd/link/internal/x86/asm.go
+++ b/src/cmd/link/internal/x86/asm.go
@@ -303,7 +303,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
ld.Adddynsym(ldr, target, syms, targ)
rel := ldr.MakeSymbolUpdater(syms.Rel)
rel.AddAddrPlus(target.Arch, s, int64(r.Off()))
- rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_386_32)))
+ 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)
@@ -483,7 +483,7 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
rel.AddAddrPlus(target.Arch, got.Sym(), got.Size()-4)
sDynid := ldr.SymDynid(s)
- rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(sDynid), uint32(elf.R_386_JMP_SLOT)))
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(sDynid), uint32(elf.R_386_JMP_SLOT)))
ldr.SetPlt(s, int32(plt.Size()-16))
} else {
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index 72ff01c932..158c670739 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -1,8 +1,13 @@
+// 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 main
import (
"bufio"
"bytes"
+ "cmd/internal/sys"
"debug/macho"
"internal/testenv"
"io/ioutil"
@@ -177,6 +182,7 @@ main.x: relocation target main.zero not defined
func TestIssue33979(t *testing.T) {
testenv.MustHaveGoBuild(t)
testenv.MustHaveCGO(t)
+ testenv.MustInternalLink(t)
// Skip test on platforms that do not support cgo internal linking.
switch runtime.GOARCH {
@@ -304,7 +310,7 @@ func TestBuildForTvOS(t *testing.T) {
cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
cmd.Env = append(os.Environ(),
"CGO_ENABLED=1",
- "GOOS=darwin",
+ "GOOS=ios",
"GOARCH=arm64",
"CC="+strings.Join(CC, " "),
"CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459
@@ -591,7 +597,12 @@ func TestFuncAlign(t *testing.T) {
}
defer os.RemoveAll(tmpdir)
- src := filepath.Join(tmpdir, "falign.go")
+ src := filepath.Join(tmpdir, "go.mod")
+ err = ioutil.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ src = filepath.Join(tmpdir, "falign.go")
err = ioutil.WriteFile(src, []byte(testFuncAlignSrc), 0666)
if err != nil {
t.Fatal(err)
@@ -796,3 +807,121 @@ func TestContentAddressableSymbols(t *testing.T) {
t.Errorf("command %s failed: %v\n%s", cmd, err, out)
}
}
+
+func TestReadOnly(t *testing.T) {
+ // Test that read-only data is indeed read-only.
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ src := filepath.Join("testdata", "testRO", "x.go")
+ cmd := exec.Command(testenv.GoToolPath(t), "run", src)
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("running test program did not fail. output:\n%s", out)
+ }
+}
+
+const testIssue38554Src = `
+package main
+
+type T [10<<20]byte
+
+//go:noinline
+func f() T {
+ return T{} // compiler will make a large stmp symbol, but not used.
+}
+
+func main() {
+ x := f()
+ println(x[1])
+}
+`
+
+func TestIssue38554(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestIssue38554")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "x.go")
+ err = ioutil.WriteFile(src, []byte(testIssue38554Src), 0666)
+ if err != nil {
+ t.Fatalf("failed to write source file: %v", err)
+ }
+ exe := filepath.Join(tmpdir, "x.exe")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failed: %v\n%s", err, out)
+ }
+
+ fi, err := os.Stat(exe)
+ if err != nil {
+ t.Fatalf("failed to stat output file: %v", err)
+ }
+
+ // The test program is not much different from a helloworld, which is
+ // typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,
+ // it will be over 10 MB.
+ const want = 5 << 20
+ if got := fi.Size(); got > want {
+ t.Errorf("binary too big: got %d, want < %d", got, want)
+ }
+}
+
+const testIssue42396src = `
+package main
+
+//go:noinline
+//go:nosplit
+func callee(x int) {
+}
+
+func main() {
+ callee(9)
+}
+`
+
+func TestIssue42396(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if !sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
+ t.Skip("no race detector support")
+ }
+
+ t.Parallel()
+
+ tmpdir, err := ioutil.TempDir("", "TestIssue42396")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "main.go")
+ err = ioutil.WriteFile(src, []byte(testIssue42396src), 0666)
+ if err != nil {
+ t.Fatalf("failed to write source file: %v", err)
+ }
+ exe := filepath.Join(tmpdir, "main.exe")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Fatalf("build unexpectedly succeeded")
+ }
+
+ // Check to make sure that we see a reasonable error message
+ // and not a panic.
+ if strings.Contains(string(out), "panic:") {
+ t.Fatalf("build should not fail with panic:\n%s", out)
+ }
+ const want = "reference to undefined builtin"
+ if !strings.Contains(string(out), want) {
+ t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
+ }
+}
diff --git a/src/cmd/link/testdata/testRO/x.go b/src/cmd/link/testdata/testRO/x.go
new file mode 100644
index 0000000000..d77db6d563
--- /dev/null
+++ b/src/cmd/link/testdata/testRO/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.
+
+// Test that read-only data is indeed read-only. This
+// program attempts to modify read-only data, and it
+// should fail.
+
+package main
+
+import "unsafe"
+
+var s = "hello"
+
+func main() {
+ println(s)
+ *(*struct {
+ p *byte
+ l int
+ })(unsafe.Pointer(&s)).p = 'H'
+ println(s)
+}