diff options
Diffstat (limited to 'src/cmd/link/internal')
48 files changed, 2726 insertions, 1581 deletions
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 { |
