diff options
Diffstat (limited to 'src/cmd/link/internal')
112 files changed, 37944 insertions, 0 deletions
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go new file mode 100644 index 0000000..2d09a61 --- /dev/null +++ b/src/cmd/link/internal/amd64/asm.go @@ -0,0 +1,676 @@ +// Inferno utils/6l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "log" +) + +func PADDR(x uint32) uint32 { + return x &^ 0x80000000 +} + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt) + if initfunc == nil { + return + } + + o := func(op ...uint8) { + for _, op1 := range op { + initfunc.AddUint8(op1) + } + } + + // 0000000000000000 <local.dso_init>: + // 0: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 7 <local.dso_init+0x7> + // 3: R_X86_64_PC32 runtime.firstmoduledata-0x4 + o(0x48, 0x8d, 0x3d) + initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 0) + // 7: e8 00 00 00 00 callq c <local.dso_init+0xc> + // 8: R_X86_64_PLT32 runtime.addmoduledata-0x4 + o(0xe8) + initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_CALL, 4) + // c: c3 retq + o(0xc3) +} + +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch rt := r.Type(); rt { + default: + if rt >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC32): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_X86_64_PC32 relocation for dynamic symbol %s", ldr.SymName(targ)) + } + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PC64): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_X86_64_PC64 relocation for dynamic symbol %s", ldr.SymName(targ)) + } + if targType == 0 || targType == sym.SXREF { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+8) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_PLT32): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCREL), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_GOTPCRELX), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_REX_GOTPCRELX): + su := ldr.MakeSymbolUpdater(s) + if targType != sym.SDYNIMPORT { + // have symbol + sData := ldr.Data(s) + if r.Off() >= 2 && sData[r.Off()-2] == 0x8b { + su.MakeWritable() + // turn MOVQ of GOT entry into LEAQ of symbol itself + writeableData := su.Data() + writeableData[r.Off()-2] = 0x8d + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + return true + } + } + + // fall back to using GOT and hope for the best (CMOV*) + // TODO: just needs relocation, no need to put in .dynsym + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_X86_64_GLOB_DAT)) + + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+4+int64(ldr.SymGot(targ))) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_X86_64_64): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_X86_64_64 relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + if target.IsPIE() && target.IsInternal() { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + break + } + return true + + // Handle relocations found in Mach-O object files. + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0: + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ)) + } + if target.IsPIE() && target.IsInternal() { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + if rt == objabi.MachoRelocOffset+ld.MACHO_X86_64_RELOC_UNSIGNED*2 { + break + } else { + // MACHO_X86_64_RELOC_SIGNED or MACHO_X86_64_RELOC_BRANCH + // Can this happen? The object is expected to be PIC. + ldr.Errorf(s, "unsupported relocation for PIE: %v", rt) + } + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1: + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) + return true + } + fallthrough + + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 1, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED*2 + 1, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_1*2 + 1, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_2*2 + 1, + objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_SIGNED_4*2 + 1: + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected pc-relative reloc for dynamic symbol %s", ldr.SymName(targ)) + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: + if targType != sym.SDYNIMPORT { + // have symbol + // turn MOVQ of GOT entry into LEAQ of symbol itself + sdata := ldr.Data(s) + if r.Off() < 2 || sdata[r.Off()-2] != 0x8b { + ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ)) + return false + } + + su := ldr.MakeSymbolUpdater(s) + su.MakeWritable() + sdata = su.Data() + sdata[r.Off()-2] = 0x8d + su.SetRelocType(rIdx, objabi.R_PCREL) + return true + } + fallthrough + + case objabi.MachoRelocOffset + ld.MACHO_X86_64_RELOC_GOT*2 + 1: + if targType != sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) + } + ld.AddGotSym(target, ldr, syms, targ, 0) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + return true + } + + // Reread the reloc to incorporate any changes in type above. + relocs := ldr.Relocs(s) + r = relocs.At(rIdx) + + switch r.Type() { + case objabi.R_CALL, + objabi.R_PCREL: + if targType != sym.SDYNIMPORT { + // nothing to do, the relocation will be laid out in reloc + return true + } + if target.IsExternal() { + // External linker will do this relocation. + return true + } + // Internal linking, for both ELF and Mach-O. + // Build a PLT entry and change the relocation target to that entry. + addpltsym(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) + return true + + case objabi.R_ADDR: + if ldr.SymType(s) == sym.STEXT && target.IsElf() { + su := ldr.MakeSymbolUpdater(s) + if target.IsSolaris() { + addpltsym(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) + return true + } + // The code is asking for the address of an external + // function. We provide it with the address of the + // correspondent GOT symbol. + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_X86_64_GLOB_DAT)) + + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + return true + } + + // Process dynamic relocations for the data sections. + if target.IsPIE() && target.IsInternal() { + // When internally linking, generate dynamic relocations + // for all typical R_ADDR relocations. The exception + // are those R_ADDR that are created as part of generating + // the dynamic relocations and must be resolved statically. + // + // There are three phases relevant to understanding this: + // + // dodata() // we are here + // address() // symbol address assignment + // reloc() // resolution of static R_ADDR relocs + // + // At this point symbol addresses have not been + // assigned yet (as the final size of the .rela section + // will affect the addresses), and so we cannot write + // the Elf64_Rela.r_offset now. Instead we delay it + // until after the 'address' phase of the linker is + // complete. We do this via Addaddrplus, which creates + // a new R_ADDR relocation which will be resolved in + // the 'reloc' phase. + // + // These synthetic static R_ADDR relocs must be skipped + // now, or else we will be caught in an infinite loop + // of generating synthetic relocs for our synthetic + // relocs. + // + // Furthermore, the rela sections contain dynamic + // relocations with R_ADDR relocations on + // Elf64_Rela.r_offset. This field should contain the + // symbol offset as determined by reloc(), not the + // final dynamically linked address as a dynamic + // relocation would provide. + switch ldr.SymName(s) { + case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic": + return false + } + } else { + // Either internally linking a static executable, + // in which case we can resolve these relocations + // statically in the 'reloc' phase, or externally + // linking, in which case the relocation will be + // prepared in the 'reloc' phase and passed to the + // external linker in the 'asmb' phase. + if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA { + break + } + } + + if target.IsElf() { + // Generate R_X86_64_RELATIVE relocations for best + // efficiency in the dynamic linker. + // + // As noted above, symbol addresses have not been + // assigned yet, so we can't generate the final reloc + // entry yet. We ultimately want: + // + // r_offset = s + r.Off + // r_info = R_X86_64_RELATIVE + // r_addend = targ + r.Add + // + // The dynamic linker will set *offset = base address + + // addend. + // + // AddAddrPlus is used for r_offset and r_addend to + // generate new R_ADDR relocations that will update + // these fields in the 'reloc' phase. + rela := ldr.MakeSymbolUpdater(syms.Rela) + rela.AddAddrPlus(target.Arch, s, int64(r.Off())) + if r.Siz() == 8 { + rela.AddUint64(target.Arch, elf.R_INFO(0, uint32(elf.R_X86_64_RELATIVE))) + } else { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + rela.AddAddrPlus(target.Arch, targ, int64(r.Add())) + // Not mark r done here. So we still apply it statically, + // so in the file content we'll also have the right offset + // to the relocation target. So it can be examined statically + // (e.g. go version). + return true + } + + if target.IsDarwin() { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation. + // Here we record what are needed and encode them later. + ld.MachoAddRebase(s, int64(r.Off())) + // Not mark r done here. So we still apply it statically, + // so in the file content we'll also have the right offset + // to the relocation target. So it can be examined statically + // (e.g. go version). + return true + } + } + + return false +} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + out.Write64(uint64(sectoff)) + + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + siz := r.Size + switch r.Type { + default: + return false + case objabi.R_ADDR, objabi.R_DWARFSECREF: + if siz == 4 { + out.Write64(uint64(elf.R_X86_64_32) | uint64(elfsym)<<32) + } else if siz == 8 { + out.Write64(uint64(elf.R_X86_64_64) | uint64(elfsym)<<32) + } else { + return false + } + case objabi.R_TLS_LE: + if siz == 4 { + out.Write64(uint64(elf.R_X86_64_TPOFF32) | uint64(elfsym)<<32) + } else { + return false + } + case objabi.R_TLS_IE: + if siz == 4 { + out.Write64(uint64(elf.R_X86_64_GOTTPOFF) | uint64(elfsym)<<32) + } else { + return false + } + case objabi.R_CALL: + if siz == 4 { + if ldr.SymType(r.Xsym) == sym.SDYNIMPORT { + out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32) + } else { + out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32) + } + } else { + return false + } + case objabi.R_PCREL: + if siz == 4 { + if ldr.SymType(r.Xsym) == sym.SDYNIMPORT && ldr.SymElfType(r.Xsym) == elf.STT_FUNC { + out.Write64(uint64(elf.R_X86_64_PLT32) | uint64(elfsym)<<32) + } else { + out.Write64(uint64(elf.R_X86_64_PC32) | uint64(elfsym)<<32) + } + } else { + return false + } + case objabi.R_GOTPCREL: + if siz == 4 { + out.Write64(uint64(elf.R_X86_64_GOTPCREL) | uint64(elfsym)<<32) + } else { + return false + } + } + + out.Write64(uint64(r.Xadd)) + return true +} + +func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + rt := r.Type + + if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_PCREL || rt == objabi.R_GOTPCREL || rt == objabi.R_CALL { + if ldr.SymDynid(rs) < 0 { + ldr.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs)) + return false + } + + v = uint32(ldr.SymDynid(rs)) + v |= 1 << 27 // external relocation + } else { + v = uint32(ldr.SymSect(rs).Extnum) + if v == 0 { + ldr.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymSect(rs).Name, ldr.SymType(rs), ldr.SymType(rs)) + return false + } + } + + switch rt { + default: + return false + + case objabi.R_ADDR: + v |= ld.MACHO_X86_64_RELOC_UNSIGNED << 28 + + case objabi.R_CALL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_BRANCH << 28 + + // NOTE: Only works with 'external' relocation. Forced above. + case objabi.R_PCREL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_SIGNED << 28 + case objabi.R_GOTPCREL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_GOT_LOAD << 28 + } + + switch r.Size { + default: + return false + + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + out.Write32(uint32(sectoff)) + out.Write32(v) + return true +} + +func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + rt := r.Type + + if ldr.SymDynid(rs) < 0 { + ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs)) + return false + } + + out.Write32(uint32(sectoff)) + out.Write32(uint32(ldr.SymDynid(rs))) + + switch rt { + default: + return false + + case objabi.R_DWARFSECREF: + v = ld.IMAGE_REL_AMD64_SECREL + + case objabi.R_ADDR: + if r.Size == 8 { + v = ld.IMAGE_REL_AMD64_ADDR64 + } else { + v = ld.IMAGE_REL_AMD64_ADDR32 + } + + case objabi.R_CALL, + objabi.R_PCREL: + v = ld.IMAGE_REL_AMD64_REL32 + } + + out.Write16(uint16(v)) + + return true +} + +func archreloc(*ld.Target, *loader.Loader, *ld.ArchSyms, loader.Reloc, loader.Sym, int64) (int64, int, bool) { + return -1, 0, false +} + +func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 { + log.Fatalf("unexpected relocation variant") + return -1 +} + +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { + // pushq got+8(IP) + plt.AddUint8(0xff) + + plt.AddUint8(0x35) + plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 8) + + // jmpq got+16(IP) + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 16) + + // nopl 0(AX) + plt.AddUint32(ctxt.Arch, 0x00401f0f) + + // assume got->size == 0 too + got.AddAddrPlus(ctxt.Arch, dynamic, 0) + + got.AddUint64(ctxt.Arch, 0) + got.AddUint64(ctxt.Arch, 0) + } +} + +func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { + return + } + + ld.Adddynsym(ldr, target, syms, s) + + if target.IsElf() { + plt := ldr.MakeSymbolUpdater(syms.PLT) + got := ldr.MakeSymbolUpdater(syms.GOTPLT) + rela := ldr.MakeSymbolUpdater(syms.RelaPLT) + if plt.Size() == 0 { + panic("plt is not set up") + } + + // jmpq *got+size(IP) + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddPCRelPlus(target.Arch, got.Sym(), got.Size()) + + // add to got: pointer to current pos in plt + got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size()) + + // pushq $x + plt.AddUint8(0x68) + + plt.AddUint32(target.Arch, uint32((got.Size()-24-8)/8)) + + // jmpq .plt + plt.AddUint8(0xe9) + + plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4))) + + // rela + rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8) + + sDynid := ldr.SymDynid(s) + rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_X86_64_JMP_SLOT))) + rela.AddUint64(target.Arch, 0) + + ldr.SetPlt(s, int32(plt.Size()-16)) + } else if target.IsDarwin() { + ld.AddGotSym(target, ldr, syms, s, 0) + + sDynid := ldr.SymDynid(s) + lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT) + lep.AddUint32(target.Arch, uint32(sDynid)) + + plt := ldr.MakeSymbolUpdater(syms.PLT) + ldr.SetPlt(s, int32(plt.Size())) + + // jmpq *got+size(IP) + plt.AddUint8(0xff) + plt.AddUint8(0x25) + plt.AddPCRelPlus(target.Arch, syms.GOT, int64(ldr.SymGot(s))) + } else { + ldr.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func tlsIEtoLE(P []byte, off, size int) { + // Transform the PC-relative instruction into a constant load. + // That is, + // + // MOVQ X(IP), REG -> MOVQ $Y, REG + // + // To determine the instruction and register, we study the op codes. + // Consult an AMD64 instruction encoding guide to decipher this. + if off < 3 { + log.Fatal("R_X86_64_GOTTPOFF reloc not preceded by MOVQ or ADDQ instruction") + } + op := P[off-3 : off] + reg := op[2] >> 3 + + if op[1] == 0x8b || reg == 4 { + // MOVQ + if op[0] == 0x4c { + op[0] = 0x49 + } else if size == 4 && op[0] == 0x44 { + op[0] = 0x41 + } + if op[1] == 0x8b { + op[1] = 0xc7 + } else { + op[1] = 0x81 // special case for SP + } + op[2] = 0xc0 | reg + } else { + // An alternate op is ADDQ. This is handled by GNU gold, + // but right now is not generated by the Go compiler: + // ADDQ X(IP), REG -> ADDQ $Y, REG + // Consider adding support for it here. + log.Fatalf("expected TLS IE op to be MOVQ, got %v", op) + } +} diff --git a/src/cmd/link/internal/amd64/l.go b/src/cmd/link/internal/amd64/l.go new file mode 100644 index 0000000..c9ea90a --- /dev/null +++ b/src/cmd/link/internal/amd64/l.go @@ -0,0 +1,43 @@ +// Inferno utils/6l/l.h +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 32 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 7 + dwarfRegLR = 16 +) diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go new file mode 100644 index 0000000..d09c90e --- /dev/null +++ b/src/cmd/link/internal/amd64/obj.go @@ -0,0 +1,122 @@ +// Inferno utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchAMD64 + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + // 0xCC is INT $3 - breakpoint instruction + CodePad: []byte{0xCC}, + + Plan9Magic: uint32(4*26*26 + 7), + Plan9_64Bit: true, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Elfreloc1: elfreloc1, + ElfrelocSize: 24, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + MachorelocSize: 8, + PEreloc1: pereloc1, + TLSIEtoLE: tlsIEtoLE, + + Linuxdynld: "/lib64/ld-linux-x86-64.so.2", + Freebsddynld: "/libexec/ld-elf.so.1", + Openbsddynld: "/usr/libexec/ld.so", + Netbsddynld: "/libexec/ld.elf_so", + Dragonflydynld: "/usr/libexec/ld-elf.so.2", + Solarisdynld: "/lib/amd64/ld.so.1", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + 8 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x200000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x200000 + } + + case objabi.Hdarwin: /* apple MACH */ + ld.HEADR = ld.INITIAL_MACHO_HEADR + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x1000000 + int64(ld.HEADR) + } + + case objabi.Hlinux, /* elf64 executable */ + objabi.Hfreebsd, /* freebsd */ + objabi.Hnetbsd, /* netbsd */ + objabi.Hopenbsd, /* openbsd */ + objabi.Hdragonfly, /* dragonfly */ + objabi.Hsolaris: /* solaris */ + ld.Elfinit(ctxt) + + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = (1 << 22) + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hwindows: /* PE executable */ + // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit + return + } +} diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go new file mode 100644 index 0000000..03caeae --- /dev/null +++ b/src/cmd/link/internal/arm/asm.go @@ -0,0 +1,657 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "fmt" + "log" +) + +// This assembler: +// +// .align 2 +// local.dso_init: +// ldr r0, .Lmoduledata +// .Lloadfrom: +// ldr r0, [r0] +// b runtime.addmoduledata@plt +// .align 2 +// .Lmoduledata: +// .word local.moduledata(GOT_PREL) + (. - (.Lloadfrom + 4)) +// assembles to: +// +// 00000000 <local.dso_init>: +// 0: e59f0004 ldr r0, [pc, #4] ; c <local.dso_init+0xc> +// 4: e5900000 ldr r0, [r0] +// 8: eafffffe b 0 <runtime.addmoduledata> +// 8: R_ARM_JUMP24 runtime.addmoduledata +// c: 00000004 .word 0x00000004 +// c: R_ARM_GOT_PREL local.moduledata + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt) + if initfunc == nil { + return + } + + o := func(op uint32) { + initfunc.AddUint32(ctxt.Arch, op) + } + o(0xe59f0004) + o(0xe08f0000) + + o(0xeafffffe) + rel, _ := initfunc.AddRel(objabi.R_CALLARM) + rel.SetOff(8) + rel.SetSiz(4) + rel.SetSym(addmoduledata) + rel.SetAdd(0xeafffffe) // vomit + + o(0x00000000) + + rel2, _ := initfunc.AddRel(objabi.R_PCREL) + rel2.SetOff(12) + rel2.SetSiz(4) + rel2.SetSym(ctxt.Moduledata) + rel2.SetAdd(4) +} + +// Preserve highest 8 bits of a, and do addition to lower 24-bit +// of a and b; used to adjust ARM branch instruction's target +func braddoff(a int32, b int32) int32 { + return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) +} + +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch r.Type() { + default: + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PLT32): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM) + + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_THM_PC22): // R_ARM_THM_CALL + ld.Exitf("R_ARM_THM_CALL, are you using -marm?") + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT32): // R_ARM_GOT_BREL + if targType != sym.SDYNIMPORT { + addgotsyminternal(target, ldr, syms, targ) + } else { + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT)) + } + + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym + su.SetRelocSym(rIdx, 0) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOT_PREL): // GOT(nil) + A - nil + if targType != sym.SDYNIMPORT { + addgotsyminternal(target, ldr, syms, targ) + } else { + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_ARM_GLOB_DAT)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+4+int64(ldr.SymGot(targ))) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTOFF): // R_ARM_GOTOFF32 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_GOTOFF) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_GOTPC): // R_ARM_BASE_PREL + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+4) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_CALL): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM) + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_REL32): // R_ARM_REL32 + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_ABS32): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_ARM_ABS32 relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_PC24), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_ARM_JUMP24): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM) + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) + } + + return true + } + + // Handle references to ELF symbols from our own object files. + if targType != sym.SDYNIMPORT { + return true + } + + // Reread the reloc to incorporate any changes in type above. + relocs := ldr.Relocs(s) + r = relocs.At(rIdx) + + switch r.Type() { + case objabi.R_CALLARM: + if target.IsExternal() { + // External linker will do this relocation. + return true + } + addpltsym(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(braddoff(int32(r.Add()), ldr.SymPlt(targ)/4))) // TODO: don't use r.Add for instruction bytes (issue 19811) + return true + + case objabi.R_ADDR: + if ldr.SymType(s) != sym.SDATA { + break + } + if target.IsElf() { + ld.Adddynsym(ldr, target, syms, targ) + rel := ldr.MakeSymbolUpdater(syms.Rel) + rel.AddAddrPlus(target.Arch, s, int64(r.Off())) + rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(targ)), uint32(elf.R_ARM_GLOB_DAT))) // we need a nil + A dynamic reloc + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym + su.SetRelocSym(rIdx, 0) + return true + } + } + + return false +} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + out.Write32(uint32(sectoff)) + + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + siz := r.Size + switch r.Type { + default: + return false + case objabi.R_ADDR, objabi.R_DWARFSECREF: + if siz == 4 { + out.Write32(uint32(elf.R_ARM_ABS32) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_PCREL: + if siz == 4 { + out.Write32(uint32(elf.R_ARM_REL32) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_CALLARM: + if siz == 4 { + relocs := ldr.Relocs(s) + r := relocs.At(ri) + if r.Add()&0xff000000 == 0xeb000000 { // BL // TODO: using r.Add here is bad (issue 19811) + out.Write32(uint32(elf.R_ARM_CALL) | uint32(elfsym)<<8) + } else { + out.Write32(uint32(elf.R_ARM_JUMP24) | uint32(elfsym)<<8) + } + } else { + return false + } + case objabi.R_TLS_LE: + out.Write32(uint32(elf.R_ARM_TLS_LE32) | uint32(elfsym)<<8) + case objabi.R_TLS_IE: + out.Write32(uint32(elf.R_ARM_TLS_IE32) | uint32(elfsym)<<8) + case objabi.R_GOTPCREL: + if siz == 4 { + out.Write32(uint32(elf.R_ARM_GOT_PREL) | uint32(elfsym)<<8) + } else { + return false + } + } + + return true +} + +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { + // str lr, [sp, #-4]! + plt.AddUint32(ctxt.Arch, 0xe52de004) + + // ldr lr, [pc, #4] + plt.AddUint32(ctxt.Arch, 0xe59fe004) + + // add lr, pc, lr + plt.AddUint32(ctxt.Arch, 0xe08fe00e) + + // ldr pc, [lr, #8]! + plt.AddUint32(ctxt.Arch, 0xe5bef008) + + // .word &GLOBAL_OFFSET_TABLE[0] - . + plt.AddPCRelPlus(ctxt.Arch, got.Sym(), 4) + + // the first .plt entry requires 3 .plt.got entries + got.AddUint32(ctxt.Arch, 0) + + got.AddUint32(ctxt.Arch, 0) + got.AddUint32(ctxt.Arch, 0) + } +} + +func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { + return false +} + +func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool { + rs := r.Xsym + rt := r.Type + + if ldr.SymDynid(rs) < 0 { + ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs)) + return false + } + + out.Write32(uint32(sectoff)) + out.Write32(uint32(ldr.SymDynid(rs))) + + var v uint32 + switch rt { + default: + // unsupported relocation type + return false + + case objabi.R_DWARFSECREF: + v = ld.IMAGE_REL_ARM_SECREL + + case objabi.R_ADDR: + v = ld.IMAGE_REL_ARM_ADDR32 + } + + out.Write16(uint16(v)) + + return true +} + +// sign extend a 24-bit integer +func signext24(x int64) int32 { + return (int32(x) << 8) >> 8 +} + +// encode an immediate in ARM's imm12 format. copied from ../../../internal/obj/arm/asm5.go +func immrot(v uint32) uint32 { + for i := 0; i < 16; i++ { + if v&^0xff == 0 { + return uint32(i<<8) | v | 1<<25 + } + v = v<<2 | v>>30 + } + return 0 +} + +// Convert the direct jump relocation r to refer to a trampoline if the target is too far +func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) { + relocs := ldr.Relocs(s) + r := relocs.At(ri) + switch r.Type() { + case objabi.R_CALLARM: + var t int64 + // ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet + // laid out. Conservatively use a trampoline. This should be rare, as we lay out packages + // in dependency order. + if ldr.SymValue(rs) != 0 { + // r.Add is the instruction + // low 24-bit encodes the target address + t = (ldr.SymValue(rs) + int64(signext24(r.Add()&0xffffff)*4) - (ldr.SymValue(s) + int64(r.Off()))) / 4 + } + if t > 0x7fffff || t < -0x800000 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && ldr.SymPkg(s) != ldr.SymPkg(rs)) { + // direct call too far, need to insert trampoline. + // look up existing trampolines first. if we found one within the range + // of direct call, we can reuse it. otherwise create a new one. + offset := (signext24(r.Add()&0xffffff) + 2) * 4 + var tramp loader.Sym + for i := 0; ; i++ { + oName := ldr.SymName(rs) + name := oName + fmt.Sprintf("%+d-tramp%d", offset, i) + tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs))) + ldr.SetAttrReachable(tramp, true) + if ldr.SymType(tramp) == sym.SDYNIMPORT { + // don't reuse trampoline defined in other module + continue + } + if oName == "runtime.deferreturn" { + ldr.SetIsDeferReturnTramp(tramp, true) + } + if ldr.SymValue(tramp) == 0 { + // either the trampoline does not exist -- we need to create one, + // or found one the address which is not assigned -- this will be + // laid down immediately after the current function. use this one. + break + } + + t = (ldr.SymValue(tramp) - 8 - (ldr.SymValue(s) + int64(r.Off()))) / 4 + if t >= -0x800000 && t < 0x7fffff { + // found an existing trampoline that is not too far + // we can just use it + break + } + } + if ldr.SymType(tramp) == 0 { + // trampoline does not exist, create one + trampb := ldr.MakeSymbolUpdater(tramp) + ctxt.AddTramp(trampb) + if ctxt.DynlinkingGo() { + if immrot(uint32(offset)) == 0 { + ctxt.Errorf(s, "odd offset in dynlink direct call: %v+%d", ldr.SymName(rs), offset) + } + gentrampdyn(ctxt.Arch, trampb, rs, int64(offset)) + } else if ctxt.BuildMode == ld.BuildModeCArchive || ctxt.BuildMode == ld.BuildModeCShared || ctxt.BuildMode == ld.BuildModePIE { + gentramppic(ctxt.Arch, trampb, rs, int64(offset)) + } else { + gentramp(ctxt.Arch, ctxt.LinkMode, ldr, trampb, rs, int64(offset)) + } + } + // modify reloc to point to tramp, which will be resolved later + sb := ldr.MakeSymbolUpdater(s) + relocs := sb.Relocs() + r := relocs.At(ri) + r.SetSym(tramp) + r.SetAdd(r.Add()&0xff000000 | 0xfffffe) // clear the offset embedded in the instruction + } + default: + ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type())) + } +} + +// generate a trampoline to target+offset +func gentramp(arch *sys.Arch, linkmode ld.LinkMode, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { + tramp.SetSize(12) // 3 instructions + P := make([]byte, tramp.Size()) + t := ldr.SymValue(target) + offset + o1 := uint32(0xe5900000 | 11<<12 | 15<<16) // MOVW (R15), R11 // R15 is actual pc + 8 + o2 := uint32(0xe12fff10 | 11) // JMP (R11) + o3 := uint32(t) // WORD $target + arch.ByteOrder.PutUint32(P, o1) + arch.ByteOrder.PutUint32(P[4:], o2) + arch.ByteOrder.PutUint32(P[8:], o3) + tramp.SetData(P) + + if linkmode == ld.LinkExternal || ldr.SymValue(target) == 0 { + r, _ := tramp.AddRel(objabi.R_ADDR) + r.SetOff(8) + r.SetSiz(4) + r.SetSym(target) + r.SetAdd(offset) + } +} + +// generate a trampoline to target+offset in position independent code +func gentramppic(arch *sys.Arch, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { + tramp.SetSize(16) // 4 instructions + P := make([]byte, tramp.Size()) + o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 4) // MOVW 4(R15), R11 // R15 is actual pc + 8 + o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11 + o3 := uint32(0xe12fff10 | 11) // JMP (R11) + o4 := uint32(0) // WORD $(target-pc) // filled in with relocation + arch.ByteOrder.PutUint32(P, o1) + arch.ByteOrder.PutUint32(P[4:], o2) + arch.ByteOrder.PutUint32(P[8:], o3) + arch.ByteOrder.PutUint32(P[12:], o4) + tramp.SetData(P) + + r, _ := tramp.AddRel(objabi.R_PCREL) + r.SetOff(12) + r.SetSiz(4) + r.SetSym(target) + r.SetAdd(offset + 4) +} + +// generate a trampoline to target+offset in dynlink mode (using GOT) +func gentrampdyn(arch *sys.Arch, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { + tramp.SetSize(20) // 5 instructions + o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 8) // MOVW 8(R15), R11 // R15 is actual pc + 8 + o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11 + o3 := uint32(0xe5900000 | 11<<12 | 11<<16) // MOVW (R11), R11 + o4 := uint32(0xe12fff10 | 11) // JMP (R11) + o5 := uint32(0) // WORD $target@GOT // filled in with relocation + o6 := uint32(0) + if offset != 0 { + // insert an instruction to add offset + tramp.SetSize(24) // 6 instructions + o6 = o5 + o5 = o4 + o4 = 0xe2800000 | 11<<12 | 11<<16 | immrot(uint32(offset)) // ADD $offset, R11, R11 + o1 = uint32(0xe5900000 | 11<<12 | 15<<16 | 12) // MOVW 12(R15), R11 + } + P := make([]byte, tramp.Size()) + arch.ByteOrder.PutUint32(P, o1) + arch.ByteOrder.PutUint32(P[4:], o2) + arch.ByteOrder.PutUint32(P[8:], o3) + arch.ByteOrder.PutUint32(P[12:], o4) + arch.ByteOrder.PutUint32(P[16:], o5) + if offset != 0 { + arch.ByteOrder.PutUint32(P[20:], o6) + } + tramp.SetData(P) + + r, _ := tramp.AddRel(objabi.R_GOTPCREL) + r.SetOff(16) + r.SetSiz(4) + r.SetSym(target) + r.SetAdd(8) + if offset != 0 { + // increase reloc offset by 4 as we inserted an ADD instruction + r.SetOff(20) + r.SetAdd(12) + } +} + +func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) { + rs := r.Sym() + rs = ldr.ResolveABIAlias(rs) + if target.IsExternal() { + switch r.Type() { + case objabi.R_CALLARM: + // set up addend for eventual relocation via outer symbol. + _, off := ld.FoldSubSymbolOffset(ldr, rs) + xadd := int64(signext24(r.Add()&0xffffff))*4 + off + if xadd/4 > 0x7fffff || xadd/4 < -0x800000 { + ldr.Errorf(s, "direct call too far %d", xadd/4) + } + return int64(braddoff(int32(0xff000000&uint32(r.Add())), int32(0xffffff&uint32(xadd/4)))), 1, true + } + return -1, 0, false + } + + const isOk = true + const noExtReloc = 0 + switch r.Type() { + // The following three arch specific relocations are only for generation of + // Linux/ARM ELF's PLT entry (3 assembler instruction) + case objabi.R_PLT0: // add ip, pc, #0xXX00000 + if ldr.SymValue(syms.GOTPLT) < ldr.SymValue(syms.PLT) { + ldr.Errorf(s, ".got.plt should be placed after .plt section.") + } + return 0xe28fc600 + (0xff & (int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add())) >> 20)), noExtReloc, isOk + case objabi.R_PLT1: // add ip, ip, #0xYY000 + return 0xe28cca00 + (0xff & (int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add()+4)) >> 12)), noExtReloc, isOk + case objabi.R_PLT2: // ldr pc, [ip, #0xZZZ]! + return 0xe5bcf000 + (0xfff & int64(uint32(ldr.SymValue(rs)-(ldr.SymValue(syms.PLT)+int64(r.Off()))+r.Add()+8))), noExtReloc, isOk + case objabi.R_CALLARM: // bl XXXXXX or b YYYYYY + // r.Add is the instruction + // low 24-bit encodes the target address + t := (ldr.SymValue(rs) + int64(signext24(r.Add()&0xffffff)*4) - (ldr.SymValue(s) + int64(r.Off()))) / 4 + if t > 0x7fffff || t < -0x800000 { + ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t) + } + return int64(braddoff(int32(0xff000000&uint32(r.Add())), int32(0xffffff&t))), noExtReloc, isOk + } + + return val, 0, false +} + +func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 { + log.Fatalf("unexpected relocation variant") + return -1 +} + +func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) { + rs := ldr.ResolveABIAlias(r.Sym()) + var rr loader.ExtReloc + switch r.Type() { + case objabi.R_CALLARM: + // set up addend for eventual relocation via outer symbol. + rs, off := ld.FoldSubSymbolOffset(ldr, rs) + rr.Xadd = int64(signext24(r.Add()&0xffffff))*4 + off + rst := ldr.SymType(rs) + if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil { + ldr.Errorf(s, "missing section for %s", ldr.SymName(rs)) + } + rr.Xsym = rs + rr.Type = r.Type() + rr.Size = r.Siz() + return rr, true + } + return rr, false +} + +func addpltreloc(ldr *loader.Loader, plt *loader.SymbolBuilder, got *loader.SymbolBuilder, s loader.Sym, typ objabi.RelocType) { + r, _ := plt.AddRel(typ) + r.SetSym(got.Sym()) + r.SetOff(int32(plt.Size())) + r.SetSiz(4) + r.SetAdd(int64(ldr.SymGot(s)) - 8) + + plt.SetReachable(true) + plt.SetSize(plt.Size() + 4) + plt.Grow(plt.Size()) +} + +func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { + return + } + + ld.Adddynsym(ldr, target, syms, s) + + if target.IsElf() { + plt := ldr.MakeSymbolUpdater(syms.PLT) + got := ldr.MakeSymbolUpdater(syms.GOTPLT) + rel := ldr.MakeSymbolUpdater(syms.RelPLT) + if plt.Size() == 0 { + panic("plt is not set up") + } + + // .got entry + ldr.SetGot(s, int32(got.Size())) + + // In theory, all GOT should point to the first PLT entry, + // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's + // dynamic linker won't, so we'd better do it ourselves. + got.AddAddrPlus(target.Arch, plt.Sym(), 0) + + // .plt entry, this depends on the .got entry + ldr.SetPlt(s, int32(plt.Size())) + + addpltreloc(ldr, plt, got, s, objabi.R_PLT0) // add lr, pc, #0xXX00000 + addpltreloc(ldr, plt, got, s, objabi.R_PLT1) // add lr, lr, #0xYY000 + addpltreloc(ldr, plt, got, s, objabi.R_PLT2) // ldr pc, [lr, #0xZZZ]! + + // rel + rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + + rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), uint32(elf.R_ARM_JUMP_SLOT))) + } else { + ldr.Errorf(s, "addpltsym: unsupported binary format") + } +} + +func addgotsyminternal(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymGot(s) >= 0 { + return + } + + got := ldr.MakeSymbolUpdater(syms.GOT) + ldr.SetGot(s, int32(got.Size())) + got.AddAddrPlus(target.Arch, s, 0) + + if target.IsElf() { + } else { + ldr.Errorf(s, "addgotsyminternal: unsupported binary format") + } +} diff --git a/src/cmd/link/internal/arm/l.go b/src/cmd/link/internal/arm/l.go new file mode 100644 index 0000000..d16dc27 --- /dev/null +++ b/src/cmd/link/internal/arm/l.go @@ -0,0 +1,75 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +// Writing object files. + +// Inferno utils/5l/l.h +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 8 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 4 // single-instruction alignment +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 13 + dwarfRegLR = 14 +) diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go new file mode 100644 index 0000000..fed8dce --- /dev/null +++ b/src/cmd/link/internal/arm/obj.go @@ -0,0 +1,109 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchARM + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Plan9Magic: 0x647, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Extreloc: extreloc, + Trampoline: trampoline, + Elfreloc1: elfreloc1, + ElfrelocSize: 8, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + PEreloc1: pereloc1, + + Linuxdynld: "/lib/ld-linux.so.3", // 2 for OABI, 3 for EABI + Freebsddynld: "/usr/libexec/ld-elf.so.1", + Openbsddynld: "/usr/libexec/ld.so", + Netbsddynld: "/libexec/ld.elf_so", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4128 + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hlinux, /* arm elf */ + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + *ld.FlagD = false + // with dynamic linking + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + + case objabi.Hwindows: /* PE executable */ + // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit + return + } +} diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go new file mode 100644 index 0000000..14a20a1 --- /dev/null +++ b/src/cmd/link/internal/arm64/asm.go @@ -0,0 +1,1048 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "fmt" + "log" +) + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt) + if initfunc == nil { + return + } + + o := func(op uint32) { + initfunc.AddUint32(ctxt.Arch, op) + } + // 0000000000000000 <local.dso_init>: + // 0: 90000000 adrp x0, 0 <runtime.firstmoduledata> + // 0: R_AARCH64_ADR_PREL_PG_HI21 local.moduledata + // 4: 91000000 add x0, x0, #0x0 + // 4: R_AARCH64_ADD_ABS_LO12_NC local.moduledata + o(0x90000000) + o(0x91000000) + rel, _ := initfunc.AddRel(objabi.R_ADDRARM64) + rel.SetOff(0) + rel.SetSiz(8) + rel.SetSym(ctxt.Moduledata) + + // 8: 14000000 b 0 <runtime.addmoduledata> + // 8: R_AARCH64_CALL26 runtime.addmoduledata + o(0x14000000) + rel2, _ := initfunc.AddRel(objabi.R_CALLARM64) + rel2.SetOff(8) + rel2.SetSiz(4) + rel2.SetSym(addmoduledata) +} + +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + const pcrel = 1 + switch r.Type() { + default: + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL32): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_AARCH64_PREL32 relocation for dynamic symbol %s", ldr.SymName(targ)) + } + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_PREL64): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_AARCH64_PREL64 relocation for dynamic symbol %s", ldr.SymName(targ)) + } + if targType == 0 || targType == sym.SXREF { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+8) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_CALL26), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_JUMP26): + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) + } + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in callarm64", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM64) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_GOT_PAGE), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LD64_GOT_LO12_NC): + if targType != sym.SDYNIMPORT { + // have symbol + // TODO: turn LDR of GOT entry into ADR of symbol itself + } + + // fall back to using GOT + // TODO: just needs relocation, no need to put in .dynsym + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_AARCH64_GLOB_DAT)) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_GOT) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADR_PREL_PG_HI21), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ADD_ABS_LO12_NC): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + if targType == 0 || targType == sym.SXREF { + ldr.Errorf(s, "unknown symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_PCREL) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_ABS64): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_AARCH64_ABS64 relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + if target.IsPIE() && target.IsInternal() { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + break + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST8_ABS_LO12_NC): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_LDST8) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST16_ABS_LO12_NC): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_LDST16) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST32_ABS_LO12_NC): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_LDST32) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST64_ABS_LO12_NC): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_LDST64) + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_AARCH64_LDST128_ABS_LO12_NC): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_LDST128) + return true + + // Handle relocations found in Mach-O object files. + case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_UNSIGNED*2: + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + if target.IsPIE() && target.IsInternal() { + // For internal linking PIE, this R_ADDR relocation cannot + // be resolved statically. We need to generate a dynamic + // relocation. Let the code below handle it. + break + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_BRANCH26*2 + pcrel: + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLARM64) + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_PAGE21*2 + pcrel, + objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_PAGEOFF12*2: + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_PCREL) + return true + + case objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21*2 + pcrel, + objabi.MachoRelocOffset + ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12*2: + if targType != sym.SDYNIMPORT { + // have symbol + // turn MOVD sym@GOT (adrp+ldr) into MOVD $sym (adrp+add) + data := ldr.Data(s) + off := r.Off() + if int(off+3) >= len(data) { + ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ)) + return false + } + o := target.Arch.ByteOrder.Uint32(data[off:]) + su := ldr.MakeSymbolUpdater(s) + switch { + case (o>>24)&0x9f == 0x90: // adrp + // keep instruction unchanged, change relocation type below + case o>>24 == 0xf9: // ldr + // rewrite to add + o = (0x91 << 24) | (o & (1<<22 - 1)) + su.MakeWritable() + su.SetUint32(target.Arch, int64(off), o) + default: + ldr.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", ldr.SymName(targ)) + return false + } + su.SetRelocType(rIdx, objabi.R_ARM64_PCREL) + return true + } + ld.AddGotSym(target, ldr, syms, targ, 0) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ARM64_GOT) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, int64(ldr.SymGot(targ))) + return true + } + + // Reread the reloc to incorporate any changes in type above. + relocs := ldr.Relocs(s) + r = relocs.At(rIdx) + + switch r.Type() { + case objabi.R_CALL, + objabi.R_PCREL, + objabi.R_CALLARM64: + if targType != sym.SDYNIMPORT { + // nothing to do, the relocation will be laid out in reloc + return true + } + if target.IsExternal() { + // External linker will do this relocation. + return true + } + // Internal linking. + if r.Add() != 0 { + ldr.Errorf(s, "PLT call with non-zero addend (%v)", r.Add()) + } + // Build a PLT entry and change the relocation target to that entry. + addpltsym(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) + return true + + case objabi.R_ADDR: + if ldr.SymType(s) == sym.STEXT && target.IsElf() { + // The code is asking for the address of an external + // function. We provide it with the address of the + // correspondent GOT symbol. + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_AARCH64_GLOB_DAT)) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + return true + } + + // Process dynamic relocations for the data sections. + if target.IsPIE() && target.IsInternal() { + // When internally linking, generate dynamic relocations + // for all typical R_ADDR relocations. The exception + // are those R_ADDR that are created as part of generating + // the dynamic relocations and must be resolved statically. + // + // There are three phases relevant to understanding this: + // + // dodata() // we are here + // address() // symbol address assignment + // reloc() // resolution of static R_ADDR relocs + // + // At this point symbol addresses have not been + // assigned yet (as the final size of the .rela section + // will affect the addresses), and so we cannot write + // the Elf64_Rela.r_offset now. Instead we delay it + // until after the 'address' phase of the linker is + // complete. We do this via Addaddrplus, which creates + // a new R_ADDR relocation which will be resolved in + // the 'reloc' phase. + // + // These synthetic static R_ADDR relocs must be skipped + // now, or else we will be caught in an infinite loop + // of generating synthetic relocs for our synthetic + // relocs. + // + // Furthermore, the rela sections contain dynamic + // relocations with R_ADDR relocations on + // Elf64_Rela.r_offset. This field should contain the + // symbol offset as determined by reloc(), not the + // final dynamically linked address as a dynamic + // relocation would provide. + switch ldr.SymName(s) { + case ".dynsym", ".rela", ".rela.plt", ".got.plt", ".dynamic": + return false + } + } else { + // Either internally linking a static executable, + // in which case we can resolve these relocations + // statically in the 'reloc' phase, or externally + // linking, in which case the relocation will be + // prepared in the 'reloc' phase and passed to the + // external linker in the 'asmb' phase. + if ldr.SymType(s) != sym.SDATA && ldr.SymType(s) != sym.SRODATA { + break + } + } + + if target.IsElf() { + // Generate R_AARCH64_RELATIVE relocations for best + // efficiency in the dynamic linker. + // + // As noted above, symbol addresses have not been + // assigned yet, so we can't generate the final reloc + // entry yet. We ultimately want: + // + // r_offset = s + r.Off + // r_info = R_AARCH64_RELATIVE + // r_addend = targ + r.Add + // + // The dynamic linker will set *offset = base address + + // addend. + // + // AddAddrPlus is used for r_offset and r_addend to + // generate new R_ADDR relocations that will update + // these fields in the 'reloc' phase. + rela := ldr.MakeSymbolUpdater(syms.Rela) + rela.AddAddrPlus(target.Arch, s, int64(r.Off())) + if r.Siz() == 8 { + rela.AddUint64(target.Arch, elf.R_INFO(0, uint32(elf.R_AARCH64_RELATIVE))) + } else { + ldr.Errorf(s, "unexpected relocation for dynamic symbol %s", ldr.SymName(targ)) + } + rela.AddAddrPlus(target.Arch, targ, int64(r.Add())) + // Not mark r done here. So we still apply it statically, + // so in the file content we'll also have the right offset + // to the relocation target. So it can be examined statically + // (e.g. go version). + return true + } + + if target.IsDarwin() { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation. + // Here we record what are needed and encode them later. + ld.MachoAddRebase(s, int64(r.Off())) + // Not mark r done here. So we still apply it statically, + // so in the file content we'll also have the right offset + // to the relocation target. So it can be examined statically + // (e.g. go version). + return true + } + } + return false +} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + out.Write64(uint64(sectoff)) + + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + siz := r.Size + switch r.Type { + default: + return false + case objabi.R_ADDR, objabi.R_DWARFSECREF: + switch siz { + case 4: + out.Write64(uint64(elf.R_AARCH64_ABS32) | uint64(elfsym)<<32) + case 8: + out.Write64(uint64(elf.R_AARCH64_ABS64) | uint64(elfsym)<<32) + default: + return false + } + case objabi.R_ADDRARM64: + // two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_ADD_ABS_LO12_NC + out.Write64(uint64(elf.R_AARCH64_ADR_PREL_PG_HI21) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_AARCH64_ADD_ABS_LO12_NC) | uint64(elfsym)<<32) + case objabi.R_ARM64_TLS_LE: + out.Write64(uint64(elf.R_AARCH64_TLSLE_MOVW_TPREL_G0) | uint64(elfsym)<<32) + case objabi.R_ARM64_TLS_IE: + out.Write64(uint64(elf.R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) | uint64(elfsym)<<32) + case objabi.R_ARM64_GOTPCREL: + out.Write64(uint64(elf.R_AARCH64_ADR_GOT_PAGE) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_AARCH64_LD64_GOT_LO12_NC) | uint64(elfsym)<<32) + case objabi.R_CALLARM64: + if siz != 4 { + return false + } + out.Write64(uint64(elf.R_AARCH64_CALL26) | uint64(elfsym)<<32) + + } + out.Write64(uint64(r.Xadd)) + + return true +} + +// sign-extends from 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 + + rs := r.Xsym + rt := r.Type + siz := r.Size + xadd := r.Xadd + + if xadd != signext24(xadd) { + // If the relocation target would overflow the addend, then target + // a linker-manufactured label symbol with a smaller addend instead. + label := ldr.Lookup(machoLabelName(ldr, rs, xadd), ldr.SymVersion(rs)) + if label != 0 { + xadd = ldr.SymValue(rs) + xadd - ldr.SymValue(label) + rs = label + } + if xadd != signext24(xadd) { + ldr.Errorf(s, "internal error: relocation addend overflow: %s+0x%x", ldr.SymName(rs), xadd) + } + } + + if 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 + } + + v = uint32(ldr.SymDynid(rs)) + v |= 1 << 27 // external relocation + } else { + v = uint32(ldr.SymSect(rs).Extnum) + if v == 0 { + ldr.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymSect(rs).Name, ldr.SymType(rs), ldr.SymType(rs)) + return false + } + } + + switch rt { + default: + return false + case objabi.R_ADDR: + v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28 + case objabi.R_CALLARM64: + if xadd != 0 { + ldr.Errorf(s, "ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", ldr.SymName(rs), xadd) + } + + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM64_RELOC_BRANCH26 << 28 + case objabi.R_ADDRARM64: + siz = 4 + // Two relocation entries: MACHO_ARM64_RELOC_PAGEOFF12 MACHO_ARM64_RELOC_PAGE21 + // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND. + if r.Xadd != 0 { + out.Write32(uint32(sectoff + 4)) + out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff)) + } + out.Write32(uint32(sectoff + 4)) + out.Write32(v | (ld.MACHO_ARM64_RELOC_PAGEOFF12 << 28) | (2 << 25)) + if r.Xadd != 0 { + out.Write32(uint32(sectoff)) + out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff)) + } + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28 + case objabi.R_ARM64_GOTPCREL: + siz = 4 + // Two relocation entries: MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 + // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND. + if r.Xadd != 0 { + out.Write32(uint32(sectoff + 4)) + out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff)) + } + out.Write32(uint32(sectoff + 4)) + out.Write32(v | (ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28) | (2 << 25)) + if r.Xadd != 0 { + out.Write32(uint32(sectoff)) + out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(xadd&0xffffff)) + } + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28 + } + + switch siz { + default: + return false + case 1: + v |= 0 << 25 + case 2: + v |= 1 << 25 + case 4: + v |= 2 << 25 + case 8: + v |= 3 << 25 + } + + out.Write32(uint32(sectoff)) + out.Write32(v) + return true +} + +func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (int64, int, bool) { + const noExtReloc = 0 + const isOk = true + + rs := ldr.ResolveABIAlias(r.Sym()) + + if target.IsExternal() { + nExtReloc := 0 + switch rt := r.Type(); rt { + default: + case objabi.R_ARM64_GOTPCREL, + objabi.R_ADDRARM64: + + // set up addend for eventual relocation via outer symbol. + rs, off := ld.FoldSubSymbolOffset(ldr, rs) + xadd := r.Add() + off + rst := ldr.SymType(rs) + if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil { + ldr.Errorf(s, "missing section for %s", ldr.SymName(rs)) + } + + nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1 + if target.IsDarwin() && xadd != 0 { + nExtReloc = 4 // need another two relocations for non-zero addend + } + + // Note: ld64 currently has a bug that any non-zero addend for BR26 relocation + // will make the linking fail because it thinks the code is not PIC even though + // the BR26 relocation should be fully resolved at link time. + // That is the reason why the next if block is disabled. When the bug in ld64 + // is fixed, we can enable this block and also enable duff's device in cmd/7g. + if false && target.IsDarwin() { + var o0, o1 uint32 + + if target.IsBigEndian() { + o0 = uint32(val >> 32) + o1 = uint32(val) + } else { + o0 = uint32(val) + o1 = uint32(val >> 32) + } + // Mach-O wants the addend to be encoded in the instruction + // Note that although Mach-O supports ARM64_RELOC_ADDEND, it + // can only encode 24-bit of signed addend, but the instructions + // supports 33-bit of signed addend, so we always encode the + // addend in place. + o0 |= (uint32((xadd>>12)&3) << 29) | (uint32((xadd>>12>>2)&0x7ffff) << 5) + o1 |= uint32(xadd&0xfff) << 10 + + // when laid out, the instruction order must always be o1, o2. + if target.IsBigEndian() { + val = int64(o0)<<32 | int64(o1) + } else { + val = int64(o1)<<32 | int64(o0) + } + } + + return val, nExtReloc, isOk + case objabi.R_CALLARM64, + objabi.R_ARM64_TLS_LE, + objabi.R_ARM64_TLS_IE: + nExtReloc = 1 + if rt == objabi.R_ARM64_TLS_IE { + nExtReloc = 2 // need two ELF relocations. see elfreloc1 + } + return val, nExtReloc, isOk + } + } + + switch r.Type() { + case objabi.R_ADDRARM64: + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t >= 1<<32 || t < -1<<32 { + ldr.Errorf(s, "program too large, address relocation distance = %d", t) + } + + var o0, o1 uint32 + + if target.IsBigEndian() { + o0 = uint32(val >> 32) + o1 = uint32(val) + } else { + o0 = uint32(val) + o1 = uint32(val >> 32) + } + + o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) + o1 |= uint32(t&0xfff) << 10 + + // when laid out, the instruction order must always be o1, o2. + if target.IsBigEndian() { + return int64(o0)<<32 | int64(o1), noExtReloc, true + } + return int64(o1)<<32 | int64(o0), noExtReloc, true + + case objabi.R_ARM64_TLS_LE: + if target.IsDarwin() { + ldr.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType) + } + // The TCB is two pointers. This is not documented anywhere, but is + // de facto part of the ABI. + v := ldr.SymValue(rs) + int64(2*target.Arch.PtrSize) + if v < 0 || v >= 32678 { + ldr.Errorf(s, "TLS offset out of range %d", v) + } + return val | (v << 5), noExtReloc, true + + case objabi.R_ARM64_TLS_IE: + if target.IsPIE() && target.IsElf() { + // We are linking the final executable, so we + // can optimize any TLS IE relocation to LE. + + if !target.IsLinux() { + ldr.Errorf(s, "TLS reloc on unsupported OS %v", target.HeadType) + } + + // The TCB is two pointers. This is not documented anywhere, but is + // de facto part of the ABI. + v := ldr.SymAddr(rs) + int64(2*target.Arch.PtrSize) + r.Add() + if v < 0 || v >= 32678 { + ldr.Errorf(s, "TLS offset out of range %d", v) + } + + var o0, o1 uint32 + if target.IsBigEndian() { + o0 = uint32(val >> 32) + o1 = uint32(val) + } else { + o0 = uint32(val) + o1 = uint32(val >> 32) + } + + // R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 + // turn ADRP to MOVZ + o0 = 0xd2a00000 | uint32(o0&0x1f) | (uint32((v>>16)&0xffff) << 5) + // R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC + // turn LD64 to MOVK + if v&3 != 0 { + ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC", v) + } + o1 = 0xf2800000 | uint32(o1&0x1f) | (uint32(v&0xffff) << 5) + + // when laid out, the instruction order must always be o0, o1. + if target.IsBigEndian() { + return int64(o0)<<32 | int64(o1), noExtReloc, isOk + } + return int64(o1)<<32 | int64(o0), noExtReloc, isOk + } else { + log.Fatalf("cannot handle R_ARM64_TLS_IE (sym %s) when linking internally", ldr.SymName(s)) + } + + case objabi.R_CALLARM64: + var t int64 + if ldr.SymType(rs) == sym.SDYNIMPORT { + t = (ldr.SymAddr(syms.PLT) + r.Add()) - (ldr.SymValue(s) + int64(r.Off())) + } else { + t = (ldr.SymAddr(rs) + r.Add()) - (ldr.SymValue(s) + int64(r.Off())) + } + if t >= 1<<27 || t < -1<<27 { + ldr.Errorf(s, "program too large, call relocation distance = %d", t) + } + return val | ((t >> 2) & 0x03ffffff), noExtReloc, true + + case objabi.R_ARM64_GOT: + if (val>>24)&0x9f == 0x90 { + // R_AARCH64_ADR_GOT_PAGE + // patch instruction: adrp + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t >= 1<<32 || t < -1<<32 { + ldr.Errorf(s, "program too large, address relocation distance = %d", t) + } + var o0 uint32 + o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) + return val | int64(o0), noExtReloc, isOk + } else if val>>24 == 0xf9 { + // R_AARCH64_LD64_GOT_LO12_NC + // patch instruction: ldr + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t&7 != 0 { + ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LD64_GOT_LO12_NC", t) + } + var o1 uint32 + o1 |= uint32(t&0xfff) << (10 - 3) + return val | int64(uint64(o1)), noExtReloc, isOk + } else { + ldr.Errorf(s, "unsupported instruction for %x R_GOTARM64", val) + } + + case objabi.R_ARM64_PCREL: + if (val>>24)&0x9f == 0x90 { + // R_AARCH64_ADR_PREL_PG_HI21 + // patch instruction: adrp + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t >= 1<<32 || t < -1<<32 { + ldr.Errorf(s, "program too large, address relocation distance = %d", t) + } + o0 := (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) + return val | int64(o0), noExtReloc, isOk + } else if (val>>24)&0x9f == 0x91 { + // ELF R_AARCH64_ADD_ABS_LO12_NC or Mach-O ARM64_RELOC_PAGEOFF12 + // patch instruction: add + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + o1 := uint32(t&0xfff) << 10 + return val | int64(o1), noExtReloc, isOk + } else if (val>>24)&0x3b == 0x39 { + // Mach-O ARM64_RELOC_PAGEOFF12 + // patch ldr/str(b/h/w/d/q) (integer or vector) instructions, which have different scaling factors. + // Mach-O uses same relocation type for them. + shift := uint32(val) >> 30 + if shift == 0 && (val>>20)&0x048 == 0x048 { // 128-bit vector load + shift = 4 + } + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t&(1<<shift-1) != 0 { + ldr.Errorf(s, "invalid address: %x for relocation type: ARM64_RELOC_PAGEOFF12", t) + } + o1 := (uint32(t&0xfff) >> shift) << 10 + return val | int64(o1), noExtReloc, isOk + } else { + ldr.Errorf(s, "unsupported instruction for %x R_ARM64_PCREL", val) + } + + case objabi.R_ARM64_LDST8: + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + o0 := uint32(t&0xfff) << 10 + return val | int64(o0), noExtReloc, true + + case objabi.R_ARM64_LDST16: + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t&1 != 0 { + ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST16_ABS_LO12_NC", t) + } + o0 := (uint32(t&0xfff) >> 1) << 10 + return val | int64(o0), noExtReloc, true + + case objabi.R_ARM64_LDST32: + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t&3 != 0 { + ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST32_ABS_LO12_NC", t) + } + o0 := (uint32(t&0xfff) >> 2) << 10 + return val | int64(o0), noExtReloc, true + + case objabi.R_ARM64_LDST64: + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t&7 != 0 { + ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST64_ABS_LO12_NC", t) + } + o0 := (uint32(t&0xfff) >> 3) << 10 + return val | int64(o0), noExtReloc, true + + case objabi.R_ARM64_LDST128: + t := ldr.SymAddr(rs) + r.Add() - ((ldr.SymValue(s) + int64(r.Off())) &^ 0xfff) + if t&15 != 0 { + ldr.Errorf(s, "invalid address: %x for relocation type: R_AARCH64_LDST128_ABS_LO12_NC", t) + } + o0 := (uint32(t&0xfff) >> 4) << 10 + return val | int64(o0), noExtReloc, true + } + + return val, 0, false +} + +func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 { + log.Fatalf("unexpected relocation variant") + return -1 +} + +func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) { + switch rt := r.Type(); rt { + case objabi.R_ARM64_GOTPCREL, + objabi.R_ADDRARM64: + rr := ld.ExtrelocViaOuterSym(ldr, r, s) + + // Note: ld64 currently has a bug that any non-zero addend for BR26 relocation + // will make the linking fail because it thinks the code is not PIC even though + // the BR26 relocation should be fully resolved at link time. + // That is the reason why the next if block is disabled. When the bug in ld64 + // is fixed, we can enable this block and also enable duff's device in cmd/7g. + if false && target.IsDarwin() { + // Mach-O wants the addend to be encoded in the instruction + // Note that although Mach-O supports ARM64_RELOC_ADDEND, it + // can only encode 24-bit of signed addend, but the instructions + // supports 33-bit of signed addend, so we always encode the + // addend in place. + rr.Xadd = 0 + } + return rr, true + case objabi.R_CALLARM64, + objabi.R_ARM64_TLS_LE, + objabi.R_ARM64_TLS_IE: + return ld.ExtrelocSimple(ldr, r), true + } + return loader.ExtReloc{}, false +} + +func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { + // stp x16, x30, [sp, #-16]! + // identifying information + plt.AddUint32(ctxt.Arch, 0xa9bf7bf0) + + // the following two instructions (adrp + ldr) load *got[2] into x17 + // adrp x16, &got[0] + plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4) + plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x90000010) + + // <imm> is the offset value of &got[2] to &got[0], the same below + // ldr x17, [x16, <imm>] + plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_GOT, 4) + plt.SetUint32(ctxt.Arch, plt.Size()-4, 0xf9400211) + + // add x16, x16, <imm> + plt.AddSymRef(ctxt.Arch, gotplt.Sym(), 16, objabi.R_ARM64_PCREL, 4) + plt.SetUint32(ctxt.Arch, plt.Size()-4, 0x91000210) + + // br x17 + plt.AddUint32(ctxt.Arch, 0xd61f0220) + + // 3 nop for place holder + plt.AddUint32(ctxt.Arch, 0xd503201f) + plt.AddUint32(ctxt.Arch, 0xd503201f) + plt.AddUint32(ctxt.Arch, 0xd503201f) + + // check gotplt.size == 0 + if gotplt.Size() != 0 { + ctxt.Errorf(gotplt.Sym(), "got.plt is not empty at the very beginning") + } + gotplt.AddAddrPlus(ctxt.Arch, dynamic, 0) + + gotplt.AddUint64(ctxt.Arch, 0) + gotplt.AddUint64(ctxt.Arch, 0) + } +} + +func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { + return + } + + ld.Adddynsym(ldr, target, syms, s) + + if target.IsElf() { + plt := ldr.MakeSymbolUpdater(syms.PLT) + gotplt := ldr.MakeSymbolUpdater(syms.GOTPLT) + rela := ldr.MakeSymbolUpdater(syms.RelaPLT) + if plt.Size() == 0 { + panic("plt is not set up") + } + + // adrp x16, &got.plt[0] + plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size()) + plt.SetUint32(target.Arch, plt.Size()-4, 0x90000010) + relocs := plt.Relocs() + plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_GOT) + + // <offset> is the offset value of &got.plt[n] to &got.plt[0] + // ldr x17, [x16, <offset>] + plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size()) + plt.SetUint32(target.Arch, plt.Size()-4, 0xf9400211) + relocs = plt.Relocs() + plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_GOT) + + // add x16, x16, <offset> + plt.AddAddrPlus4(target.Arch, gotplt.Sym(), gotplt.Size()) + plt.SetUint32(target.Arch, plt.Size()-4, 0x91000210) + relocs = plt.Relocs() + plt.SetRelocType(relocs.Count()-1, objabi.R_ARM64_PCREL) + + // br x17 + plt.AddUint32(target.Arch, 0xd61f0220) + + // add to got.plt: pointer to plt[0] + gotplt.AddAddrPlus(target.Arch, plt.Sym(), 0) + + // rela + rela.AddAddrPlus(target.Arch, gotplt.Sym(), gotplt.Size()-8) + sDynid := ldr.SymDynid(s) + + rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_AARCH64_JUMP_SLOT))) + rela.AddUint64(target.Arch, 0) + + ldr.SetPlt(s, int32(plt.Size()-16)) + } else if target.IsDarwin() { + ld.AddGotSym(target, ldr, syms, s, 0) + + sDynid := ldr.SymDynid(s) + lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT) + lep.AddUint32(target.Arch, uint32(sDynid)) + + plt := ldr.MakeSymbolUpdater(syms.PLT) + ldr.SetPlt(s, int32(plt.Size())) + + // adrp x16, GOT + plt.AddUint32(target.Arch, 0x90000010) + r, _ := plt.AddRel(objabi.R_ARM64_GOT) + r.SetOff(int32(plt.Size() - 4)) + r.SetSiz(4) + r.SetSym(syms.GOT) + r.SetAdd(int64(ldr.SymGot(s))) + + // ldr x17, [x16, <offset>] + plt.AddUint32(target.Arch, 0xf9400211) + r, _ = plt.AddRel(objabi.R_ARM64_GOT) + r.SetOff(int32(plt.Size() - 4)) + r.SetSiz(4) + r.SetSym(syms.GOT) + r.SetAdd(int64(ldr.SymGot(s))) + + // br x17 + plt.AddUint32(target.Arch, 0xd61f0220) + } else { + ldr.Errorf(s, "addpltsym: unsupported binary format") + } +} + +const machoRelocLimit = 1 << 23 + +func gensymlate(ctxt *ld.Link, ldr *loader.Loader) { + // When external linking on darwin, Mach-O relocation has only signed 24-bit + // addend. For large symbols, we generate "label" symbols in the middle, so + // that relocations can target them with smaller addends. + if !ctxt.IsDarwin() || !ctxt.IsExternal() { + return + } + + big := false + for _, seg := range ld.Segments { + if seg.Length >= machoRelocLimit { + big = true + break + } + } + if !big { + return // skip work if nothing big + } + + // addLabelSyms adds "label" symbols at s+machoRelocLimit, s+2*machoRelocLimit, etc. + addLabelSyms := func(s loader.Sym, sz int64) { + v := ldr.SymValue(s) + for off := int64(machoRelocLimit); off < sz; off += machoRelocLimit { + p := ldr.LookupOrCreateSym(machoLabelName(ldr, s, off), ldr.SymVersion(s)) + ldr.SetAttrReachable(p, true) + ldr.SetSymValue(p, v+off) + ldr.SetSymSect(p, ldr.SymSect(s)) + ld.AddMachoSym(ldr, p) + //fmt.Printf("gensymlate %s %x\n", ldr.SymName(p), ldr.SymValue(p)) + } + } + + for s, n := loader.Sym(1), loader.Sym(ldr.NSym()); s < n; s++ { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymType(s) == sym.STEXT { + continue // we don't target the middle of a function + } + sz := ldr.SymSize(s) + if sz <= machoRelocLimit { + continue + } + addLabelSyms(s, sz) + } + + // Also for carrier symbols (for which SymSize is 0) + for _, ss := range ld.CarrierSymByType { + if ss.Sym != 0 && ss.Size > machoRelocLimit { + addLabelSyms(ss.Sym, ss.Size) + } + } +} + +// machoLabelName returns the name of the "label" symbol used for a +// relocation targeting s+off. The label symbols is used on darwin +// when external linking, so that the addend fits in a Mach-O relocation. +func machoLabelName(ldr *loader.Loader, s loader.Sym, off int64) string { + return fmt.Sprintf("%s.%d", ldr.SymExtname(s), off/machoRelocLimit) +} diff --git a/src/cmd/link/internal/arm64/l.go b/src/cmd/link/internal/arm64/l.go new file mode 100644 index 0000000..4aa2708 --- /dev/null +++ b/src/cmd/link/internal/arm64/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 16 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 31 + dwarfRegLR = 30 +) diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go new file mode 100644 index 0000000..bd13295 --- /dev/null +++ b/src/cmd/link/internal/arm64/obj.go @@ -0,0 +1,112 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchARM64 + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Extreloc: extreloc, + Elfreloc1: elfreloc1, + ElfrelocSize: 24, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + GenSymsLate: gensymlate, + Machoreloc1: machoreloc1, + MachorelocSize: 8, + + Androiddynld: "/system/bin/linker64", + Linuxdynld: "/lib/ld-linux-aarch64.so.1", + + Freebsddynld: "/usr/libexec/ld-elf.so.1", + Openbsddynld: "/usr/libexec/ld.so", + Netbsddynld: "/libexec/ld.elf_so", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4096 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hlinux, /* arm64 elf */ + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + + case objabi.Hdarwin: /* apple MACH */ + ld.HEADR = ld.INITIAL_MACHO_HEADR + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 1<<32 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 16384 // 16K page alignment + } + } +} diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go new file mode 100644 index 0000000..6c163c8 --- /dev/null +++ b/src/cmd/link/internal/benchmark/bench.go @@ -0,0 +1,195 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package benchmark provides a Metrics object that enables memory and CPU +// profiling for the linker. The Metrics objects can be used to mark stages +// of the code, and name the measurements during that stage. There is also +// optional GCs that can be performed at the end of each stage, so you +// can get an accurate measurement of how each stage changes live memory. +package benchmark + +import ( + "fmt" + "io" + "os" + "runtime" + "runtime/pprof" + "time" + "unicode" +) + +type Flags int + +const ( + GC = 1 << iota + NoGC Flags = 0 +) + +type Metrics struct { + gc Flags + marks []*mark + curMark *mark + filebase string + pprofFile *os.File +} + +type mark struct { + name string + startM, endM, gcM runtime.MemStats + startT, endT time.Time +} + +// New creates a new Metrics object. +// +// Typical usage should look like: +// +// func main() { +// filename := "" // Set to enable per-phase pprof file output. +// bench := benchmark.New(benchmark.GC, filename) +// defer bench.Report(os.Stdout) +// // etc +// bench.Start("foo") +// foo() +// bench.Start("bar") +// bar() +// } +// +// Note that a nil Metrics object won't cause any errors, so one could write +// code like: +// +// func main() { +// enableBenchmarking := flag.Bool("enable", true, "enables benchmarking") +// flag.Parse() +// var bench *benchmark.Metrics +// if *enableBenchmarking { +// bench = benchmark.New(benchmark.GC) +// } +// bench.Start("foo") +// // etc. +// } +func New(gc Flags, filebase string) *Metrics { + if gc == GC { + runtime.GC() + } + return &Metrics{gc: gc, filebase: filebase} +} + +// Report reports the metrics. +// Closes the currently Start(ed) range, and writes the report to the given io.Writer. +func (m *Metrics) Report(w io.Writer) { + if m == nil { + return + } + + m.closeMark() + + gcString := "" + if m.gc == GC { + gcString = "_GC" + } + + var totTime time.Duration + for _, curMark := range m.marks { + dur := curMark.endT.Sub(curMark.startT) + totTime += dur + fmt.Fprintf(w, "%s 1 %d ns/op", makeBenchString(curMark.name+gcString), dur.Nanoseconds()) + fmt.Fprintf(w, "\t%d B/op", curMark.endM.TotalAlloc-curMark.startM.TotalAlloc) + fmt.Fprintf(w, "\t%d allocs/op", curMark.endM.Mallocs-curMark.startM.Mallocs) + if m.gc == GC { + fmt.Fprintf(w, "\t%d live-B", curMark.gcM.HeapAlloc) + } else { + fmt.Fprintf(w, "\t%d heap-B", curMark.endM.HeapAlloc) + } + fmt.Fprintf(w, "\n") + } + fmt.Fprintf(w, "%s 1 %d ns/op\n", makeBenchString("total time"+gcString), totTime.Nanoseconds()) +} + +// Starts marks the beginning of a new measurement phase. +// Once a metric is started, it continues until either a Report is issued, or another Start is called. +func (m *Metrics) Start(name string) { + if m == nil { + return + } + m.closeMark() + m.curMark = &mark{name: name} + // Unlikely we need to a GC here, as one was likely just done in closeMark. + if m.shouldPProf() { + f, err := os.Create(makePProfFilename(m.filebase, name, "cpuprof")) + if err != nil { + panic(err) + } + m.pprofFile = f + if err = pprof.StartCPUProfile(m.pprofFile); err != nil { + panic(err) + } + } + runtime.ReadMemStats(&m.curMark.startM) + m.curMark.startT = time.Now() +} + +func (m *Metrics) closeMark() { + if m == nil || m.curMark == nil { + return + } + m.curMark.endT = time.Now() + if m.shouldPProf() { + pprof.StopCPUProfile() + m.pprofFile.Close() + m.pprofFile = nil + } + runtime.ReadMemStats(&m.curMark.endM) + if m.gc == GC { + runtime.GC() + runtime.ReadMemStats(&m.curMark.gcM) + if m.shouldPProf() { + // Collect a profile of the live heap. Do a + // second GC to force sweep completion so we + // get a complete snapshot of the live heap at + // the end of this phase. + runtime.GC() + f, err := os.Create(makePProfFilename(m.filebase, m.curMark.name, "memprof")) + if err != nil { + panic(err) + } + err = pprof.WriteHeapProfile(f) + if err != nil { + panic(err) + } + err = f.Close() + if err != nil { + panic(err) + } + } + } + m.marks = append(m.marks, m.curMark) + m.curMark = nil +} + +// shouldPProf returns true if we should be doing pprof runs. +func (m *Metrics) shouldPProf() bool { + return m != nil && len(m.filebase) > 0 +} + +// makeBenchString makes a benchmark string consumable by Go's benchmarking tools. +func makeBenchString(name string) string { + needCap := true + ret := []rune("Benchmark") + for _, r := range name { + if unicode.IsSpace(r) { + needCap = true + continue + } + if needCap { + r = unicode.ToUpper(r) + needCap = false + } + ret = append(ret, r) + } + return string(ret) +} + +func makePProfFilename(filebase, name, typ string) string { + return fmt.Sprintf("%s_%s.%s", filebase, makeBenchString(name), typ) +} diff --git a/src/cmd/link/internal/benchmark/bench_test.go b/src/cmd/link/internal/benchmark/bench_test.go new file mode 100644 index 0000000..419dc55 --- /dev/null +++ b/src/cmd/link/internal/benchmark/bench_test.go @@ -0,0 +1,54 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package benchmark + +import ( + "testing" +) + +func TestMakeBenchString(t *testing.T) { + tests := []struct { + have, want string + }{ + {"foo", "BenchmarkFoo"}, + {" foo ", "BenchmarkFoo"}, + {"foo bar", "BenchmarkFooBar"}, + } + for i, test := range tests { + if v := makeBenchString(test.have); test.want != v { + t.Errorf("test[%d] makeBenchString(%q) == %q, want %q", i, test.have, v, test.want) + } + } +} + +func TestPProfFlag(t *testing.T) { + tests := []struct { + name string + want bool + }{ + {"", false}, + {"foo", true}, + } + for i, test := range tests { + b := New(GC, test.name) + if v := b.shouldPProf(); test.want != v { + t.Errorf("test[%d] shouldPProf() == %v, want %v", i, v, test.want) + } + } +} + +func TestPProfNames(t *testing.T) { + want := "foo_BenchmarkTest.cpuprof" + if v := makePProfFilename("foo", "test", "cpuprof"); v != want { + t.Errorf("makePProfFilename() == %q, want %q", v, want) + } +} + +// Ensure that public APIs work with a nil Metrics object. +func TestNilBenchmarkObject(t *testing.T) { + var b *Metrics + b.Start("TEST") + b.Report(nil) +} diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go new file mode 100644 index 0000000..e4fd591 --- /dev/null +++ b/src/cmd/link/internal/ld/ar.go @@ -0,0 +1,191 @@ +// Inferno utils/include/ar.h +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/include/ar.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/link/internal/sym" + "encoding/binary" + "fmt" + "io" + "os" +) + +const ( + SARMAG = 8 + SAR_HDR = 16 + 44 +) + +const ( + ARMAG = "!<arch>\n" +) + +type ArHdr struct { + name string + date string + uid string + gid string + mode string + size string + fmag string +} + +// hostArchive reads an archive file holding host objects and links in +// required objects. The general format is the same as a Go archive +// file, but it has an armap listing symbols and the objects that +// define them. This is used for the compiler support library +// libgcc.a. +func hostArchive(ctxt *Link, name string) { + f, err := bio.Open(name) + if err != nil { + if os.IsNotExist(err) { + // It's OK if we don't have a libgcc file at all. + if ctxt.Debugvlog != 0 { + ctxt.Logf("skipping libgcc file: %v\n", err) + } + return + } + Exitf("cannot open file %s: %v", name, err) + } + defer f.Close() + + var magbuf [len(ARMAG)]byte + if _, err := io.ReadFull(f, magbuf[:]); err != nil { + Exitf("file %s too short", name) + } + + if string(magbuf[:]) != ARMAG { + Exitf("%s is not an archive file", name) + } + + var arhdr ArHdr + l := nextar(f, f.Offset(), &arhdr) + if l <= 0 { + Exitf("%s missing armap", name) + } + + var armap archiveMap + if arhdr.name == "/" || arhdr.name == "/SYM64/" { + armap = readArmap(name, f, arhdr) + } else { + Exitf("%s missing armap", name) + } + + loaded := make(map[uint64]bool) + any := true + for any { + var load []uint64 + returnAllUndefs := -1 + undefs := ctxt.loader.UndefinedRelocTargets(returnAllUndefs) + for _, symIdx := range undefs { + name := ctxt.loader.SymName(symIdx) + if off := armap[name]; off != 0 && !loaded[off] { + load = append(load, off) + loaded[off] = true + } + } + + for _, off := range load { + l := nextar(f, int64(off), &arhdr) + if l <= 0 { + Exitf("%s missing archive entry at offset %d", name, off) + } + pname := fmt.Sprintf("%s(%s)", name, arhdr.name) + l = atolwhex(arhdr.size) + + libgcc := sym.Library{Pkg: "libgcc"} + h := ldobj(ctxt, f, &libgcc, l, pname, name) + f.MustSeek(h.off, 0) + h.ld(ctxt, f, h.pkg, h.length, h.pn) + } + + any = len(load) > 0 + } +} + +// archiveMap is an archive symbol map: a mapping from symbol name to +// offset within the archive file. +type archiveMap map[string]uint64 + +// readArmap reads the archive symbol map. +func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap { + is64 := arhdr.name == "/SYM64/" + wordSize := 4 + if is64 { + wordSize = 8 + } + + contents := make([]byte, atolwhex(arhdr.size)) + if _, err := io.ReadFull(f, contents); err != nil { + Exitf("short read from %s", filename) + } + + var c uint64 + if is64 { + c = binary.BigEndian.Uint64(contents) + } else { + c = uint64(binary.BigEndian.Uint32(contents)) + } + contents = contents[wordSize:] + + ret := make(archiveMap) + + names := contents[c*uint64(wordSize):] + for i := uint64(0); i < c; i++ { + n := 0 + for names[n] != 0 { + n++ + } + name := string(names[:n]) + names = names[n+1:] + + // For Mach-O and PE/386 files we strip a leading + // underscore from the symbol name. + if objabi.GOOS == "darwin" || objabi.GOOS == "ios" || (objabi.GOOS == "windows" && objabi.GOARCH == "386") { + if name[0] == '_' && len(name) > 1 { + name = name[1:] + } + } + + var off uint64 + if is64 { + off = binary.BigEndian.Uint64(contents) + } else { + off = uint64(binary.BigEndian.Uint32(contents)) + } + contents = contents[wordSize:] + + ret[name] = off + } + + return ret +} diff --git a/src/cmd/link/internal/ld/asmb.go b/src/cmd/link/internal/ld/asmb.go new file mode 100644 index 0000000..fda0439 --- /dev/null +++ b/src/cmd/link/internal/ld/asmb.go @@ -0,0 +1,214 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "runtime" + "sync" +) + +// Assembling the binary is broken into two steps: +// - writing out the code/data/dwarf Segments, applying relocations on the fly +// - writing out the architecture specific pieces. +// This function handles the first part. +func asmb(ctxt *Link) { + // TODO(jfaller): delete me. + if thearch.Asmb != nil { + thearch.Asmb(ctxt, ctxt.loader) + return + } + + if ctxt.IsELF { + Asmbelfsetup() + } + + var wg sync.WaitGroup + sect := Segtext.Sections[0] + offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff + f := func(ctxt *Link, out *OutBuf, start, length int64) { + pad := thearch.CodePad + if pad == nil { + pad = zeros[:] + } + CodeblkPad(ctxt, out, start, length, pad) + } + + if !thearch.WriteTextBlocks { + writeParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length) + for _, sect := range Segtext.Sections[1:] { + offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff + writeParallel(&wg, datblk, ctxt, offset, sect.Vaddr, sect.Length) + } + } else { + // TODO why can't we handle all sections this way? + for _, sect := range Segtext.Sections { + offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff + // Handle additional text sections with Codeblk + if sect.Name == ".text" { + writeParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length) + } else { + writeParallel(&wg, datblk, ctxt, offset, sect.Vaddr, sect.Length) + } + } + } + + if Segrodata.Filelen > 0 { + writeParallel(&wg, datblk, ctxt, Segrodata.Fileoff, Segrodata.Vaddr, Segrodata.Filelen) + } + + if Segrelrodata.Filelen > 0 { + writeParallel(&wg, datblk, ctxt, Segrelrodata.Fileoff, Segrelrodata.Vaddr, Segrelrodata.Filelen) + } + + writeParallel(&wg, datblk, ctxt, Segdata.Fileoff, Segdata.Vaddr, Segdata.Filelen) + + writeParallel(&wg, dwarfblk, ctxt, Segdwarf.Fileoff, Segdwarf.Vaddr, Segdwarf.Filelen) + + wg.Wait() +} + +// Assembling the binary is broken into two steps: +// - writing out the code/data/dwarf Segments +// - writing out the architecture specific pieces. +// This function handles the second part. +func asmb2(ctxt *Link) { + if thearch.Asmb2 != nil { + thearch.Asmb2(ctxt, ctxt.loader) + return + } + + symSize = 0 + spSize = 0 + lcSize = 0 + + switch ctxt.HeadType { + default: + panic("unknown platform") + + // Macho + case objabi.Hdarwin: + asmbMacho(ctxt) + + // Plan9 + case objabi.Hplan9: + asmbPlan9(ctxt) + + // PE + case objabi.Hwindows: + asmbPe(ctxt) + + // Xcoff + case objabi.Haix: + asmbXcoff(ctxt) + + // Elf + case objabi.Hdragonfly, + objabi.Hfreebsd, + objabi.Hlinux, + objabi.Hnetbsd, + objabi.Hopenbsd, + objabi.Hsolaris: + asmbElf(ctxt) + } + + if *FlagC { + fmt.Printf("textsize=%d\n", Segtext.Filelen) + fmt.Printf("datsize=%d\n", Segdata.Filelen) + fmt.Printf("bsssize=%d\n", Segdata.Length-Segdata.Filelen) + fmt.Printf("symsize=%d\n", symSize) + fmt.Printf("lcsize=%d\n", lcSize) + fmt.Printf("total=%d\n", Segtext.Filelen+Segdata.Length+uint64(symSize)+uint64(lcSize)) + } +} + +// writePlan9Header writes out the plan9 header at the present position in the OutBuf. +func writePlan9Header(buf *OutBuf, magic uint32, entry int64, is64Bit bool) { + if is64Bit { + magic |= 0x00008000 + } + buf.Write32b(magic) + buf.Write32b(uint32(Segtext.Filelen)) + buf.Write32b(uint32(Segdata.Filelen)) + buf.Write32b(uint32(Segdata.Length - Segdata.Filelen)) + buf.Write32b(uint32(symSize)) + if is64Bit { + buf.Write32b(uint32(entry &^ 0x80000000)) + } else { + buf.Write32b(uint32(entry)) + } + buf.Write32b(uint32(spSize)) + buf.Write32b(uint32(lcSize)) + // amd64 includes the entry at the beginning of the symbol table. + if is64Bit { + buf.Write64b(uint64(entry)) + } +} + +// asmbPlan9 assembles a plan 9 binary. +func asmbPlan9(ctxt *Link) { + if !*FlagS { + *FlagS = true + symo := int64(Segdata.Fileoff + Segdata.Filelen) + ctxt.Out.SeekSet(symo) + asmbPlan9Sym(ctxt) + } + ctxt.Out.SeekSet(0) + writePlan9Header(ctxt.Out, thearch.Plan9Magic, Entryvalue(ctxt), thearch.Plan9_64Bit) +} + +// sizeExtRelocs precomputes the size needed for the reloc records, +// sets the size and offset for relocation records in each section, +// and mmap the output buffer with the proper size. +func sizeExtRelocs(ctxt *Link, relsize uint32) { + if relsize == 0 { + panic("sizeExtRelocs: relocation size not set") + } + var sz int64 + for _, seg := range Segments { + for _, sect := range seg.Sections { + sect.Reloff = uint64(ctxt.Out.Offset() + sz) + sect.Rellen = uint64(relsize * sect.Relcount) + sz += int64(sect.Rellen) + } + } + filesz := ctxt.Out.Offset() + sz + ctxt.Out.Mmap(uint64(filesz)) +} + +// relocSectFn wraps the function writing relocations of a section +// for parallel execution. Returns the wrapped function and a wait +// group for which the caller should wait. +func relocSectFn(ctxt *Link, relocSect func(*Link, *OutBuf, *sym.Section, []loader.Sym)) (func(*Link, *sym.Section, []loader.Sym), *sync.WaitGroup) { + var fn func(ctxt *Link, sect *sym.Section, syms []loader.Sym) + var wg sync.WaitGroup + var sem chan int + if ctxt.Out.isMmapped() { + // Write sections in parallel. + sem = make(chan int, 2*runtime.GOMAXPROCS(0)) + fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) { + wg.Add(1) + sem <- 1 + out, err := ctxt.Out.View(sect.Reloff) + if err != nil { + panic(err) + } + go func() { + relocSect(ctxt, out, sect, syms) + wg.Done() + <-sem + }() + } + } else { + // We cannot Mmap. Write sequentially. + fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) { + relocSect(ctxt, ctxt.Out, sect, syms) + } + } + return fn, &wg +} diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go new file mode 100644 index 0000000..d1e0623 --- /dev/null +++ b/src/cmd/link/internal/ld/config.go @@ -0,0 +1,282 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "fmt" + "log" +) + +// A BuildMode indicates the sort of object we are building. +// +// Possible build modes are the same as those for the -buildmode flag +// in cmd/go, and are documented in 'go help buildmode'. +type BuildMode uint8 + +const ( + BuildModeUnset BuildMode = iota + BuildModeExe + BuildModePIE + BuildModeCArchive + BuildModeCShared + BuildModeShared + BuildModePlugin +) + +func (mode *BuildMode) Set(s string) error { + badmode := func() error { + return fmt.Errorf("buildmode %s not supported on %s/%s", s, objabi.GOOS, objabi.GOARCH) + } + switch s { + default: + return fmt.Errorf("invalid buildmode: %q", s) + case "exe": + 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", "darwin", "ios": + case "freebsd": + switch objabi.GOARCH { + case "amd64": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModePIE + case "c-archive": + switch objabi.GOOS { + case "aix", "darwin", "ios", "linux": + case "freebsd": + switch objabi.GOARCH { + case "amd64": + default: + return badmode() + } + case "windows": + switch objabi.GOARCH { + case "amd64", "386", "arm": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModeCArchive + case "c-shared": + switch objabi.GOARCH { + case "386", "amd64", "arm", "arm64", "ppc64le", "s390x": + default: + return badmode() + } + *mode = BuildModeCShared + case "shared": + switch objabi.GOOS { + case "linux": + switch objabi.GOARCH { + case "386", "amd64", "arm", "arm64", "ppc64le", "s390x": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModeShared + case "plugin": + switch objabi.GOOS { + case "linux": + switch objabi.GOARCH { + case "386", "amd64", "arm", "arm64", "s390x", "ppc64le": + default: + return badmode() + } + case "darwin": + switch objabi.GOARCH { + case "amd64", "arm64": + default: + return badmode() + } + case "freebsd": + switch objabi.GOARCH { + case "amd64": + default: + return badmode() + } + default: + return badmode() + } + *mode = BuildModePlugin + } + return nil +} + +func (mode *BuildMode) String() string { + switch *mode { + case BuildModeUnset: + return "" // avoid showing a default in usage message + case BuildModeExe: + return "exe" + case BuildModePIE: + return "pie" + case BuildModeCArchive: + return "c-archive" + case BuildModeCShared: + return "c-shared" + case BuildModeShared: + return "shared" + case BuildModePlugin: + return "plugin" + } + return fmt.Sprintf("BuildMode(%d)", uint8(*mode)) +} + +// LinkMode indicates whether an external linker is used for the final link. +type LinkMode uint8 + +const ( + LinkAuto LinkMode = iota + LinkInternal + LinkExternal +) + +func (mode *LinkMode) Set(s string) error { + switch s { + default: + return fmt.Errorf("invalid linkmode: %q", s) + case "auto": + *mode = LinkAuto + case "internal": + *mode = LinkInternal + case "external": + *mode = LinkExternal + } + return nil +} + +func (mode *LinkMode) String() string { + switch *mode { + case LinkAuto: + return "auto" + case LinkInternal: + return "internal" + case LinkExternal: + return "external" + } + return fmt.Sprintf("LinkMode(%d)", uint8(*mode)) +} + +// mustLinkExternal reports whether the program being linked requires +// the external linker be used to complete the link. +func mustLinkExternal(ctxt *Link) (res bool, reason string) { + if ctxt.Debugvlog > 1 { + defer func() { + if res { + log.Printf("external linking is forced by: %s\n", reason) + } + }() + } + + if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) { + return true, fmt.Sprintf("%s/%s requires external linking", objabi.GOOS, objabi.GOARCH) + } + + if *flagMsan { + return true, "msan" + } + + // 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, sys.RISCV64) { + return true, objabi.GOARCH + " does not support internal cgo" + } + if iscgo && objabi.GOOS == "android" { + return true, objabi.GOOS + " does not support internal cgo" + } + + // When the race flag is set, the LLVM tsan relocatable file is linked + // into the final binary, which means external linking is required because + // internal linking does not support it. + if *flagRace && ctxt.Arch.InFamily(sys.PPC64) { + return true, "race on " + objabi.GOARCH + } + + // Some build modes require work the internal linker cannot do (yet). + switch ctxt.BuildMode { + case BuildModeCArchive: + return true, "buildmode=c-archive" + case BuildModeCShared: + return true, "buildmode=c-shared" + case BuildModePIE: + switch 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" + } + case BuildModePlugin: + return true, "buildmode=plugin" + case BuildModeShared: + return true, "buildmode=shared" + } + if ctxt.linkShared { + return true, "dynamically linking with a shared library" + } + + return false, "" +} + +// determineLinkMode sets ctxt.LinkMode. +// +// It is called after flags are processed and inputs are processed, +// so the ctxt.LinkMode variable has an initial value from the -linkmode +// flag and the iscgo externalobj variables are set. +func determineLinkMode(ctxt *Link) { + extNeeded, extReason := mustLinkExternal(ctxt) + via := "" + + if ctxt.LinkMode == LinkAuto { + // The environment variable GO_EXTLINK_ENABLED controls the + // default value of -linkmode. If it is not set when the + // linker is called we take the value it was set to when + // cmd/link was compiled. (See make.bash.) + switch objabi.Getgoextlinkenabled() { + case "0": + ctxt.LinkMode = LinkInternal + via = "via GO_EXTLINK_ENABLED " + case "1": + ctxt.LinkMode = LinkExternal + via = "via GO_EXTLINK_ENABLED " + default: + if extNeeded || (iscgo && externalobj) { + ctxt.LinkMode = LinkExternal + } else { + ctxt.LinkMode = LinkInternal + } + } + } + + switch ctxt.LinkMode { + case LinkInternal: + if extNeeded { + Exitf("internal linking requested %sbut external linking required: %s", via, extReason) + } + case LinkExternal: + switch { + case 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 new file mode 100644 index 0000000..7028b85 --- /dev/null +++ b/src/cmd/link/internal/ld/data.go @@ -0,0 +1,2685 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bytes" + "cmd/internal/gcprog" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "compress/zlib" + "encoding/binary" + "fmt" + "log" + "os" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" +) + +// isRuntimeDepPkg reports whether pkg is the runtime package or its dependency +func isRuntimeDepPkg(pkg string) bool { + switch pkg { + case "runtime", + "sync/atomic", // runtime may call to sync/atomic, due to go:linkname + "internal/bytealg", // for IndexByte + "internal/cpu": // for cpu features + return true + } + return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test") +} + +// Estimate the max size needed to hold any new trampolines created for this function. This +// is used to determine when the section can be split if it becomes too large, to ensure that +// the trampolines are in the same section as the function that uses them. +func maxSizeTrampolinesPPC64(ldr *loader.Loader, s loader.Sym, isTramp bool) uint64 { + // If thearch.Trampoline is nil, then trampoline support is not available on this arch. + // A trampoline does not need any dependent trampolines. + if thearch.Trampoline == nil || isTramp { + return 0 + } + + n := uint64(0) + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.Type().IsDirectCallOrJump() { + n++ + } + } + // Trampolines in ppc64 are 4 instructions. + return n * 16 +} + +// detect too-far jumps in function s, and add trampolines if necessary +// ARM, PPC64 & PPC64LE support trampoline insertion for internal and external linking +// On PPC64 & PPC64LE the text sections might be split but will still insert trampolines +// where necessary. +func trampoline(ctxt *Link, s loader.Sym) { + if thearch.Trampoline == nil { + return // no need or no support of trampolines on this arch + } + + ldr := ctxt.loader + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if !r.Type().IsDirectCallOrJump() { + continue + } + rs := r.Sym() + if !ldr.AttrReachable(rs) || ldr.SymType(rs) == sym.Sxxx { + continue // something is wrong. skip it here and we'll emit a better error later + } + rs = ldr.ResolveABIAlias(rs) + if ldr.SymValue(rs) == 0 && (ldr.SymType(rs) != sym.SDYNIMPORT && ldr.SymType(rs) != sym.SUNDEFEXT) { + if ldr.SymPkg(rs) == ldr.SymPkg(s) { + continue // symbols in the same package are laid out together + } + if isRuntimeDepPkg(ldr.SymPkg(s)) && isRuntimeDepPkg(ldr.SymPkg(rs)) { + continue // runtime packages are laid out together + } + } + + thearch.Trampoline(ctxt, ldr, ri, rs, s) + } + +} + +// FoldSubSymbolOffset computes the offset of symbol s to its top-level outer +// symbol. Returns the top-level symbol and the offset. +// This is used in generating external relocations. +func FoldSubSymbolOffset(ldr *loader.Loader, s loader.Sym) (loader.Sym, int64) { + outer := ldr.OuterSym(s) + off := int64(0) + if outer != 0 { + off += ldr.SymValue(s) - ldr.SymValue(outer) + s = outer + } + return s, off +} + +// relocsym resolve relocations in "s", updating the symbol's content +// in "P". +// The main loop walks through the list of relocations attached to "s" +// and resolves them where applicable. Relocations are often +// architecture-specific, requiring calls into the 'archreloc' and/or +// 'archrelocvariant' functions for the architecture. When external +// linking is in effect, it may not be possible to completely resolve +// the address/offset for a symbol, in which case the goal is to lay +// the groundwork for turning a given relocation into an external reloc +// (to be applied by the external linker). For more on how relocations +// work in general, see +// +// "Linkers and Loaders", by John R. Levine (Morgan Kaufmann, 1999), ch. 7 +// +// This is a performance-critical function for the linker; be careful +// to avoid introducing unnecessary allocations in the main loop. +func (st *relocSymState) relocsym(s loader.Sym, P []byte) { + ldr := st.ldr + relocs := ldr.Relocs(s) + if relocs.Count() == 0 { + return + } + target := st.target + syms := st.syms + nExtReloc := 0 // number of external relocations + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + off := r.Off() + siz := int32(r.Siz()) + rs := r.Sym() + rs = ldr.ResolveABIAlias(rs) + rt := r.Type() + if off < 0 || off+siz > int32(len(P)) { + rname := "" + if rs != 0 { + rname = ldr.SymName(rs) + } + st.err.Errorf(s, "invalid relocation %s: %d+%d not in [%d,%d)", rname, off, siz, 0, len(P)) + continue + } + if siz == 0 { // informational relocation - no work to do + continue + } + + var rst sym.SymKind + if rs != 0 { + rst = ldr.SymType(rs) + } + + if rs != 0 && ((rst == sym.Sxxx && !ldr.AttrVisibilityHidden(rs)) || rst == sym.SXREF) { + // When putting the runtime but not main into a shared library + // these symbols are undefined and that's OK. + if target.IsShared() || target.IsPlugin() { + if ldr.SymName(rs) == "main.main" || (!target.IsPlugin() && ldr.SymName(rs) == "main..inittask") { + sb := ldr.MakeSymbolUpdater(rs) + sb.SetType(sym.SDYNIMPORT) + } else if strings.HasPrefix(ldr.SymName(rs), "go.info.") { + // Skip go.info symbols. They are only needed to communicate + // DWARF info between the compiler and linker. + continue + } + } else { + st.err.errorUnresolved(ldr, s, rs) + continue + } + } + + if rt >= objabi.ElfRelocOffset { + continue + } + + // We need to be able to reference dynimport symbols when linking against + // shared libraries, and AIX, Darwin, OpenBSD and Solaris always need it. + if !target.IsAIX() && !target.IsDarwin() && !target.IsSolaris() && !target.IsOpenbsd() && rs != 0 && rst == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !ldr.AttrSubSymbol(rs) { + if !(target.IsPPC64() && target.IsExternal() && ldr.SymName(rs) == ".TOC.") { + st.err.Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", ldr.SymName(rs), rst, rst, rt, sym.RelocName(target.Arch, rt)) + } + } + if rs != 0 && rst != sym.STLSBSS && rt != objabi.R_WEAKADDROFF && rt != objabi.R_METHODOFF && !ldr.AttrReachable(rs) { + st.err.Errorf(s, "unreachable sym in relocation: %s", ldr.SymName(rs)) + } + + var rv sym.RelocVariant + if target.IsPPC64() || target.IsS390X() { + rv = ldr.RelocVariant(s, ri) + } + + // TODO(mundaym): remove this special case - see issue 14218. + if target.IsS390X() { + switch rt { + case objabi.R_PCRELDBL: + rt = objabi.R_PCREL + rv = sym.RV_390_DBL + case objabi.R_CALL: + rv = sym.RV_390_DBL + } + } + + var o int64 + switch rt { + default: + switch siz { + default: + st.err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs)) + case 1: + o = int64(P[off]) + case 2: + o = int64(target.Arch.ByteOrder.Uint16(P[off:])) + case 4: + o = int64(target.Arch.ByteOrder.Uint32(P[off:])) + case 8: + o = int64(target.Arch.ByteOrder.Uint64(P[off:])) + } + out, n, ok := thearch.Archreloc(target, ldr, syms, r, s, o) + if target.IsExternal() { + nExtReloc += n + } + if ok { + o = out + } else { + st.err.Errorf(s, "unknown reloc to %v: %d (%s)", ldr.SymName(rs), rt, sym.RelocName(target.Arch, rt)) + } + case objabi.R_TLS_LE: + if target.IsExternal() && target.IsElf() { + nExtReloc++ + o = 0 + if !target.IsAMD64() { + o = r.Add() + } + break + } + + if target.IsElf() && target.IsARM() { + // On ELF ARM, the thread pointer is 8 bytes before + // the start of the thread-local data block, so add 8 + // to the actual TLS offset (r->sym->value). + // This 8 seems to be a fundamental constant of + // ELF on ARM (or maybe Glibc on ARM); it is not + // related to the fact that our own TLS storage happens + // to take up 8 bytes. + o = 8 + ldr.SymValue(rs) + } else if target.IsElf() || target.IsPlan9() || target.IsDarwin() { + o = int64(syms.Tlsoffset) + r.Add() + } else if target.IsWindows() { + o = r.Add() + } else { + log.Fatalf("unexpected R_TLS_LE relocation for %v", target.HeadType) + } + case objabi.R_TLS_IE: + if target.IsExternal() && target.IsElf() { + nExtReloc++ + o = 0 + if !target.IsAMD64() { + o = r.Add() + } + if target.Is386() { + nExtReloc++ // need two ELF relocations on 386, see ../x86/asm.go:elfreloc1 + } + break + } + if target.IsPIE() && target.IsElf() { + // We are linking the final executable, so we + // can optimize any TLS IE relocation to LE. + if thearch.TLSIEtoLE == nil { + log.Fatalf("internal linking of TLS IE not supported on %v", target.Arch.Family) + } + thearch.TLSIEtoLE(P, int(off), int(siz)) + o = int64(syms.Tlsoffset) + } else { + log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", ldr.SymName(s)) + } + case objabi.R_ADDR: + if target.IsExternal() { + nExtReloc++ + + // set up addend for eventual relocation via outer symbol. + rs := rs + rs, off := FoldSubSymbolOffset(ldr, rs) + xadd := r.Add() + off + rst := ldr.SymType(rs) + if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil { + st.err.Errorf(s, "missing section for relocation target %s", ldr.SymName(rs)) + } + + o = xadd + if target.IsElf() { + if target.IsAMD64() { + o = 0 + } + } else if target.IsDarwin() { + if ldr.SymType(rs) != sym.SHOSTOBJ { + o += ldr.SymValue(rs) + } + } else if target.IsWindows() { + // nothing to do + } else if target.IsAIX() { + o = ldr.SymValue(rs) + xadd + } else { + st.err.Errorf(s, "unhandled pcrel relocation to %s on %v", ldr.SymName(rs), target.HeadType) + } + + break + } + + // On AIX, a second relocation must be done by the loader, + // as section addresses can change once loaded. + // The "default" symbol address is still needed by the loader so + // the current relocation can't be skipped. + if target.IsAIX() && rst != sym.SDYNIMPORT { + // It's not possible to make a loader relocation in a + // symbol which is not inside .data section. + // FIXME: It should be forbidden to have R_ADDR from a + // symbol which isn't in .data. However, as .text has the + // same address once loaded, this is possible. + if ldr.SymSect(s).Seg == &Segdata { + Xcoffadddynrel(target, ldr, syms, s, r, ri) + } + } + + o = ldr.SymValue(rs) + r.Add() + + // On amd64, 4-byte offsets will be sign-extended, so it is impossible to + // access more than 2GB of static data; fail at link time is better than + // fail at runtime. See https://golang.org/issue/7980. + // Instead of special casing only amd64, we treat this as an error on all + // 64-bit architectures so as to be future-proof. + if int32(o) < 0 && target.Arch.PtrSize > 4 && siz == 4 { + st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", ldr.SymName(rs), uint64(o), ldr.SymValue(rs), r.Add()) + errorexit() + } + case objabi.R_DWARFSECREF: + if ldr.SymSect(rs) == nil { + st.err.Errorf(s, "missing DWARF section for relocation target %s", ldr.SymName(rs)) + } + + if target.IsExternal() { + // On most platforms, the external linker needs to adjust DWARF references + // as it combines DWARF sections. However, on Darwin, dsymutil does the + // DWARF linking, and it understands how to follow section offsets. + // Leaving in the relocation records confuses it (see + // https://golang.org/issue/22068) so drop them for Darwin. + if !target.IsDarwin() { + nExtReloc++ + } + + xadd := r.Add() + ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + + o = xadd + if target.IsElf() && target.IsAMD64() { + o = 0 + } + break + } + o = ldr.SymValue(rs) + r.Add() - int64(ldr.SymSect(rs).Vaddr) + case objabi.R_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 + case objabi.R_ADDROFF: + // The method offset tables using this relocation expect the offset to be relative + // to the start of the first text section, even if there are multiple. + if ldr.SymSect(rs).Name == ".text" { + o = ldr.SymValue(rs) - int64(Segtext.Sections[0].Vaddr) + r.Add() + } else { + o = ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + r.Add() + } + + case objabi.R_ADDRCUOFF: + // debug_range and debug_loc elements use this relocation type to get an + // offset from the start of the compile unit. + o = ldr.SymValue(rs) + r.Add() - ldr.SymValue(loader.Sym(ldr.SymUnit(rs).Textp[0])) + + // r.Sym() can be 0 when CALL $(constant) is transformed from absolute PC to relative PC call. + case objabi.R_GOTPCREL: + if target.IsDynlinkingGo() && target.IsDarwin() && rs != 0 { + nExtReloc++ + o = r.Add() + break + } + if target.Is386() && target.IsExternal() && target.IsELF { + nExtReloc++ // need two ELF relocations on 386, see ../x86/asm.go:elfreloc1 + } + fallthrough + case objabi.R_CALL, objabi.R_PCREL: + if target.IsExternal() && rs != 0 && rst == sym.SUNDEFEXT { + // pass through to the external linker. + nExtReloc++ + o = 0 + break + } + if target.IsExternal() && rs != 0 && (ldr.SymSect(rs) != ldr.SymSect(s) || rt == objabi.R_GOTPCREL) { + nExtReloc++ + + // set up addend for eventual relocation via outer symbol. + rs := rs + rs, off := FoldSubSymbolOffset(ldr, rs) + xadd := r.Add() + off - int64(siz) // relative to address after the relocated chunk + rst := ldr.SymType(rs) + if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil { + st.err.Errorf(s, "missing section for relocation target %s", ldr.SymName(rs)) + } + + o = xadd + if target.IsElf() { + if target.IsAMD64() { + o = 0 + } + } else if target.IsDarwin() { + if rt == objabi.R_CALL { + if target.IsExternal() && rst == sym.SDYNIMPORT { + if target.IsAMD64() { + // AMD64 dynamic relocations are relative to the end of the relocation. + o += int64(siz) + } + } else { + if rst != sym.SHOSTOBJ { + o += int64(uint64(ldr.SymValue(rs)) - ldr.SymSect(rs).Vaddr) + } + o -= int64(off) // relative to section offset, not symbol + } + } else { + o += int64(siz) + } + } else if target.IsWindows() && target.IsAMD64() { // only amd64 needs PCREL + // PE/COFF's PC32 relocation uses the address after the relocated + // bytes as the base. Compensate by skewing the addend. + o += int64(siz) + } else { + st.err.Errorf(s, "unhandled pcrel relocation to %s on %v", ldr.SymName(rs), target.HeadType) + } + + break + } + + o = 0 + if rs != 0 { + o = ldr.SymValue(rs) + } + + o += r.Add() - (ldr.SymValue(s) + int64(off) + int64(siz)) + case objabi.R_SIZE: + o = ldr.SymSize(rs) + r.Add() + + case objabi.R_XCOFFREF: + if !target.IsAIX() { + st.err.Errorf(s, "find XCOFF R_REF on non-XCOFF files") + } + if !target.IsExternal() { + st.err.Errorf(s, "find XCOFF R_REF with internal linking") + } + nExtReloc++ + continue + + case objabi.R_DWARFFILEREF: + // We don't renumber files in dwarf.go:writelines anymore. + continue + + case objabi.R_CONST: + o = r.Add() + + case objabi.R_GOTOFF: + o = ldr.SymValue(rs) + r.Add() - ldr.SymValue(syms.GOT) + } + + if target.IsPPC64() || target.IsS390X() { + if rv != sym.RV_NONE { + o = thearch.Archrelocvariant(target, ldr, r, rv, s, o) + } + } + + switch siz { + default: + st.err.Errorf(s, "bad reloc size %#x for %s", uint32(siz), ldr.SymName(rs)) + case 1: + P[off] = byte(int8(o)) + case 2: + if o != int64(int16(o)) { + st.err.Errorf(s, "relocation address for %s is too big: %#x", ldr.SymName(rs), o) + } + target.Arch.ByteOrder.PutUint16(P[off:], uint16(o)) + case 4: + if rt == objabi.R_PCREL || rt == objabi.R_CALL { + if o != int64(int32(o)) { + st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o) + } + } else { + if o != int64(int32(o)) && o != int64(uint32(o)) { + st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o)) + } + } + target.Arch.ByteOrder.PutUint32(P[off:], uint32(o)) + case 8: + target.Arch.ByteOrder.PutUint64(P[off:], uint64(o)) + } + } + if target.IsExternal() { + // We'll stream out the external relocations in asmb2 (e.g. elfrelocsect) + // and we only need the count here. + atomic.AddUint32(&ldr.SymSect(s).Relcount, uint32(nExtReloc)) + } +} + +// Convert a Go relocation to an external relocation. +func extreloc(ctxt *Link, ldr *loader.Loader, s loader.Sym, r loader.Reloc) (loader.ExtReloc, bool) { + var rr loader.ExtReloc + target := &ctxt.Target + siz := int32(r.Siz()) + if siz == 0 { // informational relocation - no work to do + return rr, false + } + + rt := r.Type() + if rt >= objabi.ElfRelocOffset { + return rr, false + } + rr.Type = rt + rr.Size = uint8(siz) + + // TODO(mundaym): remove this special case - see issue 14218. + if target.IsS390X() { + switch rt { + case objabi.R_PCRELDBL: + rt = objabi.R_PCREL + } + } + + switch rt { + default: + return thearch.Extreloc(target, ldr, r, s) + + case objabi.R_TLS_LE, objabi.R_TLS_IE: + if target.IsElf() { + rs := ldr.ResolveABIAlias(r.Sym()) + rr.Xsym = rs + if rr.Xsym == 0 { + rr.Xsym = ctxt.Tlsg + } + rr.Xadd = r.Add() + break + } + return rr, false + + case objabi.R_ADDR: + // set up addend for eventual relocation via outer symbol. + rs := ldr.ResolveABIAlias(r.Sym()) + rs, off := FoldSubSymbolOffset(ldr, rs) + rr.Xadd = r.Add() + off + rr.Xsym = rs + + case objabi.R_DWARFSECREF: + // On most platforms, the external linker needs to adjust DWARF references + // as it combines DWARF sections. However, on Darwin, dsymutil does the + // DWARF linking, and it understands how to follow section offsets. + // Leaving in the relocation records confuses it (see + // https://golang.org/issue/22068) so drop them for Darwin. + if target.IsDarwin() { + return rr, false + } + rs := ldr.ResolveABIAlias(r.Sym()) + rr.Xsym = loader.Sym(ldr.SymSect(rs).Sym) + rr.Xadd = r.Add() + ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + + // r.Sym() can be 0 when CALL $(constant) is transformed from absolute PC to relative PC call. + case objabi.R_GOTPCREL, objabi.R_CALL, objabi.R_PCREL: + rs := ldr.ResolveABIAlias(r.Sym()) + if rt == objabi.R_GOTPCREL && target.IsDynlinkingGo() && target.IsDarwin() && rs != 0 { + rr.Xadd = r.Add() + rr.Xadd -= int64(siz) // relative to address after the relocated chunk + rr.Xsym = rs + break + } + if rs != 0 && ldr.SymType(rs) == sym.SUNDEFEXT { + // pass through to the external linker. + rr.Xadd = 0 + if target.IsElf() { + rr.Xadd -= int64(siz) + } + rr.Xsym = rs + break + } + if rs != 0 && (ldr.SymSect(rs) != ldr.SymSect(s) || rt == objabi.R_GOTPCREL) { + // set up addend for eventual relocation via outer symbol. + rs := rs + rs, off := FoldSubSymbolOffset(ldr, rs) + rr.Xadd = r.Add() + off + rr.Xadd -= int64(siz) // relative to address after the relocated chunk + rr.Xsym = rs + break + } + return rr, false + + case objabi.R_XCOFFREF: + return ExtrelocSimple(ldr, r), true + + // These reloc types don't need external relocations. + case objabi.R_ADDROFF, objabi.R_WEAKADDROFF, objabi.R_METHODOFF, objabi.R_ADDRCUOFF, + objabi.R_SIZE, objabi.R_CONST, objabi.R_GOTOFF: + return rr, false + } + return rr, true +} + +// ExtrelocSimple creates a simple external relocation from r, with the same +// symbol and addend. +func ExtrelocSimple(ldr *loader.Loader, r loader.Reloc) loader.ExtReloc { + var rr loader.ExtReloc + rs := ldr.ResolveABIAlias(r.Sym()) + rr.Xsym = rs + rr.Xadd = r.Add() + rr.Type = r.Type() + rr.Size = r.Siz() + return rr +} + +// ExtrelocViaOuterSym creates an external relocation from r targeting the +// outer symbol and folding the subsymbol's offset into the addend. +func ExtrelocViaOuterSym(ldr *loader.Loader, r loader.Reloc, s loader.Sym) loader.ExtReloc { + // set up addend for eventual relocation via outer symbol. + var rr loader.ExtReloc + rs := ldr.ResolveABIAlias(r.Sym()) + rs, off := FoldSubSymbolOffset(ldr, rs) + rr.Xadd = r.Add() + off + rst := ldr.SymType(rs) + if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && rst != sym.SUNDEFEXT && ldr.SymSect(rs) == nil { + ldr.Errorf(s, "missing section for %s", ldr.SymName(rs)) + } + rr.Xsym = rs + rr.Type = r.Type() + rr.Size = r.Siz() + return rr +} + +// relocSymState hold state information needed when making a series of +// successive calls to relocsym(). The items here are invariant +// (meaning that they are set up once initially and then don't change +// during the execution of relocsym), with the exception of a slice +// used to facilitate batch allocation of external relocations. Calls +// to relocsym happen in parallel; the assumption is that each +// parallel thread will have its own state object. +type relocSymState struct { + target *Target + ldr *loader.Loader + err *ErrorReporter + syms *ArchSyms +} + +// makeRelocSymState creates a relocSymState container object to +// pass to relocsym(). If relocsym() calls happen in parallel, +// each parallel thread should have its own state object. +func (ctxt *Link) makeRelocSymState() *relocSymState { + return &relocSymState{ + target: &ctxt.Target, + ldr: ctxt.loader, + err: &ctxt.ErrorReporter, + syms: &ctxt.ArchSyms, + } +} + +func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) { + var su *loader.SymbolBuilder + relocs := ctxt.loader.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.IsMarker() { + continue // skip marker relocations + } + targ := r.Sym() + if targ == 0 { + continue + } + rt := r.Type() + if !ctxt.loader.AttrReachable(targ) { + if rt == objabi.R_WEAKADDROFF { + continue + } + ctxt.Errorf(s, "dynamic relocation to unreachable symbol %s", + ctxt.loader.SymName(targ)) + } + + tplt := ctxt.loader.SymPlt(targ) + tgot := ctxt.loader.SymGot(targ) + if tplt == -2 && tgot != -2 { // make dynimport JMP table for PE object files. + tplt := int32(rel.Size()) + ctxt.loader.SetPlt(targ, tplt) + + if su == nil { + su = ctxt.loader.MakeSymbolUpdater(s) + } + r.SetSym(rel.Sym()) + r.SetAdd(int64(tplt)) + + // jmp *addr + switch ctxt.Arch.Family { + default: + ctxt.Errorf(s, "unsupported arch %v", ctxt.Arch.Family) + return + case sys.I386: + rel.AddUint8(0xff) + rel.AddUint8(0x25) + rel.AddAddrPlus(ctxt.Arch, targ, 0) + rel.AddUint8(0x90) + rel.AddUint8(0x90) + case sys.AMD64: + rel.AddUint8(0xff) + rel.AddUint8(0x24) + rel.AddUint8(0x25) + rel.AddAddrPlus4(ctxt.Arch, targ, 0) + rel.AddUint8(0x90) + } + } else if tplt >= 0 { + if su == nil { + su = ctxt.loader.MakeSymbolUpdater(s) + } + r.SetSym(rel.Sym()) + r.SetAdd(int64(tplt)) + } + } +} + +// windynrelocsyms generates jump table to C library functions that will be +// added later. windynrelocsyms writes the table into .rel symbol. +func (ctxt *Link) windynrelocsyms() { + if !(ctxt.IsWindows() && iscgo && ctxt.IsInternal()) { + return + } + + rel := ctxt.loader.CreateSymForUpdate(".rel", 0) + rel.SetType(sym.STEXT) + + for _, s := range ctxt.Textp { + windynrelocsym(ctxt, rel, s) + } + + ctxt.Textp = append(ctxt.Textp, rel.Sym()) +} + +func dynrelocsym(ctxt *Link, s loader.Sym) { + target := &ctxt.Target + ldr := ctxt.loader + syms := &ctxt.ArchSyms + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.IsMarker() { + continue // skip marker relocations + } + if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal { + // It's expected that some relocations will be done + // later by relocsym (R_TLS_LE, R_ADDROFF), so + // don't worry if Adddynrel returns false. + thearch.Adddynrel(target, ldr, syms, s, r, ri) + continue + } + + rSym := r.Sym() + if rSym != 0 && ldr.SymType(rSym) == sym.SDYNIMPORT || r.Type() >= objabi.ElfRelocOffset { + if rSym != 0 && !ldr.AttrReachable(rSym) { + ctxt.Errorf(s, "dynamic relocation to unreachable symbol %s", ldr.SymName(rSym)) + } + if !thearch.Adddynrel(target, ldr, syms, s, r, ri) { + ctxt.Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d (%s) stype=%d (%s))", ldr.SymName(rSym), r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymType(rSym), ldr.SymType(rSym)) + } + } + } +} + +func (state *dodataState) dynreloc(ctxt *Link) { + if ctxt.HeadType == objabi.Hwindows { + return + } + // -d suppresses dynamic loader format, so we may as well not + // compute these sections or mark their symbols as reachable. + if *FlagD { + return + } + + for _, s := range ctxt.Textp { + dynrelocsym(ctxt, s) + } + for _, syms := range state.data { + for _, s := range syms { + dynrelocsym(ctxt, s) + } + } + if ctxt.IsELF { + elfdynhash(ctxt) + } +} + +func CodeblkPad(ctxt *Link, out *OutBuf, addr int64, size int64, pad []byte) { + writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, ctxt.Textp, addr, size, pad) +} + +const blockSize = 1 << 20 // 1MB chunks written at a time. + +// writeBlocks writes a specified chunk of symbols to the output buffer. It +// breaks the write up into ≥blockSize chunks to write them out, and schedules +// as many goroutines as necessary to accomplish this task. This call then +// blocks, waiting on the writes to complete. Note that we use the sem parameter +// to limit the number of concurrent writes taking place. +func writeBlocks(ctxt *Link, out *OutBuf, sem chan int, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) { + for i, s := range syms { + if ldr.SymValue(s) >= addr && !ldr.AttrSubSymbol(s) { + syms = syms[i:] + break + } + } + + var wg sync.WaitGroup + max, lastAddr, written := int64(blockSize), addr+size, int64(0) + for addr < lastAddr { + // Find the last symbol we'd write. + idx := -1 + for i, s := range syms { + if ldr.AttrSubSymbol(s) { + continue + } + + // If the next symbol's size would put us out of bounds on the total length, + // stop looking. + end := ldr.SymValue(s) + ldr.SymSize(s) + if end > lastAddr { + break + } + + // We're gonna write this symbol. + idx = i + + // If we cross over the max size, we've got enough symbols. + if end > addr+max { + break + } + } + + // If we didn't find any symbols to write, we're done here. + if idx < 0 { + break + } + + // Compute the length to write, including padding. + // We need to write to the end address (lastAddr), or the next symbol's + // start address, whichever comes first. If there is no more symbols, + // just write to lastAddr. This ensures we don't leave holes between the + // blocks or at the end. + length := int64(0) + if idx+1 < len(syms) { + // Find the next top-level symbol. + // Skip over sub symbols so we won't split a containter symbol + // into two blocks. + next := syms[idx+1] + for ldr.AttrSubSymbol(next) { + idx++ + next = syms[idx+1] + } + length = ldr.SymValue(next) - addr + } + if length == 0 || length > lastAddr-addr { + length = lastAddr - addr + } + + // Start the block output operator. + if o, err := out.View(uint64(out.Offset() + written)); err == nil { + sem <- 1 + wg.Add(1) + go func(o *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) { + writeBlock(ctxt, o, ldr, syms, addr, size, pad) + wg.Done() + <-sem + }(o, ldr, syms, addr, length, pad) + } else { // output not mmaped, don't parallelize. + writeBlock(ctxt, out, ldr, syms, addr, length, pad) + } + + // Prepare for the next loop. + if idx != -1 { + syms = syms[idx+1:] + } + written += length + addr += length + } + wg.Wait() +} + +func writeBlock(ctxt *Link, out *OutBuf, ldr *loader.Loader, syms []loader.Sym, addr, size int64, pad []byte) { + + st := ctxt.makeRelocSymState() + + // This doesn't distinguish the memory size from the file + // size, and it lays out the file based on Symbol.Value, which + // is the virtual address. DWARF compression changes file sizes, + // so dwarfcompress will fix this up later if necessary. + eaddr := addr + size + for _, s := range syms { + if ldr.AttrSubSymbol(s) { + continue + } + val := ldr.SymValue(s) + if val >= eaddr { + break + } + if val < addr { + ldr.Errorf(s, "phase error: addr=%#x but sym=%#x type=%v sect=%v", addr, val, ldr.SymType(s), ldr.SymSect(s).Name) + errorexit() + } + if addr < val { + out.WriteStringPad("", int(val-addr), pad) + addr = val + } + P := out.WriteSym(ldr, s) + st.relocsym(s, P) + if f, ok := ctxt.generatorSyms[s]; ok { + f(ctxt, s) + } + addr += int64(len(P)) + siz := ldr.SymSize(s) + if addr < val+siz { + out.WriteStringPad("", int(val+siz-addr), pad) + addr = val + siz + } + if addr != val+siz { + ldr.Errorf(s, "phase error: addr=%#x value+size=%#x", addr, val+siz) + errorexit() + } + if val+siz >= eaddr { + break + } + } + + if addr < eaddr { + out.WriteStringPad("", int(eaddr-addr), pad) + } +} + +type writeFn func(*Link, *OutBuf, int64, int64) + +// writeParallel handles scheduling parallel execution of data write functions. +func writeParallel(wg *sync.WaitGroup, fn writeFn, ctxt *Link, seek, vaddr, length uint64) { + if out, err := ctxt.Out.View(seek); err != nil { + ctxt.Out.SeekSet(int64(seek)) + fn(ctxt, ctxt.Out, int64(vaddr), int64(length)) + } else { + wg.Add(1) + go func() { + defer wg.Done() + fn(ctxt, out, int64(vaddr), int64(length)) + }() + } +} + +func datblk(ctxt *Link, out *OutBuf, addr, size int64) { + writeDatblkToOutBuf(ctxt, out, addr, size) +} + +// Used only on Wasm for now. +func DatblkBytes(ctxt *Link, addr int64, size int64) []byte { + buf := make([]byte, size) + out := &OutBuf{heap: buf} + writeDatblkToOutBuf(ctxt, out, addr, size) + return buf +} + +func writeDatblkToOutBuf(ctxt *Link, out *OutBuf, addr int64, size int64) { + writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, ctxt.datap, addr, size, zeros[:]) +} + +func dwarfblk(ctxt *Link, out *OutBuf, addr int64, size int64) { + // Concatenate the section symbol lists into a single list to pass + // to writeBlocks. + // + // NB: ideally we would do a separate writeBlocks call for each + // section, but this would run the risk of undoing any file offset + // adjustments made during layout. + n := 0 + for i := range dwarfp { + n += len(dwarfp[i].syms) + } + syms := make([]loader.Sym, 0, n) + for i := range dwarfp { + syms = append(syms, dwarfp[i].syms...) + } + writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, syms, addr, size, zeros[:]) +} + +var zeros [512]byte + +var ( + strdata = make(map[string]string) + strnames []string +) + +func addstrdata1(ctxt *Link, arg string) { + eq := strings.Index(arg, "=") + dot := strings.LastIndex(arg[:eq+1], ".") + if eq < 0 || dot < 0 { + Exitf("-X flag requires argument of the form importpath.name=value") + } + pkg := arg[:dot] + if ctxt.BuildMode == BuildModePlugin && pkg == "main" { + pkg = *flagPluginPath + } + pkg = objabi.PathToPrefix(pkg) + name := pkg + arg[dot:eq] + value := arg[eq+1:] + if _, ok := strdata[name]; !ok { + strnames = append(strnames, name) + } + strdata[name] = value +} + +// addstrdata sets the initial value of the string variable name to value. +func addstrdata(arch *sys.Arch, l *loader.Loader, name, value string) { + s := l.Lookup(name, 0) + if s == 0 { + return + } + if goType := l.SymGoType(s); goType == 0 { + return + } else if typeName := l.SymName(goType); typeName != "type.string" { + Errorf(nil, "%s: cannot set with -X: not a var of type string (%s)", name, typeName) + return + } + if !l.AttrReachable(s) { + return // don't bother setting unreachable variable + } + bld := l.MakeSymbolUpdater(s) + if bld.Type() == sym.SBSS { + bld.SetType(sym.SDATA) + } + + p := fmt.Sprintf("%s.str", name) + sbld := l.CreateSymForUpdate(p, 0) + sbld.Addstring(value) + sbld.SetType(sym.SRODATA) + + bld.SetSize(0) + bld.SetData(make([]byte, 0, arch.PtrSize*2)) + bld.SetReadOnly(false) + bld.ResetRelocs() + bld.AddAddrPlus(arch, sbld.Sym(), 0) + bld.AddUint(arch, uint64(len(value))) +} + +func (ctxt *Link) dostrdata() { + for _, name := range strnames { + addstrdata(ctxt.Arch, ctxt.loader, name, strdata[name]) + } +} + +// addgostring adds str, as a Go string value, to s. symname is the name of the +// symbol used to define the string data and must be unique per linked object. +func addgostring(ctxt *Link, ldr *loader.Loader, s *loader.SymbolBuilder, symname, str string) { + sdata := ldr.CreateSymForUpdate(symname, 0) + if sdata.Type() != sym.Sxxx { + ctxt.Errorf(s.Sym(), "duplicate symname in addgostring: %s", symname) + } + sdata.SetLocal(true) + sdata.SetType(sym.SRODATA) + sdata.SetSize(int64(len(str))) + sdata.SetData([]byte(str)) + s.AddAddr(ctxt.Arch, sdata.Sym()) + s.AddUint(ctxt.Arch, uint64(len(str))) +} + +func addinitarrdata(ctxt *Link, ldr *loader.Loader, s loader.Sym) { + p := ldr.SymName(s) + ".ptr" + sp := ldr.CreateSymForUpdate(p, 0) + sp.SetType(sym.SINITARR) + sp.SetSize(0) + sp.SetDuplicateOK(true) + sp.AddAddr(ctxt.Arch, s) +} + +// symalign returns the required alignment for the given symbol s. +func symalign(ldr *loader.Loader, s loader.Sym) int32 { + min := int32(thearch.Minalign) + align := ldr.SymAlign(s) + if align >= min { + return align + } else if align != 0 { + return min + } + // FIXME: figure out a way to avoid checking by name here. + sname := ldr.SymName(s) + if strings.HasPrefix(sname, "go.string.") || strings.HasPrefix(sname, "type..namedata.") { + // String data is just bytes. + // If we align it, we waste a lot of space to padding. + return min + } + align = int32(thearch.Maxalign) + ssz := ldr.SymSize(s) + for int64(align) > ssz && align > min { + align >>= 1 + } + ldr.SetSymAlign(s, align) + return align +} + +func aligndatsize(state *dodataState, datsize int64, s loader.Sym) int64 { + return Rnd(datsize, int64(symalign(state.ctxt.loader, s))) +} + +const debugGCProg = false + +type GCProg struct { + ctxt *Link + sym *loader.SymbolBuilder + w gcprog.Writer +} + +func (p *GCProg) Init(ctxt *Link, name string) { + p.ctxt = ctxt + p.sym = ctxt.loader.CreateSymForUpdate(name, 0) + p.w.Init(p.writeByte()) + if debugGCProg { + fmt.Fprintf(os.Stderr, "ld: start GCProg %s\n", name) + p.w.Debug(os.Stderr) + } +} + +func (p *GCProg) writeByte() func(x byte) { + return func(x byte) { + p.sym.AddUint8(x) + } +} + +func (p *GCProg) End(size int64) { + p.w.ZeroUntil(size / int64(p.ctxt.Arch.PtrSize)) + p.w.End() + if debugGCProg { + fmt.Fprintf(os.Stderr, "ld: end GCProg\n") + } +} + +func (p *GCProg) AddSym(s loader.Sym) { + ldr := p.ctxt.loader + typ := ldr.SymGoType(s) + + // Things without pointers should be in sym.SNOPTRDATA or sym.SNOPTRBSS; + // everything we see should have pointers and should therefore have a type. + if typ == 0 { + switch ldr.SymName(s) { + case "runtime.data", "runtime.edata", "runtime.bss", "runtime.ebss": + // Ignore special symbols that are sometimes laid out + // as real symbols. See comment about dyld on darwin in + // the address function. + return + } + p.ctxt.Errorf(p.sym.Sym(), "missing Go type information for global symbol %s: size %d", ldr.SymName(s), ldr.SymSize(s)) + return + } + + ptrsize := int64(p.ctxt.Arch.PtrSize) + typData := ldr.Data(typ) + nptr := decodetypePtrdata(p.ctxt.Arch, typData) / ptrsize + + if debugGCProg { + fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", ldr.SymName(s), ldr.SymValue(s), ldr.SymValue(s)/ptrsize, nptr) + } + + sval := ldr.SymValue(s) + if decodetypeUsegcprog(p.ctxt.Arch, typData) == 0 { + // Copy pointers from mask into program. + mask := decodetypeGcmask(p.ctxt, typ) + for i := int64(0); i < nptr; i++ { + if (mask[i/8]>>uint(i%8))&1 != 0 { + p.w.Ptr(sval/ptrsize + i) + } + } + return + } + + // Copy program. + prog := decodetypeGcprog(p.ctxt, typ) + p.w.ZeroUntil(sval / ptrsize) + p.w.Append(prog[4:], nptr) +} + +// cutoff is the maximum data section size permitted by the linker +// (see issue #9862). +const cutoff = 2e9 // 2 GB (or so; looks better in errors than 2^31) + +func (state *dodataState) checkdatsize(symn sym.SymKind) { + if state.datsize > cutoff { + Errorf(nil, "too much data in section %v (over %v bytes)", symn, cutoff) + } +} + +// fixZeroSizedSymbols gives a few special symbols with zero size some space. +func fixZeroSizedSymbols(ctxt *Link) { + // The values in moduledata are filled out by relocations + // pointing to the addresses of these special symbols. + // Typically these symbols have no size and are not laid + // out with their matching section. + // + // However on darwin, dyld will find the special symbol + // in the first loaded module, even though it is local. + // + // (An hypothesis, formed without looking in the dyld sources: + // these special symbols have no size, so their address + // matches a real symbol. The dynamic linker assumes we + // want the normal symbol with the same address and finds + // it in the other module.) + // + // To work around this we lay out the symbls whose + // addresses are vital for multi-module programs to work + // as normal symbols, and give them a little size. + // + // On AIX, as all DATA sections are merged together, ld might not put + // these symbols at the beginning of their respective section if there + // aren't real symbols, their alignment might not match the + // first symbol alignment. Therefore, there are explicitly put at the + // beginning of their section with the same alignment. + if !(ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) && !(ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + return + } + + ldr := ctxt.loader + bss := ldr.CreateSymForUpdate("runtime.bss", 0) + bss.SetSize(8) + ldr.SetAttrSpecial(bss.Sym(), false) + + ebss := ldr.CreateSymForUpdate("runtime.ebss", 0) + ldr.SetAttrSpecial(ebss.Sym(), false) + + data := ldr.CreateSymForUpdate("runtime.data", 0) + data.SetSize(8) + ldr.SetAttrSpecial(data.Sym(), false) + + edata := ldr.CreateSymForUpdate("runtime.edata", 0) + ldr.SetAttrSpecial(edata.Sym(), false) + + if ctxt.HeadType == objabi.Haix { + // XCOFFTOC symbols are part of .data section. + edata.SetType(sym.SXCOFFTOC) + } + + types := ldr.CreateSymForUpdate("runtime.types", 0) + types.SetType(sym.STYPE) + types.SetSize(8) + ldr.SetAttrSpecial(types.Sym(), false) + + etypes := ldr.CreateSymForUpdate("runtime.etypes", 0) + etypes.SetType(sym.SFUNCTAB) + ldr.SetAttrSpecial(etypes.Sym(), false) + + if ctxt.HeadType == objabi.Haix { + rodata := ldr.CreateSymForUpdate("runtime.rodata", 0) + rodata.SetType(sym.SSTRING) + rodata.SetSize(8) + ldr.SetAttrSpecial(rodata.Sym(), false) + + erodata := ldr.CreateSymForUpdate("runtime.erodata", 0) + ldr.SetAttrSpecial(erodata.Sym(), false) + } +} + +// makeRelroForSharedLib creates a section of readonly data if necessary. +func (state *dodataState) makeRelroForSharedLib(target *Link) { + if !target.UseRelro() { + return + } + + // "read only" data with relocations needs to go in its own section + // when building a shared library. We do this by boosting objects of + // type SXXX with relocations to type SXXXRELRO. + ldr := target.loader + for _, symnro := range sym.ReadOnly { + symnrelro := sym.RelROMap[symnro] + + ro := []loader.Sym{} + relro := state.data[symnrelro] + + for _, s := range state.data[symnro] { + relocs := ldr.Relocs(s) + isRelro := relocs.Count() > 0 + switch state.symType(s) { + case sym.STYPE, sym.STYPERELRO, sym.SGOFUNCRELRO: + // Symbols are not sorted yet, so it is possible + // that an Outer symbol has been changed to a + // relro Type before it reaches here. + isRelro = true + case sym.SFUNCTAB: + if ldr.SymName(s) == "runtime.etypes" { + // runtime.etypes must be at the end of + // the relro data. + isRelro = true + } + } + if isRelro { + state.setSymType(s, symnrelro) + if outer := ldr.OuterSym(s); outer != 0 { + state.setSymType(outer, symnrelro) + } + relro = append(relro, s) + } else { + ro = append(ro, s) + } + } + + // Check that we haven't made two symbols with the same .Outer into + // different types (because references two symbols with non-nil Outer + // become references to the outer symbol + offset it's vital that the + // symbol and the outer end up in the same section). + for _, s := range relro { + if outer := ldr.OuterSym(s); outer != 0 { + st := state.symType(s) + ost := state.symType(outer) + if st != ost { + state.ctxt.Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)", + ldr.SymName(outer), st, ost) + } + } + } + + state.data[symnro] = ro + state.data[symnrelro] = relro + } +} + +// dodataState holds bits of state information needed by dodata() and the +// various helpers it calls. The lifetime of these items should not extend +// past the end of dodata(). +type dodataState struct { + // Link context + ctxt *Link + // Data symbols bucketed by type. + data [sym.SXREF][]loader.Sym + // Max alignment for each flavor of data symbol. + dataMaxAlign [sym.SXREF]int32 + // Overridden sym type + symGroupType []sym.SymKind + // Current data size so far. + datsize int64 +} + +// A note on symType/setSymType below: +// +// In the legacy linker, the types of symbols (notably data symbols) are +// changed during the symtab() phase so as to insure that similar symbols +// are bucketed together, then their types are changed back again during +// dodata. Symbol to section assignment also plays tricks along these lines +// in the case where a relro segment is needed. +// +// The value returned from setType() below reflects the effects of +// any overrides made by symtab and/or dodata. + +// symType returns the (possibly overridden) type of 's'. +func (state *dodataState) symType(s loader.Sym) sym.SymKind { + if int(s) < len(state.symGroupType) { + if override := state.symGroupType[s]; override != 0 { + return override + } + } + return state.ctxt.loader.SymType(s) +} + +// setSymType sets a new override type for 's'. +func (state *dodataState) setSymType(s loader.Sym, kind sym.SymKind) { + if s == 0 { + panic("bad") + } + if int(s) < len(state.symGroupType) { + state.symGroupType[s] = kind + } else { + su := state.ctxt.loader.MakeSymbolUpdater(s) + su.SetType(kind) + } +} + +func (ctxt *Link) dodata(symGroupType []sym.SymKind) { + + // Give zeros sized symbols space if necessary. + fixZeroSizedSymbols(ctxt) + + // Collect data symbols by type into data. + state := dodataState{ctxt: ctxt, symGroupType: symGroupType} + ldr := ctxt.loader + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || ldr.AttrSubSymbol(s) || + !ldr.TopLevelSym(s) { + continue + } + + st := state.symType(s) + + if st <= sym.STEXT || st >= sym.SXREF { + continue + } + state.data[st] = append(state.data[st], s) + + // Similarly with checking the onlist attr. + if ldr.AttrOnList(s) { + log.Fatalf("symbol %s listed multiple times", ldr.SymName(s)) + } + ldr.SetAttrOnList(s, true) + } + + // Now that we have the data symbols, but before we start + // to assign addresses, record all the necessary + // dynamic relocations. These will grow the relocation + // symbol, which is itself data. + // + // On darwin, we need the symbol table numbers for dynreloc. + if ctxt.HeadType == objabi.Hdarwin { + machosymorder(ctxt) + } + state.dynreloc(ctxt) + + // Move any RO data with relocations to a separate section. + state.makeRelroForSharedLib(ctxt) + + // Set alignment for the symbol with the largest known index, + // so as to trigger allocation of the loader's internal + // alignment array. This will avoid data races in the parallel + // section below. + lastSym := loader.Sym(ldr.NSym() - 1) + ldr.SetSymAlign(lastSym, ldr.SymAlign(lastSym)) + + // Sort symbols. + var wg sync.WaitGroup + for symn := range state.data { + symn := sym.SymKind(symn) + wg.Add(1) + go func() { + state.data[symn], state.dataMaxAlign[symn] = state.dodataSect(ctxt, symn, state.data[symn]) + wg.Done() + }() + } + wg.Wait() + + if ctxt.IsELF { + // Make .rela and .rela.plt contiguous, the ELF ABI requires this + // and Solaris actually cares. + syms := state.data[sym.SELFROSECT] + reli, plti := -1, -1 + for i, s := range syms { + switch ldr.SymName(s) { + case ".rel.plt", ".rela.plt": + plti = i + case ".rel", ".rela": + reli = i + } + } + if reli >= 0 && plti >= 0 && plti != reli+1 { + var first, second int + if plti > reli { + first, second = reli, plti + } else { + first, second = plti, reli + } + rel, plt := syms[reli], syms[plti] + copy(syms[first+2:], syms[first+1:second]) + syms[first+0] = rel + syms[first+1] = plt + + // Make sure alignment doesn't introduce a gap. + // Setting the alignment explicitly prevents + // symalign from basing it on the size and + // getting it wrong. + ldr.SetSymAlign(rel, int32(ctxt.Arch.RegSize)) + ldr.SetSymAlign(plt, int32(ctxt.Arch.RegSize)) + } + state.data[sym.SELFROSECT] = syms + } + + if ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal { + // These symbols must have the same alignment as their section. + // Otherwize, ld might change the layout of Go sections. + ldr.SetSymAlign(ldr.Lookup("runtime.data", 0), state.dataMaxAlign[sym.SDATA]) + ldr.SetSymAlign(ldr.Lookup("runtime.bss", 0), state.dataMaxAlign[sym.SBSS]) + } + + // Create *sym.Section objects and assign symbols to sections for + // data/rodata (and related) symbols. + state.allocateDataSections(ctxt) + + // Create *sym.Section objects and assign symbols to sections for + // DWARF symbols. + state.allocateDwarfSections(ctxt) + + /* number the sections */ + n := int16(1) + + for _, sect := range Segtext.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segrodata.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segrelrodata.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segdata.Sections { + sect.Extnum = n + n++ + } + for _, sect := range Segdwarf.Sections { + sect.Extnum = n + n++ + } +} + +// allocateDataSectionForSym creates a new sym.Section into which a a +// single symbol will be placed. Here "seg" is the segment into which +// the section will go, "s" is the symbol to be placed into the new +// section, and "rwx" contains permissions for the section. +func (state *dodataState) allocateDataSectionForSym(seg *sym.Segment, s loader.Sym, rwx int) *sym.Section { + ldr := state.ctxt.loader + sname := ldr.SymName(s) + sect := addsection(ldr, state.ctxt.Arch, seg, sname, rwx) + sect.Align = symalign(ldr, s) + state.datsize = Rnd(state.datsize, int64(sect.Align)) + sect.Vaddr = uint64(state.datsize) + return sect +} + +// allocateNamedDataSection creates a new sym.Section for a category +// of data symbols. Here "seg" is the segment into which the section +// will go, "sName" is the name to give to the section, "types" is a +// range of symbol types to be put into the section, and "rwx" +// contains permissions for the section. +func (state *dodataState) allocateNamedDataSection(seg *sym.Segment, sName string, types []sym.SymKind, rwx int) *sym.Section { + sect := addsection(state.ctxt.loader, state.ctxt.Arch, seg, sName, rwx) + if len(types) == 0 { + sect.Align = 1 + } else if len(types) == 1 { + sect.Align = state.dataMaxAlign[types[0]] + } else { + for _, symn := range types { + align := state.dataMaxAlign[symn] + if sect.Align < align { + sect.Align = align + } + } + } + state.datsize = Rnd(state.datsize, int64(sect.Align)) + sect.Vaddr = uint64(state.datsize) + return sect +} + +// assignDsymsToSection assigns a collection of data symbols to a +// newly created section. "sect" is the section into which to place +// the symbols, "syms" holds the list of symbols to assign, +// "forceType" (if non-zero) contains a new sym type to apply to each +// sym during the assignment, and "aligner" is a hook to call to +// handle alignment during the assignment process. +func (state *dodataState) assignDsymsToSection(sect *sym.Section, syms []loader.Sym, forceType sym.SymKind, aligner func(state *dodataState, datsize int64, s loader.Sym) int64) { + ldr := state.ctxt.loader + for _, s := range syms { + state.datsize = aligner(state, state.datsize, s) + ldr.SetSymSect(s, sect) + if forceType != sym.Sxxx { + state.setSymType(s, forceType) + } + ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr)) + state.datsize += ldr.SymSize(s) + } + sect.Length = uint64(state.datsize) - sect.Vaddr +} + +func (state *dodataState) assignToSection(sect *sym.Section, symn sym.SymKind, forceType sym.SymKind) { + state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) + state.checkdatsize(symn) +} + +// allocateSingleSymSections walks through the bucketed data symbols +// with type 'symn', creates a new section for each sym, and assigns +// the sym to a newly created section. Section name is set from the +// symbol name. "Seg" is the segment into which to place the new +// section, "forceType" is the new sym.SymKind to assign to the symbol +// within the section, and "rwx" holds section permissions. +func (state *dodataState) allocateSingleSymSections(seg *sym.Segment, symn sym.SymKind, forceType sym.SymKind, rwx int) { + ldr := state.ctxt.loader + for _, s := range state.data[symn] { + sect := state.allocateDataSectionForSym(seg, s, rwx) + ldr.SetSymSect(s, sect) + state.setSymType(s, forceType) + ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr)) + state.datsize += ldr.SymSize(s) + sect.Length = uint64(state.datsize) - sect.Vaddr + } + state.checkdatsize(symn) +} + +// allocateNamedSectionAndAssignSyms creates a new section with the +// specified name, then walks through the bucketed data symbols with +// type 'symn' and assigns each of them to this new section. "Seg" is +// the segment into which to place the new section, "secName" is the +// name to give to the new section, "forceType" (if non-zero) contains +// a new sym type to apply to each sym during the assignment, and +// "rwx" holds section permissions. +func (state *dodataState) allocateNamedSectionAndAssignSyms(seg *sym.Segment, secName string, symn sym.SymKind, forceType sym.SymKind, rwx int) *sym.Section { + + sect := state.allocateNamedDataSection(seg, secName, []sym.SymKind{symn}, rwx) + state.assignDsymsToSection(sect, state.data[symn], forceType, aligndatsize) + return sect +} + +// allocateDataSections allocates sym.Section objects for data/rodata +// (and related) symbols, and then assigns symbols to those sections. +func (state *dodataState) allocateDataSections(ctxt *Link) { + // Allocate sections. + // Data is processed before segtext, because we need + // to see all symbols in the .data and .bss sections in order + // to generate garbage collection information. + + // Writable data sections that do not need any specialized handling. + writable := []sym.SymKind{ + sym.SBUILDINFO, + sym.SELFSECT, + sym.SMACHO, + sym.SMACHOGOT, + sym.SWINDOWS, + } + for _, symn := range writable { + state.allocateSingleSymSections(&Segdata, symn, sym.SDATA, 06) + } + ldr := ctxt.loader + + // .got (and .toc on ppc64) + if len(state.data[sym.SELFGOT]) > 0 { + sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".got", sym.SELFGOT, sym.SDATA, 06) + if ctxt.IsPPC64() { + for _, s := range state.data[sym.SELFGOT] { + // Resolve .TOC. symbol for this object file (ppc64) + + toc := ldr.Lookup(".TOC.", int(ldr.SymVersion(s))) + if toc != 0 { + ldr.SetSymSect(toc, sect) + ldr.AddInteriorSym(s, toc) + ldr.SetSymValue(toc, 0x8000) + } + } + } + } + + /* pointer-free data */ + sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrdata", sym.SNOPTRDATA, sym.SDATA, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrdata", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrdata", 0), sect) + + hasinitarr := ctxt.linkShared + + /* shared library initializer */ + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin: + hasinitarr = true + } + + if ctxt.HeadType == objabi.Haix { + if len(state.data[sym.SINITARR]) > 0 { + Errorf(nil, "XCOFF format doesn't allow .init_array section") + } + } + + if hasinitarr && len(state.data[sym.SINITARR]) > 0 { + state.allocateNamedSectionAndAssignSyms(&Segdata, ".init_array", sym.SINITARR, sym.Sxxx, 06) + } + + /* data */ + sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".data", sym.SDATA, sym.SDATA, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.data", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.edata", 0), sect) + dataGcEnd := state.datsize - int64(sect.Vaddr) + + // On AIX, TOC entries must be the last of .data + // These aren't part of gc as they won't change during the runtime. + state.assignToSection(sect, sym.SXCOFFTOC, sym.SDATA) + state.checkdatsize(sym.SDATA) + sect.Length = uint64(state.datsize) - sect.Vaddr + + /* bss */ + sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".bss", sym.SBSS, sym.Sxxx, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.bss", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ebss", 0), sect) + bssGcEnd := state.datsize - int64(sect.Vaddr) + + // Emit gcdata for bss symbols now that symbol values have been assigned. + gcsToEmit := []struct { + symName string + symKind sym.SymKind + gcEnd int64 + }{ + {"runtime.gcdata", sym.SDATA, dataGcEnd}, + {"runtime.gcbss", sym.SBSS, bssGcEnd}, + } + for _, g := range gcsToEmit { + var gc GCProg + gc.Init(ctxt, g.symName) + for _, s := range state.data[g.symKind] { + gc.AddSym(s) + } + gc.End(g.gcEnd) + } + + /* pointer-free bss */ + sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrbss", sym.SNOPTRBSS, sym.Sxxx, 06) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrbss", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrbss", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.end", 0), sect) + + // Coverage instrumentation counters for libfuzzer. + if len(state.data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 { + state.allocateNamedSectionAndAssignSyms(&Segdata, "__libfuzzer_extra_counters", sym.SLIBFUZZER_EXTRA_COUNTER, sym.Sxxx, 06) + } + + if len(state.data[sym.STLSBSS]) > 0 { + var sect *sym.Section + // FIXME: not clear why it is sometimes necessary to suppress .tbss section creation. + if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) { + sect = addsection(ldr, ctxt.Arch, &Segdata, ".tbss", 06) + sect.Align = int32(ctxt.Arch.PtrSize) + // FIXME: why does this need to be set to zero? + sect.Vaddr = 0 + } + state.datsize = 0 + + for _, s := range state.data[sym.STLSBSS] { + state.datsize = aligndatsize(state, state.datsize, s) + if sect != nil { + ldr.SetSymSect(s, sect) + } + ldr.SetSymValue(s, state.datsize) + state.datsize += ldr.SymSize(s) + } + state.checkdatsize(sym.STLSBSS) + + if sect != nil { + sect.Length = uint64(state.datsize) + } + } + + /* + * We finished data, begin read-only data. + * Not all systems support a separate read-only non-executable data section. + * ELF and Windows PE systems do. + * OS X and Plan 9 do not. + * And if we're using external linking mode, the point is moot, + * since it's not our decision; that code expects the sections in + * segtext. + */ + var segro *sym.Segment + if ctxt.IsELF && ctxt.LinkMode == LinkInternal { + segro = &Segrodata + } else if ctxt.HeadType == objabi.Hwindows { + segro = &Segrodata + } else { + segro = &Segtext + } + + state.datsize = 0 + + /* read-only executable ELF, Mach-O sections */ + if len(state.data[sym.STEXT]) != 0 { + culprit := ldr.SymName(state.data[sym.STEXT][0]) + Errorf(nil, "dodata found an sym.STEXT symbol: %s", culprit) + } + state.allocateSingleSymSections(&Segtext, sym.SELFRXSECT, sym.SRODATA, 05) + state.allocateSingleSymSections(&Segtext, sym.SMACHOPLT, sym.SRODATA, 05) + + /* read-only data */ + sect = state.allocateNamedDataSection(segro, ".rodata", sym.ReadOnly, 04) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.rodata", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.erodata", 0), sect) + if !ctxt.UseRelro() { + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect) + } + for _, symn := range sym.ReadOnly { + symnStartValue := state.datsize + state.assignToSection(sect, symn, sym.SRODATA) + setCarrierSize(symn, state.datsize-symnStartValue) + if ctxt.HeadType == objabi.Haix { + // Read-only symbols might be wrapped inside their outer + // symbol. + // XCOFF symbol table needs to know the size of + // these outer symbols. + xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) + } + } + + /* read-only ELF, Mach-O sections */ + state.allocateSingleSymSections(segro, sym.SELFROSECT, sym.SRODATA, 04) + + // There is some data that are conceptually read-only but are written to by + // relocations. On GNU systems, we can arrange for the dynamic linker to + // mprotect sections after relocations are applied by giving them write + // permissions in the object file and calling them ".data.rel.ro.FOO". We + // divide the .rodata section between actual .rodata and .data.rel.ro.rodata, + // but for the other sections that this applies to, we just write a read-only + // .FOO section or a read-write .data.rel.ro.FOO section depending on the + // situation. + // TODO(mwhudson): It would make sense to do this more widely, but it makes + // the system linker segfault on darwin. + const relroPerm = 06 + const fallbackPerm = 04 + relroSecPerm := fallbackPerm + genrelrosecname := func(suffix string) string { + if suffix == "" { + return ".rodata" + } + return suffix + } + seg := segro + + if ctxt.UseRelro() { + segrelro := &Segrelrodata + if ctxt.LinkMode == LinkExternal && !ctxt.IsAIX() && !ctxt.IsDarwin() { + // Using a separate segment with an external + // linker results in some programs moving + // their data sections unexpectedly, which + // corrupts the moduledata. So we use the + // rodata segment and let the external linker + // sort out a rel.ro segment. + segrelro = segro + } else { + // Reset datsize for new segment. + state.datsize = 0 + } + + if !ctxt.IsDarwin() { // We don't need the special names on darwin. + genrelrosecname = func(suffix string) string { + return ".data.rel.ro" + suffix + } + } + + relroReadOnly := []sym.SymKind{} + for _, symnro := range sym.ReadOnly { + symn := sym.RelROMap[symnro] + relroReadOnly = append(relroReadOnly, symn) + } + seg = segrelro + relroSecPerm = relroPerm + + /* data only written by relocations */ + sect = state.allocateNamedDataSection(segrelro, genrelrosecname(""), relroReadOnly, relroSecPerm) + + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect) + + for i, symnro := range sym.ReadOnly { + if i == 0 && symnro == sym.STYPE && ctxt.HeadType != objabi.Haix { + // Skip forward so that no type + // reference uses a zero offset. + // This is unlikely but possible in small + // programs with no other read-only data. + state.datsize++ + } + + symn := sym.RelROMap[symnro] + symnStartValue := state.datsize + + for _, s := range state.data[symn] { + outer := ldr.OuterSym(s) + if s != 0 && ldr.SymSect(outer) != nil && ldr.SymSect(outer) != sect { + ctxt.Errorf(s, "s.Outer (%s) in different section from s, %s != %s", ldr.SymName(outer), ldr.SymSect(outer).Name, sect.Name) + } + } + state.assignToSection(sect, symn, sym.SRODATA) + setCarrierSize(symn, state.datsize-symnStartValue) + if ctxt.HeadType == objabi.Haix { + // Read-only symbols might be wrapped inside their outer + // symbol. + // XCOFF symbol table needs to know the size of + // these outer symbols. + xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn) + } + } + + sect.Length = uint64(state.datsize) - sect.Vaddr + } + + /* typelink */ + sect = state.allocateNamedDataSection(seg, genrelrosecname(".typelink"), []sym.SymKind{sym.STYPELINK}, relroSecPerm) + + typelink := ldr.CreateSymForUpdate("runtime.typelink", 0) + ldr.SetSymSect(typelink.Sym(), sect) + typelink.SetType(sym.SRODATA) + state.datsize += typelink.Size() + state.checkdatsize(sym.STYPELINK) + sect.Length = uint64(state.datsize) - sect.Vaddr + + /* itablink */ + sect = state.allocateNamedDataSection(seg, genrelrosecname(".itablink"), []sym.SymKind{sym.SITABLINK}, relroSecPerm) + + itablink := ldr.CreateSymForUpdate("runtime.itablink", 0) + ldr.SetSymSect(itablink.Sym(), sect) + itablink.SetType(sym.SRODATA) + state.datsize += itablink.Size() + state.checkdatsize(sym.SITABLINK) + sect.Length = uint64(state.datsize) - sect.Vaddr + + /* gosymtab */ + sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gosymtab"), sym.SSYMTAB, sym.SRODATA, relroSecPerm) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.symtab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.esymtab", 0), sect) + + /* gopclntab */ + sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gopclntab"), sym.SPCLNTAB, sym.SRODATA, relroSecPerm) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pcheader", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.funcnametab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.cutab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect) + setCarrierSize(sym.SPCLNTAB, int64(sect.Length)) + if ctxt.HeadType == objabi.Haix { + xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB) + } + + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if state.datsize != int64(uint32(state.datsize)) { + Errorf(nil, "read-only data segment too large: %d", state.datsize) + } + + siz := 0 + for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ { + siz += len(state.data[symn]) + } + ctxt.datap = make([]loader.Sym, 0, siz) + for symn := sym.SELFRXSECT; symn < sym.SXREF; symn++ { + ctxt.datap = append(ctxt.datap, state.data[symn]...) + } +} + +// allocateDwarfSections allocates sym.Section objects for DWARF +// symbols, and assigns symbols to sections. +func (state *dodataState) allocateDwarfSections(ctxt *Link) { + + alignOne := func(state *dodataState, datsize int64, s loader.Sym) int64 { return datsize } + + ldr := ctxt.loader + for i := 0; i < len(dwarfp); i++ { + // First the section symbol. + s := dwarfp[i].secSym() + sect := state.allocateNamedDataSection(&Segdwarf, ldr.SymName(s), []sym.SymKind{}, 04) + ldr.SetSymSect(s, sect) + sect.Sym = sym.LoaderSym(s) + curType := ldr.SymType(s) + state.setSymType(s, sym.SRODATA) + ldr.SetSymValue(s, int64(uint64(state.datsize)-sect.Vaddr)) + state.datsize += ldr.SymSize(s) + + // Then any sub-symbols for the section symbol. + subSyms := dwarfp[i].subSyms() + state.assignDsymsToSection(sect, subSyms, sym.SRODATA, alignOne) + + for j := 0; j < len(subSyms); j++ { + s := subSyms[j] + if ctxt.HeadType == objabi.Haix && curType == sym.SDWARFLOC { + // Update the size of .debug_loc for this symbol's + // package. + addDwsectCUSize(".debug_loc", ldr.SymPkg(s), uint64(ldr.SymSize(s))) + } + } + sect.Length = uint64(state.datsize) - sect.Vaddr + state.checkdatsize(curType) + } +} + +type symNameSize struct { + name string + sz int64 + val int64 + sym loader.Sym +} + +func (state *dodataState) dodataSect(ctxt *Link, symn sym.SymKind, syms []loader.Sym) (result []loader.Sym, maxAlign int32) { + var head, tail loader.Sym + ldr := ctxt.loader + sl := make([]symNameSize, len(syms)) + for k, s := range syms { + ss := ldr.SymSize(s) + sl[k] = symNameSize{name: ldr.SymName(s), sz: ss, sym: s} + ds := int64(len(ldr.Data(s))) + switch { + case ss < ds: + ctxt.Errorf(s, "initialize bounds (%d < %d)", ss, ds) + case ss < 0: + ctxt.Errorf(s, "negative size (%d bytes)", ss) + case ss > cutoff: + ctxt.Errorf(s, "symbol too large (%d bytes)", ss) + } + + // If the usually-special section-marker symbols are being laid + // out as regular symbols, put them either at the beginning or + // end of their section. + if (ctxt.DynlinkingGo() && ctxt.HeadType == objabi.Hdarwin) || (ctxt.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal) { + switch ldr.SymName(s) { + case "runtime.text", "runtime.bss", "runtime.data", "runtime.types", "runtime.rodata": + head = s + continue + case "runtime.etext", "runtime.ebss", "runtime.edata", "runtime.etypes", "runtime.erodata": + tail = s + continue + } + } + } + + // For ppc64, we want to interleave the .got and .toc sections + // from input files. Both are type sym.SELFGOT, so in that case + // we skip size comparison and fall through to the name + // comparison (conveniently, .got sorts before .toc). + checkSize := symn != sym.SELFGOT + + // Perform the sort. + if symn != sym.SPCLNTAB { + sort.Slice(sl, func(i, j int) bool { + si, sj := sl[i].sym, sl[j].sym + switch { + case si == head, sj == tail: + return true + case sj == head, si == tail: + return false + } + if checkSize { + isz := sl[i].sz + jsz := sl[j].sz + if isz != jsz { + return isz < jsz + } + } + iname := sl[i].name + jname := sl[j].name + if iname != jname { + return iname < jname + } + return si < sj + }) + } else { + // PCLNTAB was built internally, and has the proper order based on value. + // Sort the symbols as such. + for k, s := range syms { + sl[k].val = ldr.SymValue(s) + } + sort.Slice(sl, func(i, j int) bool { return sl[i].val < sl[j].val }) + } + + // Set alignment, construct result + syms = syms[:0] + for k := range sl { + s := sl[k].sym + if s != head && s != tail { + align := symalign(ldr, s) + if maxAlign < align { + maxAlign = align + } + } + syms = append(syms, s) + } + + return syms, maxAlign +} + +// Add buildid to beginning of text segment, on non-ELF systems. +// Non-ELF binary formats are not always flexible enough to +// give us a place to put the Go build ID. On those systems, we put it +// at the very beginning of the text segment. +// This ``header'' is read by cmd/go. +func (ctxt *Link) textbuildid() { + if ctxt.IsELF || ctxt.BuildMode == BuildModePlugin || *flagBuildid == "" { + return + } + + ldr := ctxt.loader + s := ldr.CreateSymForUpdate("go.buildid", 0) + // The \xff is invalid UTF-8, meant to make it less likely + // to find one of these accidentally. + data := "\xff Go build ID: " + strconv.Quote(*flagBuildid) + "\n \xff" + s.SetType(sym.STEXT) + s.SetData([]byte(data)) + s.SetSize(int64(len(data))) + + ctxt.Textp = append(ctxt.Textp, 0) + copy(ctxt.Textp[1:], ctxt.Textp) + ctxt.Textp[0] = s.Sym() +} + +func (ctxt *Link) buildinfo() { + if ctxt.linkShared || ctxt.BuildMode == BuildModePlugin { + // -linkshared and -buildmode=plugin get confused + // about the relocations in go.buildinfo + // pointing at the other data sections. + // The version information is only available in executables. + return + } + + ldr := ctxt.loader + s := ldr.CreateSymForUpdate(".go.buildinfo", 0) + // On AIX, .go.buildinfo must be in the symbol table as + // it has relocations. + s.SetNotInSymbolTable(!ctxt.IsAIX()) + s.SetType(sym.SBUILDINFO) + s.SetAlign(16) + // The \xff is invalid UTF-8, meant to make it less likely + // to find one of these accidentally. + const prefix = "\xff Go buildinf:" // 14 bytes, plus 2 data bytes filled in below + data := make([]byte, 32) + copy(data, prefix) + data[len(prefix)] = byte(ctxt.Arch.PtrSize) + data[len(prefix)+1] = 0 + if ctxt.Arch.ByteOrder == binary.BigEndian { + data[len(prefix)+1] = 1 + } + s.SetData(data) + s.SetSize(int64(len(data))) + r, _ := s.AddRel(objabi.R_ADDR) + r.SetOff(16) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + r.SetSym(ldr.LookupOrCreateSym("runtime.buildVersion", 0)) + r, _ = s.AddRel(objabi.R_ADDR) + r.SetOff(16 + int32(ctxt.Arch.PtrSize)) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + r.SetSym(ldr.LookupOrCreateSym("runtime.modinfo", 0)) +} + +// assign addresses to text +func (ctxt *Link) textaddress() { + addsection(ctxt.loader, ctxt.Arch, &Segtext, ".text", 05) + + // Assign PCs in text segment. + // Could parallelize, by assigning to text + // and then letting threads copy down, but probably not worth it. + sect := Segtext.Sections[0] + + sect.Align = int32(Funcalign) + + ldr := ctxt.loader + + text := ctxt.xdefine("runtime.text", sym.STEXT, 0) + etext := ctxt.xdefine("runtime.etext", sym.STEXT, 0) + ldr.SetSymSect(text, sect) + if ctxt.IsAIX() && ctxt.IsExternal() { + // Setting runtime.text has a real symbol prevents ld to + // change its base address resulting in wrong offsets for + // reflect methods. + u := ldr.MakeSymbolUpdater(text) + u.SetAlign(sect.Align) + u.SetSize(8) + } + + if (ctxt.DynlinkingGo() && ctxt.IsDarwin()) || (ctxt.IsAIX() && ctxt.IsExternal()) { + ldr.SetSymSect(etext, sect) + ctxt.Textp = append(ctxt.Textp, etext, 0) + copy(ctxt.Textp[1:], ctxt.Textp) + ctxt.Textp[0] = text + } + + va := uint64(Rnd(*FlagTextAddr, int64(Funcalign))) + n := 1 + sect.Vaddr = va + ntramps := 0 + for _, s := range ctxt.Textp { + sect, n, va = assignAddress(ctxt, sect, n, s, va, false) + + trampoline(ctxt, s) // resolve jumps, may add trampolines if jump too far + + // lay down trampolines after each function + for ; ntramps < len(ctxt.tramps); ntramps++ { + tramp := ctxt.tramps[ntramps] + if ctxt.IsAIX() && strings.HasPrefix(ldr.SymName(tramp), "runtime.text.") { + // Already set in assignAddress + continue + } + sect, n, va = assignAddress(ctxt, sect, n, tramp, va, true) + } + } + + sect.Length = va - sect.Vaddr + ldr.SetSymSect(etext, sect) + if ldr.SymValue(etext) == 0 { + // Set the address of the start/end symbols, if not already + // (i.e. not darwin+dynlink or AIX+external, see above). + ldr.SetSymValue(etext, int64(va)) + ldr.SetSymValue(text, int64(Segtext.Sections[0].Vaddr)) + } + + // merge tramps into Textp, keeping Textp in address order + if ntramps != 0 { + newtextp := make([]loader.Sym, 0, len(ctxt.Textp)+ntramps) + i := 0 + for _, s := range ctxt.Textp { + for ; i < ntramps && ldr.SymValue(ctxt.tramps[i]) < ldr.SymValue(s); i++ { + newtextp = append(newtextp, ctxt.tramps[i]) + } + newtextp = append(newtextp, s) + } + newtextp = append(newtextp, ctxt.tramps[i:ntramps]...) + + ctxt.Textp = newtextp + } +} + +// assigns address for a text symbol, returns (possibly new) section, its number, and the address +func assignAddress(ctxt *Link, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) { + ldr := ctxt.loader + if thearch.AssignAddress != nil { + return thearch.AssignAddress(ldr, sect, n, s, va, isTramp) + } + + ldr.SetSymSect(s, sect) + if ldr.AttrSubSymbol(s) { + return sect, n, va + } + + align := ldr.SymAlign(s) + if align == 0 { + align = int32(Funcalign) + } + va = uint64(Rnd(int64(va), int64(align))) + if sect.Align < align { + sect.Align = align + } + + funcsize := uint64(MINFUNC) // spacing required for findfunctab + if ldr.SymSize(s) > MINFUNC { + funcsize = uint64(ldr.SymSize(s)) + } + + // On ppc64x a text section should not be larger than 2^26 bytes due to the size of + // call target offset field in the bl instruction. Splitting into smaller text + // sections smaller than this limit allows the GNU linker to modify the long calls + // appropriately. The limit allows for the space needed for tables inserted by the linker. + + // If this function doesn't fit in the current text section, then create a new one. + + // Only break at outermost syms. + + // For debugging purposes, allow text size limit to be cranked down, + // so as to stress test the code that handles multiple text sections. + var textSizelimit uint64 = 0x1c00000 + if *FlagDebugTextSize != 0 { + textSizelimit = uint64(*FlagDebugTextSize) + } + + if ctxt.Arch.InFamily(sys.PPC64) && ldr.OuterSym(s) == 0 && ctxt.IsExternal() { + // Sanity check: make sure the limit is larger than any + // individual text symbol. + if funcsize > textSizelimit { + panic(fmt.Sprintf("error: ppc64 text size limit %d less than text symbol %s size of %d", textSizelimit, ldr.SymName(s), funcsize)) + } + + if va-sect.Vaddr+funcsize+maxSizeTrampolinesPPC64(ldr, s, isTramp) > textSizelimit { + // Set the length for the previous text section + sect.Length = va - sect.Vaddr + + // Create new section, set the starting Vaddr + sect = addsection(ctxt.loader, ctxt.Arch, &Segtext, ".text", 05) + sect.Vaddr = va + ldr.SetSymSect(s, sect) + + // Create a symbol for the start of the secondary text sections + ntext := ldr.CreateSymForUpdate(fmt.Sprintf("runtime.text.%d", n), 0) + ntext.SetSect(sect) + if ctxt.IsAIX() { + // runtime.text.X must be a real symbol on AIX. + // Assign its address directly in order to be the + // first symbol of this new section. + ntext.SetType(sym.STEXT) + ntext.SetSize(int64(MINFUNC)) + ntext.SetOnList(true) + ctxt.tramps = append(ctxt.tramps, ntext.Sym()) + + ntext.SetValue(int64(va)) + va += uint64(ntext.Size()) + + if align := ldr.SymAlign(s); align != 0 { + va = uint64(Rnd(int64(va), int64(align))) + } else { + va = uint64(Rnd(int64(va), int64(Funcalign))) + } + } + n++ + } + } + + ldr.SetSymValue(s, 0) + for sub := s; sub != 0; sub = ldr.SubSym(sub) { + ldr.SetSymValue(sub, ldr.SymValue(sub)+int64(va)) + } + + va += funcsize + + return sect, n, va +} + +// On Wasm, we reserve 4096 bytes for zero page, then 8192 bytes for wasm_exec.js +// to store command line args and environment variables. +// Data sections starts from at least address 12288. +// Keep in sync with wasm_exec.js. +const wasmMinDataAddr = 4096 + 8192 + +// address assigns virtual addresses to all segments and sections and +// returns all segments in file order. +func (ctxt *Link) address() []*sym.Segment { + var order []*sym.Segment // Layout order + + va := uint64(*FlagTextAddr) + order = append(order, &Segtext) + Segtext.Rwx = 05 + Segtext.Vaddr = va + for i, s := range Segtext.Sections { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + + if ctxt.IsWasm() && i == 0 && va < wasmMinDataAddr { + va = wasmMinDataAddr + } + } + + Segtext.Length = va - uint64(*FlagTextAddr) + + if len(Segrodata.Sections) > 0 { + // align to page boundary so as not to mix + // rodata and executable text. + // + // Note: gold or GNU ld will reduce the size of the executable + // file by arranging for the relro segment to end at a page + // boundary, and overlap the end of the text segment with the + // start of the relro segment in the file. The PT_LOAD segments + // will be such that the last page of the text segment will be + // mapped twice, once r-x and once starting out rw- and, after + // relocation processing, changed to r--. + // + // Ideally the last page of the text segment would not be + // writable even for this short period. + va = uint64(Rnd(int64(va), int64(*FlagRound))) + + order = append(order, &Segrodata) + Segrodata.Rwx = 04 + Segrodata.Vaddr = va + for _, s := range Segrodata.Sections { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segrodata.Length = va - Segrodata.Vaddr + } + if len(Segrelrodata.Sections) > 0 { + // align to page boundary so as not to mix + // rodata, rel-ro data, and executable text. + va = uint64(Rnd(int64(va), int64(*FlagRound))) + if ctxt.HeadType == objabi.Haix { + // Relro data are inside data segment on AIX. + va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE) + } + + order = append(order, &Segrelrodata) + Segrelrodata.Rwx = 06 + Segrelrodata.Vaddr = va + for _, s := range Segrelrodata.Sections { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segrelrodata.Length = va - Segrelrodata.Vaddr + } + + va = uint64(Rnd(int64(va), int64(*FlagRound))) + if ctxt.HeadType == objabi.Haix && len(Segrelrodata.Sections) == 0 { + // Data sections are moved to an unreachable segment + // to ensure that they are position-independent. + // Already done if relro sections exist. + va += uint64(XCOFFDATABASE) - uint64(XCOFFTEXTBASE) + } + order = append(order, &Segdata) + Segdata.Rwx = 06 + Segdata.Vaddr = va + var data *sym.Section + var noptr *sym.Section + var bss *sym.Section + var noptrbss *sym.Section + for i, s := range Segdata.Sections { + if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && s.Name == ".tbss" { + continue + } + vlen := int64(s.Length) + if i+1 < len(Segdata.Sections) && !((ctxt.IsELF || ctxt.HeadType == objabi.Haix) && Segdata.Sections[i+1].Name == ".tbss") { + vlen = int64(Segdata.Sections[i+1].Vaddr - s.Vaddr) + } + s.Vaddr = va + va += uint64(vlen) + Segdata.Length = va - Segdata.Vaddr + if s.Name == ".data" { + data = s + } + if s.Name == ".noptrdata" { + noptr = s + } + if s.Name == ".bss" { + bss = s + } + if s.Name == ".noptrbss" { + noptrbss = s + } + } + + // Assign Segdata's Filelen omitting the BSS. We do this here + // simply because right now we know where the BSS starts. + Segdata.Filelen = bss.Vaddr - Segdata.Vaddr + + va = uint64(Rnd(int64(va), int64(*FlagRound))) + order = append(order, &Segdwarf) + Segdwarf.Rwx = 06 + Segdwarf.Vaddr = va + for i, s := range Segdwarf.Sections { + vlen := int64(s.Length) + if i+1 < len(Segdwarf.Sections) { + vlen = int64(Segdwarf.Sections[i+1].Vaddr - s.Vaddr) + } + s.Vaddr = va + va += uint64(vlen) + if ctxt.HeadType == objabi.Hwindows { + va = uint64(Rnd(int64(va), PEFILEALIGN)) + } + Segdwarf.Length = va - Segdwarf.Vaddr + } + + ldr := ctxt.loader + var ( + rodata = ldr.SymSect(ldr.LookupOrCreateSym("runtime.rodata", 0)) + symtab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.symtab", 0)) + pclntab = ldr.SymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0)) + types = ldr.SymSect(ldr.LookupOrCreateSym("runtime.types", 0)) + ) + + for _, s := range ctxt.datap { + if sect := ldr.SymSect(s); sect != nil { + ldr.AddToSymValue(s, int64(sect.Vaddr)) + } + v := ldr.SymValue(s) + for sub := ldr.SubSym(s); sub != 0; sub = ldr.SubSym(sub) { + ldr.AddToSymValue(sub, v) + } + } + + for _, si := range dwarfp { + for _, s := range si.syms { + if sect := ldr.SymSect(s); sect != nil { + ldr.AddToSymValue(s, int64(sect.Vaddr)) + } + sub := ldr.SubSym(s) + if sub != 0 { + panic(fmt.Sprintf("unexpected sub-sym for %s %s", ldr.SymName(s), ldr.SymType(s).String())) + } + v := ldr.SymValue(s) + for ; sub != 0; sub = ldr.SubSym(sub) { + ldr.AddToSymValue(s, v) + } + } + } + + if ctxt.BuildMode == BuildModeShared { + s := ldr.LookupOrCreateSym("go.link.abihashbytes", 0) + sect := ldr.SymSect(ldr.LookupOrCreateSym(".note.go.abihash", 0)) + ldr.SetSymSect(s, sect) + ldr.SetSymValue(s, int64(sect.Vaddr+16)) + } + + // If there are multiple text sections, create runtime.text.n for + // their section Vaddr, using n for index + n := 1 + for _, sect := range Segtext.Sections[1:] { + if sect.Name != ".text" { + break + } + symname := fmt.Sprintf("runtime.text.%d", n) + if ctxt.HeadType != objabi.Haix || ctxt.LinkMode != LinkExternal { + // Addresses are already set on AIX with external linker + // because these symbols are part of their sections. + ctxt.xdefine(symname, sym.STEXT, int64(sect.Vaddr)) + } + n++ + } + + ctxt.xdefine("runtime.rodata", sym.SRODATA, int64(rodata.Vaddr)) + ctxt.xdefine("runtime.erodata", sym.SRODATA, int64(rodata.Vaddr+rodata.Length)) + ctxt.xdefine("runtime.types", sym.SRODATA, int64(types.Vaddr)) + ctxt.xdefine("runtime.etypes", sym.SRODATA, int64(types.Vaddr+types.Length)) + + s := ldr.Lookup("runtime.gcdata", 0) + ldr.SetAttrLocal(s, true) + ctxt.xdefine("runtime.egcdata", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s)) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcdata", 0), ldr.SymSect(s)) + + s = ldr.LookupOrCreateSym("runtime.gcbss", 0) + ldr.SetAttrLocal(s, true) + ctxt.xdefine("runtime.egcbss", sym.SRODATA, ldr.SymAddr(s)+ldr.SymSize(s)) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.egcbss", 0), ldr.SymSect(s)) + + ctxt.xdefine("runtime.symtab", sym.SRODATA, int64(symtab.Vaddr)) + ctxt.xdefine("runtime.esymtab", sym.SRODATA, int64(symtab.Vaddr+symtab.Length)) + ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr)) + ctxt.defineInternal("runtime.pcheader", sym.SRODATA) + ctxt.defineInternal("runtime.funcnametab", sym.SRODATA) + ctxt.defineInternal("runtime.cutab", sym.SRODATA) + ctxt.defineInternal("runtime.filetab", sym.SRODATA) + ctxt.defineInternal("runtime.pctab", sym.SRODATA) + ctxt.defineInternal("runtime.functab", sym.SRODATA) + ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length)) + ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr)) + ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length)) + ctxt.xdefine("runtime.bss", sym.SBSS, int64(bss.Vaddr)) + ctxt.xdefine("runtime.ebss", sym.SBSS, int64(bss.Vaddr+bss.Length)) + ctxt.xdefine("runtime.data", sym.SDATA, int64(data.Vaddr)) + ctxt.xdefine("runtime.edata", sym.SDATA, int64(data.Vaddr+data.Length)) + ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr)) + ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length)) + ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length)) + + if ctxt.IsSolaris() { + // On Solaris, in the runtime it sets the external names of the + // end symbols. Unset them and define separate symbols, so we + // keep both. + etext := ldr.Lookup("runtime.etext", 0) + edata := ldr.Lookup("runtime.edata", 0) + end := ldr.Lookup("runtime.end", 0) + ldr.SetSymExtname(etext, "runtime.etext") + ldr.SetSymExtname(edata, "runtime.edata") + ldr.SetSymExtname(end, "runtime.end") + ctxt.xdefine("_etext", ldr.SymType(etext), ldr.SymValue(etext)) + ctxt.xdefine("_edata", ldr.SymType(edata), ldr.SymValue(edata)) + ctxt.xdefine("_end", ldr.SymType(end), ldr.SymValue(end)) + ldr.SetSymSect(ldr.Lookup("_etext", 0), ldr.SymSect(etext)) + ldr.SetSymSect(ldr.Lookup("_edata", 0), ldr.SymSect(edata)) + ldr.SetSymSect(ldr.Lookup("_end", 0), ldr.SymSect(end)) + } + + return order +} + +// layout assigns file offsets and lengths to the segments in order. +// Returns the file size containing all the segments. +func (ctxt *Link) layout(order []*sym.Segment) uint64 { + var prev *sym.Segment + for _, seg := range order { + if prev == nil { + seg.Fileoff = uint64(HEADR) + } else { + switch ctxt.HeadType { + default: + // Assuming the previous segment was + // aligned, the following rounding + // should ensure that this segment's + // VA ≡ Fileoff mod FlagRound. + seg.Fileoff = uint64(Rnd(int64(prev.Fileoff+prev.Filelen), int64(*FlagRound))) + if seg.Vaddr%uint64(*FlagRound) != seg.Fileoff%uint64(*FlagRound) { + Exitf("bad segment rounding (Vaddr=%#x Fileoff=%#x FlagRound=%#x)", seg.Vaddr, seg.Fileoff, *FlagRound) + } + case objabi.Hwindows: + seg.Fileoff = prev.Fileoff + uint64(Rnd(int64(prev.Filelen), PEFILEALIGN)) + case objabi.Hplan9: + seg.Fileoff = prev.Fileoff + prev.Filelen + } + } + if seg != &Segdata { + // Link.address already set Segdata.Filelen to + // account for BSS. + seg.Filelen = seg.Length + } + prev = seg + } + return prev.Fileoff + prev.Filelen +} + +// add a trampoline with symbol s (to be laid down after the current function) +func (ctxt *Link) AddTramp(s *loader.SymbolBuilder) { + s.SetType(sym.STEXT) + s.SetReachable(true) + s.SetOnList(true) + ctxt.tramps = append(ctxt.tramps, s.Sym()) + if *FlagDebugTramp > 0 && ctxt.Debugvlog > 0 { + ctxt.Logf("trampoline %s inserted\n", s.Name()) + } +} + +// compressSyms compresses syms and returns the contents of the +// compressed section. If the section would get larger, it returns nil. +func compressSyms(ctxt *Link, syms []loader.Sym) []byte { + ldr := ctxt.loader + var total int64 + for _, sym := range syms { + total += ldr.SymSize(sym) + } + + var buf bytes.Buffer + buf.Write([]byte("ZLIB")) + var sizeBytes [8]byte + binary.BigEndian.PutUint64(sizeBytes[:], uint64(total)) + buf.Write(sizeBytes[:]) + + var relocbuf []byte // temporary buffer for applying relocations + + // Using zlib.BestSpeed achieves very nearly the same + // compression levels of zlib.DefaultCompression, but takes + // substantially less time. This is important because DWARF + // compression can be a significant fraction of link time. + z, err := zlib.NewWriterLevel(&buf, zlib.BestSpeed) + if err != nil { + log.Fatalf("NewWriterLevel failed: %s", err) + } + st := ctxt.makeRelocSymState() + for _, s := range syms { + // Symbol data may be read-only. Apply relocations in a + // temporary buffer, and immediately write it out. + P := ldr.Data(s) + relocs := ldr.Relocs(s) + if relocs.Count() != 0 { + relocbuf = append(relocbuf[:0], P...) + P = relocbuf + st.relocsym(s, P) + } + if _, err := z.Write(P); err != nil { + log.Fatalf("compression failed: %s", err) + } + for i := ldr.SymSize(s) - int64(len(P)); i > 0; { + b := zeros[:] + if i < int64(len(b)) { + b = b[:i] + } + n, err := z.Write(b) + if err != nil { + log.Fatalf("compression failed: %s", err) + } + i -= int64(n) + } + } + if err := z.Close(); err != nil { + log.Fatalf("compression failed: %s", err) + } + if int64(buf.Len()) >= total { + // Compression didn't save any space. + return nil + } + return buf.Bytes() +} diff --git a/src/cmd/link/internal/ld/data_test.go b/src/cmd/link/internal/ld/data_test.go new file mode 100644 index 0000000..7c46307 --- /dev/null +++ b/src/cmd/link/internal/ld/data_test.go @@ -0,0 +1,92 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "testing" +) + +func setUpContext(arch *sys.Arch, iself bool, ht objabi.HeadType, bm, lm string) *Link { + ctxt := linknew(arch) + edummy := func(str string, off int) {} + ctxt.HeadType = ht + er := loader.ErrorReporter{} + ctxt.loader = loader.NewLoader(0, edummy, &er) + ctxt.BuildMode.Set(bm) + ctxt.LinkMode.Set(lm) + ctxt.IsELF = iself + ctxt.mustSetHeadType() + ctxt.setArchSyms() + return ctxt +} + +// Make sure the addgotsym properly increases the symbols. +func TestAddGotSym(t *testing.T) { + tests := []struct { + arch *sys.Arch + ht objabi.HeadType + bm, lm string + rel string + relsize int + gotsize int + }{ + { + arch: sys.Arch386, + ht: objabi.Hlinux, + bm: "pie", + lm: "internal", + rel: ".rel", + relsize: 2 * sys.Arch386.PtrSize, + gotsize: sys.Arch386.PtrSize, + }, + { + arch: sys.ArchAMD64, + ht: objabi.Hlinux, + bm: "pie", + lm: "internal", + rel: ".rela", + relsize: 3 * sys.ArchAMD64.PtrSize, + gotsize: sys.ArchAMD64.PtrSize, + }, + { + arch: sys.ArchAMD64, + ht: objabi.Hdarwin, + bm: "pie", + lm: "external", + gotsize: sys.ArchAMD64.PtrSize, + }, + } + + // Save the architecture as we're going to set it on each test run. + origArch := objabi.GOARCH + defer func() { + objabi.GOARCH = origArch + }() + + for i, test := range tests { + iself := len(test.rel) != 0 + objabi.GOARCH = test.arch.Name + ctxt := setUpContext(test.arch, iself, test.ht, test.bm, test.lm) + foo := ctxt.loader.CreateSymForUpdate("foo", 0) + ctxt.loader.CreateExtSym("bar", 0) + AddGotSym(&ctxt.Target, ctxt.loader, &ctxt.ArchSyms, foo.Sym(), 0) + + if iself { + rel := ctxt.loader.Lookup(test.rel, 0) + if rel == 0 { + t.Fatalf("[%d] could not find symbol: %q", i, test.rel) + } + if s := ctxt.loader.SymSize(rel); s != int64(test.relsize) { + t.Fatalf("[%d] expected ldr.Size(%q) == %v, got %v", i, test.rel, test.relsize, s) + } + } + if s := ctxt.loader.SymSize(ctxt.loader.Lookup(".got", 0)); s != int64(test.gotsize) { + t.Fatalf(`[%d] expected ldr.Size(".got") == %v, got %v`, i, test.gotsize, s) + } + } +} diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go new file mode 100644 index 0000000..bfa8640 --- /dev/null +++ b/src/cmd/link/internal/ld/deadcode.go @@ -0,0 +1,444 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "unicode" +) + +var _ = fmt.Print + +type deadcodePass struct { + ctxt *Link + ldr *loader.Loader + wq heap // work queue, using min-heap for beter locality + + ifaceMethod map[methodsig]bool // methods declared in reached interfaces + markableMethods []methodref // methods of reached types + reflectSeen bool // whether we have seen a reflect method call + dynlink bool + + methodsigstmp []methodsig // scratch buffer for decoding method signatures +} + +func (d *deadcodePass) init() { + d.ldr.InitReachable() + d.ifaceMethod = make(map[methodsig]bool) + if objabi.Fieldtrack_enabled != 0 { + d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym()) + } + d.dynlink = d.ctxt.DynlinkingGo() + + if d.ctxt.BuildMode == BuildModeShared { + // Mark all symbols defined in this library as reachable when + // building a shared library. + n := d.ldr.NDef() + for i := 1; i < n; i++ { + s := loader.Sym(i) + d.mark(s, 0) + } + return + } + + var names []string + + // In a normal binary, start at main.main and the init + // functions and mark what is reachable from there. + if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + names = append(names, "main.main", "main..inittask") + } else { + // The external linker refers main symbol directly. + if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 { + *flagEntrySymbol = "_main" + } else { + *flagEntrySymbol = "main" + } + } + names = append(names, *flagEntrySymbol) + 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") + + // We don't keep the go.plugin.exports symbol, + // but we do keep the symbols it refers to. + exportsIdx := d.ldr.Lookup("go.plugin.exports", 0) + if exportsIdx != 0 { + relocs := d.ldr.Relocs(exportsIdx) + for i := 0; i < relocs.Count(); i++ { + d.mark(relocs.At(i).Sym(), 0) + } + } + } + } + + dynexpMap := d.ctxt.cgo_export_dynamic + if d.ctxt.LinkMode == LinkExternal { + dynexpMap = d.ctxt.cgo_export_static + } + for exp := range dynexpMap { + names = append(names, exp) + } + + for _, name := range names { + // Mark symbol as a data/ABI0 symbol. + d.mark(d.ldr.Lookup(name, 0), 0) + // Also mark any Go functions (internal ABI). + d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal), 0) + } +} + +func (d *deadcodePass) flood() { + var methods []methodref + for !d.wq.empty() { + symIdx := d.wq.pop() + + d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx) + + isgotype := d.ldr.IsGoType(symIdx) + relocs := d.ldr.Relocs(symIdx) + var usedInIface bool + + if isgotype { + if d.dynlink { + // When dynaamic linking, a type may be passed across DSO + // boundary and get converted to interface at the other side. + d.ldr.SetAttrUsedInIface(symIdx, true) + } + usedInIface = d.ldr.AttrUsedInIface(symIdx) + } + + methods = methods[:0] + for i := 0; i < relocs.Count(); i++ { + r := relocs.At(i) + t := r.Type() + switch t { + case objabi.R_WEAKADDROFF: + continue + case objabi.R_METHODOFF: + if i+2 >= relocs.Count() { + panic("expect three consecutive R_METHODOFF relocs") + } + if usedInIface { + methods = append(methods, methodref{src: symIdx, r: i}) + // The method descriptor is itself a type descriptor, and + // it can be used to reach other types, e.g. by using + // reflect.Type.Method(i).Type.In(j). We need to traverse + // its child types with UsedInIface set. (See also the + // comment below.) + rs := r.Sym() + if !d.ldr.AttrUsedInIface(rs) { + d.ldr.SetAttrUsedInIface(rs, true) + if d.ldr.AttrReachable(rs) { + d.ldr.SetAttrReachable(rs, false) + d.mark(rs, symIdx) + } + } + } + i += 2 + continue + case objabi.R_USETYPE: + // type symbol used for DWARF. we need to load the symbol but it may not + // be otherwise reachable in the program. + // do nothing for now as we still load all type symbols. + continue + case objabi.R_USEIFACE: + // R_USEIFACE is a marker relocation that tells the linker the type is + // converted to an interface, i.e. should have UsedInIface set. See the + // comment below for why we need to unset the Reachable bit and re-mark it. + rs := r.Sym() + if !d.ldr.AttrUsedInIface(rs) { + d.ldr.SetAttrUsedInIface(rs, true) + if d.ldr.AttrReachable(rs) { + d.ldr.SetAttrReachable(rs, false) + d.mark(rs, symIdx) + } + } + continue + case objabi.R_USEIFACEMETHOD: + // R_USEIFACEMETHOD is a marker relocation that marks an interface + // method as used. + rs := r.Sym() + if d.ctxt.linkShared && (d.ldr.SymType(rs) == sym.SDYNIMPORT || d.ldr.SymType(rs) == sym.Sxxx) { + // Don't decode symbol from shared library (we'll mark all exported methods anyway). + // We check for both SDYNIMPORT and Sxxx because name-mangled symbols haven't + // been resolved at this point. + continue + } + m := d.decodeIfaceMethod(d.ldr, d.ctxt.Arch, rs, r.Add()) + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("reached iface method: %v\n", m) + } + d.ifaceMethod[m] = true + continue + } + rs := r.Sym() + if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) { + // If a type is converted to an interface, it is possible to obtain an + // interface with a "child" type of it using reflection (e.g. obtain an + // interface of T from []chan T). We need to traverse its "child" types + // with UsedInIface attribute set. + // When visiting the child type (chan T in the example above), it will + // have UsedInIface set, so it in turn will mark and (re)visit its children + // (e.g. T above). + // We unset the reachable bit here, so if the child type is already visited, + // it will be visited again. + // Note that a type symbol can be visited at most twice, one without + // UsedInIface and one with. So termination is still guaranteed. + d.ldr.SetAttrUsedInIface(rs, true) + d.ldr.SetAttrReachable(rs, false) + } + d.mark(rs, symIdx) + } + naux := d.ldr.NAux(symIdx) + for i := 0; i < naux; i++ { + a := d.ldr.Aux(symIdx, i) + if a.Type() == goobj.AuxGotype { + // A symbol being reachable doesn't imply we need its + // type descriptor. Don't mark it. + continue + } + d.mark(a.Sym(), symIdx) + } + // Some host object symbols have an outer object, which acts like a + // "carrier" symbol, or it holds all the symbols for a particular + // section. We need to mark all "referenced" symbols from that carrier, + // so we make sure we're pulling in all outer symbols, and their sub + // symbols. This is not ideal, and these carrier/section symbols could + // be removed. + if d.ldr.IsExternal(symIdx) { + d.mark(d.ldr.OuterSym(symIdx), symIdx) + d.mark(d.ldr.SubSym(symIdx), symIdx) + } + + if len(methods) != 0 { + if !isgotype { + panic("method found on non-type symbol") + } + // Decode runtime type information for type methods + // to help work out which methods can be called + // dynamically via interfaces. + methodsigs := d.decodetypeMethods(d.ldr, d.ctxt.Arch, symIdx, &relocs) + if len(methods) != len(methodsigs) { + panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs))) + } + for i, m := range methodsigs { + methods[i].m = m + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("markable method: %v of sym %v %s\n", m, symIdx, d.ldr.SymName(symIdx)) + } + } + d.markableMethods = append(d.markableMethods, methods...) + } + } +} + +func (d *deadcodePass) mark(symIdx, parent loader.Sym) { + if symIdx != 0 && !d.ldr.AttrReachable(symIdx) { + d.wq.push(symIdx) + d.ldr.SetAttrReachable(symIdx, true) + if 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) + } + } + } +} + +func (d *deadcodePass) markMethod(m methodref) { + relocs := d.ldr.Relocs(m.src) + d.mark(relocs.At(m.r).Sym(), m.src) + d.mark(relocs.At(m.r+1).Sym(), m.src) + d.mark(relocs.At(m.r+2).Sym(), m.src) +} + +// deadcode marks all reachable symbols. +// +// The basis of the dead code elimination is a flood fill of symbols, +// following their relocations, beginning at *flagEntrySymbol. +// +// This flood fill is wrapped in logic for pruning unused methods. +// All methods are mentioned by relocations on their receiver's *rtype. +// These relocations are specially defined as R_METHODOFF by the compiler +// so we can detect and manipulated them here. +// +// There are three ways a method of a reachable type can be invoked: +// +// 1. direct call +// 2. through a reachable interface type +// 3. reflect.Value.Method (or MethodByName), or reflect.Type.Method +// (or MethodByName) +// +// The first case is handled by the flood fill, a directly called method +// is marked as reachable. +// +// The second case is handled by decomposing all reachable interface +// types into method signatures. Each encountered method is compared +// against the interface method signatures, if it matches it is marked +// as reachable. This is extremely conservative, but easy and correct. +// +// The third case is handled by looking to see if any of: +// - reflect.Value.Method or MethodByName is reachable +// - reflect.Type.Method or MethodByName is called (through the +// REFLECTMETHOD attribute marked by the compiler). +// If any of these happen, all bets are off and all exported methods +// of reachable types are marked reachable. +// +// Any unreached text symbols are removed from ctxt.Textp. +func deadcode(ctxt *Link) { + ldr := ctxt.loader + d := deadcodePass{ctxt: ctxt, ldr: ldr} + d.init() + d.flood() + + methSym := ldr.Lookup("reflect.Value.Method", sym.SymVerABIInternal) + methByNameSym := ldr.Lookup("reflect.Value.MethodByName", sym.SymVerABIInternal) + + if ctxt.DynlinkingGo() { + // Exported methods may satisfy interfaces we don't know + // about yet when dynamically linking. + d.reflectSeen = true + } + + for { + // Methods might be called via reflection. Give up on + // static analysis, mark all exported methods of + // all reachable types as reachable. + d.reflectSeen = d.reflectSeen || (methSym != 0 && ldr.AttrReachable(methSym)) || (methByNameSym != 0 && ldr.AttrReachable(methByNameSym)) + + // Mark all methods that could satisfy a discovered + // interface as reachable. We recheck old marked interfaces + // as new types (with new methods) may have been discovered + // in the last pass. + rem := d.markableMethods[:0] + for _, m := range d.markableMethods { + if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] { + d.markMethod(m) + } else { + rem = append(rem, m) + } + } + d.markableMethods = rem + + if d.wq.empty() { + // No new work was discovered. Done. + break + } + d.flood() + } +} + +// methodsig is a typed method signature (name + type). +type methodsig struct { + name string + typ loader.Sym // type descriptor symbol of the function +} + +// methodref holds the relocations from a receiver type symbol to its +// method. There are three relocations, one for each of the fields in +// the reflect.method struct: mtyp, ifn, and tfn. +type methodref struct { + m methodsig + src loader.Sym // receiver type symbol + r int // the index of R_METHODOFF relocations +} + +func (m methodref) isExported() bool { + for _, r := range m.m.name { + return unicode.IsUpper(r) + } + panic("methodref has no signature") +} + +// decodeMethodSig decodes an array of method signature information. +// Each element of the array is size bytes. The first 4 bytes is a +// nameOff for the method name, and the next 4 bytes is a typeOff for +// the function type. +// +// Conveniently this is the layout of both runtime.method and runtime.imethod. +func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig { + if cap(d.methodsigstmp) < count { + d.methodsigstmp = append(d.methodsigstmp[:0], make([]methodsig, count)...) + } + var methods = d.methodsigstmp[:count] + for i := 0; i < count; i++ { + methods[i].name = decodetypeName(ldr, symIdx, relocs, off) + methods[i].typ = decodeRelocSym(ldr, symIdx, relocs, int32(off+4)) + off += size + } + return methods +} + +// Decode the method of interface type symbol symIdx at offset off. +func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off int64) methodsig { + p := ldr.Data(symIdx) + if decodetypeKind(arch, p)&kindMask != kindInterface { + panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx))) + } + relocs := ldr.Relocs(symIdx) + var m methodsig + m.name = decodetypeName(ldr, symIdx, &relocs, int(off)) + m.typ = decodeRelocSym(ldr, symIdx, &relocs, int32(off+4)) + return m +} + +func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { + p := ldr.Data(symIdx) + if !decodetypeHasUncommon(arch, p) { + panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx))) + } + off := commonsize(arch) // reflect.rtype + switch decodetypeKind(arch, p) & kindMask { + case kindStruct: // reflect.structType + off += 4 * arch.PtrSize + case kindPtr: // reflect.ptrType + off += arch.PtrSize + case kindFunc: // reflect.funcType + off += arch.PtrSize // 4 bytes, pointer aligned + case kindSlice: // reflect.sliceType + off += arch.PtrSize + case kindArray: // reflect.arrayType + off += 3 * arch.PtrSize + case kindChan: // reflect.chanType + off += 2 * arch.PtrSize + case kindMap: // reflect.mapType + off += 4*arch.PtrSize + 8 + case kindInterface: // reflect.interfaceType + off += 3 * arch.PtrSize + default: + // just Sizeof(rtype) + } + + mcount := int(decodeInuxi(arch, p[off+4:], 2)) + moff := int(decodeInuxi(arch, p[off+4+2+2:], 4)) + off += moff // offset to array of reflect.method values + const sizeofMethod = 4 * 4 // sizeof reflect.method in program + return d.decodeMethodSig(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount) +} diff --git a/src/cmd/link/internal/ld/deadcode_test.go b/src/cmd/link/internal/ld/deadcode_test.go new file mode 100644 index 0000000..b756091 --- /dev/null +++ b/src/cmd/link/internal/ld/deadcode_test.go @@ -0,0 +1,57 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "testing" +) + +func TestDeadcode(t *testing.T) { + testenv.MustHaveGoBuild(t) + t.Parallel() + + tmpdir, err := ioutil.TempDir("", "TestDeadcode") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + tests := []struct { + src string + pos, neg string // positive and negative patterns + }{ + {"reflectcall", "", "main.T.M"}, + {"typedesc", "", "type.main.T"}, + {"ifacemethod", "", "main.T.M"}, + {"ifacemethod2", "main.T.M", ""}, + {"ifacemethod3", "main.S.M", ""}, + {"ifacemethod4", "", "main.T.M"}, + } + for _, test := range tests { + test := test + t.Run(test.src, func(t *testing.T) { + t.Parallel() + src := filepath.Join("testdata", "deadcode", test.src+".go") + exe := filepath.Join(tmpdir, test.src+".exe") + cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-dumpdep", "-o", exe, src) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%v: %v:\n%s", cmd.Args, err, out) + } + if test.pos != "" && !bytes.Contains(out, []byte(test.pos)) { + t.Errorf("%s should be reachable. Output:\n%s", test.pos, out) + } + if test.neg != "" && bytes.Contains(out, []byte(test.neg)) { + t.Errorf("%s should not be reachable. Output:\n%s", test.neg, out) + } + }) + } +} diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go new file mode 100644 index 0000000..fc179fc --- /dev/null +++ b/src/cmd/link/internal/ld/decodesym.go @@ -0,0 +1,293 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "log" +) + +// Decoding the type.* symbols. This has to be in sync with +// ../../runtime/type.go, or more specifically, with what +// cmd/compile/internal/gc/reflect.go stuffs in these. + +// tflag is documented in reflect/type.go. +// +// tflag values must be kept in sync with copies in: +// cmd/compile/internal/gc/reflect.go +// cmd/link/internal/ld/decodesym.go +// reflect/type.go +// runtime/type.go +const ( + tflagUncommon = 1 << 0 + tflagExtraStar = 1 << 1 +) + +func decodeInuxi(arch *sys.Arch, p []byte, sz int) uint64 { + switch sz { + case 2: + return uint64(arch.ByteOrder.Uint16(p)) + case 4: + return uint64(arch.ByteOrder.Uint32(p)) + case 8: + return arch.ByteOrder.Uint64(p) + default: + Exitf("dwarf: decode inuxi %d", sz) + panic("unreachable") + } +} + +func commonsize(arch *sys.Arch) int { return 4*arch.PtrSize + 8 + 8 } // runtime._type +func structfieldSize(arch *sys.Arch) int { return 3 * arch.PtrSize } // runtime.structfield +func uncommonSize() int { return 4 + 2 + 2 + 4 + 4 } // runtime.uncommontype + +// Type.commonType.kind +func decodetypeKind(arch *sys.Arch, p []byte) uint8 { + return p[2*arch.PtrSize+7] & objabi.KindMask // 0x13 / 0x1f +} + +// Type.commonType.kind +func decodetypeUsegcprog(arch *sys.Arch, p []byte) uint8 { + return p[2*arch.PtrSize+7] & objabi.KindGCProg // 0x13 / 0x1f +} + +// Type.commonType.size +func decodetypeSize(arch *sys.Arch, p []byte) int64 { + return int64(decodeInuxi(arch, p, arch.PtrSize)) // 0x8 / 0x10 +} + +// Type.commonType.ptrdata +func decodetypePtrdata(arch *sys.Arch, p []byte) int64 { + return int64(decodeInuxi(arch, p[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10 +} + +// Type.commonType.tflag +func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool { + return p[2*arch.PtrSize+4]&tflagUncommon != 0 +} + +// Type.FuncType.dotdotdot +func decodetypeFuncDotdotdot(arch *sys.Arch, p []byte) bool { + return uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2))&(1<<15) != 0 +} + +// Type.FuncType.inCount +func decodetypeFuncInCount(arch *sys.Arch, p []byte) int { + return int(decodeInuxi(arch, p[commonsize(arch):], 2)) +} + +func decodetypeFuncOutCount(arch *sys.Arch, p []byte) int { + return int(uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2)) & (1<<15 - 1)) +} + +// InterfaceType.methods.length +func decodetypeIfaceMethodCount(arch *sys.Arch, p []byte) int64 { + return int64(decodeInuxi(arch, p[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +// Matches runtime/typekind.go and reflect.Kind. +const ( + kindArray = 17 + kindChan = 18 + kindFunc = 19 + kindInterface = 20 + kindMap = 21 + kindPtr = 22 + kindSlice = 23 + kindStruct = 25 + kindMask = (1 << 5) - 1 +) + +func decodeReloc(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Reloc { + for j := 0; j < relocs.Count(); j++ { + rel := relocs.At(j) + if rel.Off() == off { + return rel + } + } + return loader.Reloc{} +} + +func decodeRelocSym(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Sym { + return decodeReloc(ldr, symIdx, relocs, off).Sym() +} + +// decodetypeName decodes the name from a reflect.name. +func decodetypeName(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) string { + r := decodeRelocSym(ldr, symIdx, relocs, int32(off)) + if r == 0 { + return "" + } + + data := ldr.Data(r) + namelen := int(uint16(data[1])<<8 | uint16(data[2])) + return string(data[3 : 3+namelen]) +} + +func decodetypeFuncInType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { + uadd := commonsize(arch) + 4 + if arch.PtrSize == 8 { + uadd += 4 + } + if decodetypeHasUncommon(arch, ldr.Data(symIdx)) { + uadd += uncommonSize() + } + return decodeRelocSym(ldr, symIdx, relocs, int32(uadd+i*arch.PtrSize)) +} + +func decodetypeFuncOutType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { + return decodetypeFuncInType(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx))) +} + +func decodetypeArrayElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeArrayLen(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int64 { + data := ldr.Data(symIdx) + return int64(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +func decodetypeChanElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeMapKey(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeMapValue(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38 +} + +func decodetypePtrElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30 +} + +func decodetypeStructFieldCount(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int { + data := ldr.Data(symIdx) + return int(decodeInuxi(arch, data[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize)) +} + +func decodetypeStructFieldArrayOff(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int { + data := ldr.Data(symIdx) + off := commonsize(arch) + 4*arch.PtrSize + if decodetypeHasUncommon(arch, data) { + off += uncommonSize() + } + off += i * structfieldSize(arch) + return off +} + +func decodetypeStructFieldName(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) string { + off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) + relocs := ldr.Relocs(symIdx) + return decodetypeName(ldr, symIdx, &relocs, off) +} + +func decodetypeStructFieldType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym { + off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) + relocs := ldr.Relocs(symIdx) + return decodeRelocSym(ldr, symIdx, &relocs, int32(off+arch.PtrSize)) +} + +func decodetypeStructFieldOffsAnon(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 { + off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) + data := ldr.Data(symIdx) + return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize)) +} + +// decodetypeStr returns the contents of an rtype's str field (a nameOff). +func decodetypeStr(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) string { + relocs := ldr.Relocs(symIdx) + str := decodetypeName(ldr, symIdx, &relocs, 4*arch.PtrSize+8) + data := ldr.Data(symIdx) + if data[2*arch.PtrSize+4]&tflagExtraStar != 0 { + return str[1:] + } + return str +} + +func decodetypeGcmask(ctxt *Link, s loader.Sym) []byte { + if ctxt.loader.SymType(s) == sym.SDYNIMPORT { + symData := ctxt.loader.Data(s) + addr := decodetypeGcprogShlib(ctxt, symData) + ptrdata := decodetypePtrdata(ctxt.Arch, symData) + sect := findShlibSection(ctxt, ctxt.loader.SymPkg(s), addr) + if sect != nil { + bits := ptrdata / int64(ctxt.Arch.PtrSize) + r := make([]byte, (bits+7)/8) + // ldshlibsyms avoids closing the ELF file so sect.ReadAt works. + // If we remove this read (and the ones in decodetypeGcprog), we + // can close the file. + _, err := sect.ReadAt(r, int64(addr-sect.Addr)) + if err != nil { + log.Fatal(err) + } + return r + } + Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s)) + return nil + } + relocs := ctxt.loader.Relocs(s) + mask := decodeRelocSym(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize)) + return ctxt.loader.Data(mask) +} + +// Type.commonType.gc +func decodetypeGcprog(ctxt *Link, s loader.Sym) []byte { + if ctxt.loader.SymType(s) == sym.SDYNIMPORT { + symData := ctxt.loader.Data(s) + addr := decodetypeGcprogShlib(ctxt, symData) + sect := findShlibSection(ctxt, ctxt.loader.SymPkg(s), addr) + if sect != nil { + // A gcprog is a 4-byte uint32 indicating length, followed by + // the actual program. + progsize := make([]byte, 4) + _, err := sect.ReadAt(progsize, int64(addr-sect.Addr)) + if err != nil { + log.Fatal(err) + } + progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize)) + _, err = sect.ReadAt(progbytes, int64(addr-sect.Addr+4)) + if err != nil { + log.Fatal(err) + } + return append(progsize, progbytes...) + } + Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s)) + return nil + } + relocs := ctxt.loader.Relocs(s) + rs := decodeRelocSym(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize)) + return ctxt.loader.Data(rs) +} + +// Find the elf.Section of a given shared library that contains a given address. +func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section { + for _, shlib := range ctxt.Shlibs { + if shlib.Path == path { + for _, sect := range shlib.File.Sections[1:] { // skip the NULL section + if sect.Addr <= addr && addr < sect.Addr+sect.Size { + return sect + } + } + } + } + return nil +} + +func decodetypeGcprogShlib(ctxt *Link, data []byte) uint64 { + return decodeInuxi(ctxt.Arch, data[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize) +} diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go new file mode 100644 index 0000000..2ab9a55 --- /dev/null +++ b/src/cmd/link/internal/ld/dwarf.go @@ -0,0 +1,2315 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign types to their packages +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type + +package ld + +import ( + "cmd/internal/dwarf" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/src" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "log" + "path" + "runtime" + "sort" + "strings" + "sync" +) + +// dwctxt is a wrapper intended to satisfy the method set of +// dwarf.Context, so that functions like dwarf.PutAttrs will work with +// DIEs that use loader.Sym as opposed to *sym.Symbol. It is also +// being used as a place to store tables/maps that are useful as part +// of type conversion (this is just a convenience; it would be easy to +// split these things out into another type if need be). +type dwctxt struct { + linkctxt *Link + ldr *loader.Loader + arch *sys.Arch + + // This maps type name string (e.g. "uintptr") to loader symbol for + // the DWARF DIE for that type (e.g. "go.info.type.uintptr") + tmap map[string]loader.Sym + + // This maps loader symbol for the DWARF DIE symbol generated for + // a type (e.g. "go.info.uintptr") to the type symbol itself + // ("type.uintptr"). + // FIXME: try converting this map (and the next one) to a single + // array indexed by loader.Sym -- this may perform better. + rtmap map[loader.Sym]loader.Sym + + // This maps Go type symbol (e.g. "type.XXX") to loader symbol for + // the typedef DIE for that type (e.g. "go.info.XXX..def") + tdmap map[loader.Sym]loader.Sym + + // Cache these type symbols, so as to avoid repeatedly looking them up + typeRuntimeEface loader.Sym + typeRuntimeIface loader.Sym + uintptrInfoSym loader.Sym + + // Used at various points in that parallel portion of DWARF gen to + // protect against conflicting updates to globals (such as "gdbscript") + dwmu *sync.Mutex +} + +func newdwctxt(linkctxt *Link, forTypeGen bool) dwctxt { + d := dwctxt{ + linkctxt: linkctxt, + ldr: linkctxt.loader, + arch: linkctxt.Arch, + tmap: make(map[string]loader.Sym), + tdmap: make(map[loader.Sym]loader.Sym), + rtmap: make(map[loader.Sym]loader.Sym), + } + d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface") + d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface") + return d +} + +// dwSym wraps a loader.Sym; this type is meant to obey the interface +// rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and +// DwAttr objects contain references to symbols via this type. +type dwSym loader.Sym + +func (s dwSym) Length(dwarfContext interface{}) int64 { + l := dwarfContext.(dwctxt).ldr + return int64(len(l.Data(loader.Sym(s)))) +} + +func (c dwctxt) PtrSize() int { + return c.arch.PtrSize +} + +func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + dsu.AddUintXX(c.arch, uint64(i), size) +} + +func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + dsu.AddBytes(b) +} + +func (c dwctxt) AddString(s dwarf.Sym, v string) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + dsu.Addstring(v) +} + +func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + if value != 0 { + value -= dsu.Value() + } + tgtds := loader.Sym(data.(dwSym)) + dsu.AddAddrPlus(c.arch, tgtds, value) +} + +func (c dwctxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + if value != 0 { + value -= dsu.Value() + } + tgtds := loader.Sym(data.(dwSym)) + dsu.AddCURelativeAddrPlus(c.arch, tgtds, value) +} + +func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + tds := loader.Sym(t.(dwSym)) + switch size { + default: + c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size) + case c.arch.PtrSize, 4: + } + dsu.AddSymRef(c.arch, tds, ofs, objabi.R_ADDROFF, size) +} + +func (c dwctxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { + size := 4 + if isDwarf64(c.linkctxt) { + size = 8 + } + ds := loader.Sym(s.(dwSym)) + dsu := c.ldr.MakeSymbolUpdater(ds) + tds := loader.Sym(t.(dwSym)) + switch size { + default: + c.linkctxt.Errorf(ds, "invalid size %d in adddwarfref\n", size) + case c.arch.PtrSize, 4: + } + dsu.AddSymRef(c.arch, tds, ofs, objabi.R_DWARFSECREF, size) +} + +func (c dwctxt) Logf(format string, args ...interface{}) { + c.linkctxt.Logf(format, args...) +} + +// At the moment these interfaces are only used in the compiler. + +func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) { + panic("should be used only in the compiler") +} + +func (c dwctxt) CurrentOffset(s dwarf.Sym) int64 { + panic("should be used only in the compiler") +} + +func (c dwctxt) RecordDclReference(s dwarf.Sym, t dwarf.Sym, dclIdx int, inlIndex int) { + panic("should be used only in the compiler") +} + +func (c dwctxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { + panic("should be used only in the compiler") +} + +func isDwarf64(ctxt *Link) bool { + return ctxt.HeadType == objabi.Haix +} + +var gdbscript string + +// dwarfSecInfo holds information about a DWARF output section, +// specifically a section symbol and a list of symbols contained in +// that section. On the syms list, the first symbol will always be the +// section symbol, then any remaining symbols (if any) will be +// sub-symbols in that section. Note that for some sections (eg: +// .debug_abbrev), the section symbol is all there is (all content is +// contained in it). For other sections (eg: .debug_info), the section +// symbol is empty and all the content is in the sub-symbols. Finally +// there are some sections (eg: .debug_ranges) where it is a mix (both +// the section symbol and the sub-symbols have content) +type dwarfSecInfo struct { + syms []loader.Sym +} + +// secSym returns the section symbol for the section. +func (dsi *dwarfSecInfo) secSym() loader.Sym { + if len(dsi.syms) == 0 { + return 0 + } + return dsi.syms[0] +} + +// subSyms returns a list of sub-symbols for the section. +func (dsi *dwarfSecInfo) subSyms() []loader.Sym { + if len(dsi.syms) == 0 { + return []loader.Sym{} + } + return dsi.syms[1:] +} + +// dwarfp stores the collected DWARF symbols created during +// dwarf generation. +var dwarfp []dwarfSecInfo + +func (d *dwctxt) writeabbrev() dwarfSecInfo { + abrvs := d.ldr.CreateSymForUpdate(".debug_abbrev", 0) + abrvs.SetType(sym.SDWARFSECT) + abrvs.AddBytes(dwarf.GetAbbrev()) + return dwarfSecInfo{syms: []loader.Sym{abrvs.Sym()}} +} + +var dwtypes dwarf.DWDie + +// newattr attaches a new attribute to the specified DIE. +// +// FIXME: at the moment attributes are stored in a linked list in a +// fairly space-inefficient way -- it might be better to instead look +// up all attrs in a single large table, then store indices into the +// table in the DIE. This would allow us to common up storage for +// attributes that are shared by many DIEs (ex: byte size of N). +func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr { + a := new(dwarf.DWAttr) + a.Link = die.Attr + die.Attr = a + a.Atr = attr + a.Cls = uint8(cls) + a.Value = value + a.Data = data + return a +} + +// Each DIE (except the root ones) has at least 1 attribute: its +// name. getattr moves the desired one to the front so +// frequently searched ones are found faster. +func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr { + if die.Attr.Atr == attr { + return die.Attr + } + + a := die.Attr + b := a.Link + for b != nil { + if b.Atr == attr { + a.Link = b.Link + b.Link = die.Attr + die.Attr = b + return b + } + + a = b + b = b.Link + } + + return nil +} + +// Every DIE manufactured by the linker has at least an AT_name +// attribute (but it will only be written out if it is listed in the abbrev). +// The compiler does create nameless DWARF DIEs (ex: concrete subprogram +// instance). +// FIXME: it would be more efficient to bulk-allocate DIEs. +func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie { + die := new(dwarf.DWDie) + die.Abbrev = abbrev + die.Link = parent.Child + parent.Child = die + + newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name) + + // Sanity check: all DIEs created in the linker should have a non-empty + // name and be version zero. + if name == "" || version != 0 { + panic("nameless or version non-zero DWARF DIE") + } + + var st sym.SymKind + switch abbrev { + case dwarf.DW_ABRV_FUNCTYPEPARAM, dwarf.DW_ABRV_DOTDOTDOT, dwarf.DW_ABRV_STRUCTFIELD, dwarf.DW_ABRV_ARRAYRANGE: + // There are no relocations against these dies, and their names + // are not unique, so don't create a symbol. + return die + case dwarf.DW_ABRV_COMPUNIT, dwarf.DW_ABRV_COMPUNIT_TEXTLESS: + // Avoid collisions with "real" symbol names. + name = fmt.Sprintf(".pkg.%s.%d", name, len(d.linkctxt.compUnits)) + st = sym.SDWARFCUINFO + case dwarf.DW_ABRV_VARIABLE: + st = sym.SDWARFVAR + default: + // Everything else is assigned a type of SDWARFTYPE. that + // this also includes loose ends such as STRUCT_FIELD. + st = sym.SDWARFTYPE + } + ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, version) + dsu := d.ldr.MakeSymbolUpdater(ds) + dsu.SetType(st) + d.ldr.SetAttrNotInSymbolTable(ds, true) + d.ldr.SetAttrReachable(ds, true) + die.Sym = dwSym(ds) + if abbrev >= dwarf.DW_ABRV_NULLTYPE && abbrev <= dwarf.DW_ABRV_TYPEDECL { + d.tmap[name] = ds + } + + return die +} + +func walktypedef(die *dwarf.DWDie) *dwarf.DWDie { + if die == nil { + return nil + } + // Resolve typedef if present. + if die.Abbrev == dwarf.DW_ABRV_TYPEDECL { + for attr := die.Attr; attr != nil; attr = attr.Link { + if attr.Atr == dwarf.DW_AT_type && attr.Cls == dwarf.DW_CLS_REFERENCE && attr.Data != nil { + return attr.Data.(*dwarf.DWDie) + } + } + } + + return die +} + +func (d *dwctxt) walksymtypedef(symIdx loader.Sym) loader.Sym { + + // We're being given the loader symbol for the type DIE, e.g. + // "go.info.type.uintptr". Map that first to the type symbol (e.g. + // "type.uintptr") and then to the typedef DIE for the type. + // FIXME: this seems clunky, maybe there is a better way to do this. + + if ts, ok := d.rtmap[symIdx]; ok { + if def, ok := d.tdmap[ts]; ok { + return def + } + d.linkctxt.Errorf(ts, "internal error: no entry for sym %d in tdmap\n", ts) + return 0 + } + d.linkctxt.Errorf(symIdx, "internal error: no entry for sym %d in rtmap\n", symIdx) + return 0 +} + +// Find child by AT_name using hashtable if available or linear scan +// if not. +func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie { + var prev *dwarf.DWDie + for ; die != prev; prev, die = die, walktypedef(die) { + for a := die.Child; a != nil; a = a.Link { + if name == getattr(a, dwarf.DW_AT_name).Data { + return a + } + } + continue + } + return nil +} + +// find looks up the loader symbol for the DWARF DIE generated for the +// type with the specified name. +func (d *dwctxt) find(name string) loader.Sym { + return d.tmap[name] +} + +func (d *dwctxt) mustFind(name string) loader.Sym { + r := d.find(name) + if r == 0 { + Exitf("dwarf find: cannot find %s", name) + } + return r +} + +func (d *dwctxt) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) int64 { + var result int64 + switch size { + default: + d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size) + case d.arch.PtrSize, 4: + } + result = sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size) + return result +} + +func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) *dwarf.DWAttr { + if ref == 0 { + return nil + } + return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref)) +} + +func (d *dwctxt) dtolsym(s dwarf.Sym) loader.Sym { + if s == nil { + return 0 + } + dws := loader.Sym(s.(dwSym)) + return dws +} + +func (d *dwctxt) putdie(syms []loader.Sym, die *dwarf.DWDie) []loader.Sym { + s := d.dtolsym(die.Sym) + if s == 0 { + s = syms[len(syms)-1] + } else { + syms = append(syms, s) + } + sDwsym := dwSym(s) + dwarf.Uleb128put(d, sDwsym, int64(die.Abbrev)) + dwarf.PutAttrs(d, sDwsym, die.Abbrev, die.Attr) + if dwarf.HasChildren(die) { + for die := die.Child; die != nil; die = die.Link { + syms = d.putdie(syms, die) + } + dsu := d.ldr.MakeSymbolUpdater(syms[len(syms)-1]) + dsu.AddUint8(0) + } + return syms +} + +func reverselist(list **dwarf.DWDie) { + curr := *list + var prev *dwarf.DWDie + for curr != nil { + next := curr.Link + curr.Link = prev + prev = curr + curr = next + } + + *list = prev +} + +func reversetree(list **dwarf.DWDie) { + reverselist(list) + for die := *list; die != nil; die = die.Link { + if dwarf.HasChildren(die) { + reversetree(&die.Child) + } + } +} + +func newmemberoffsetattr(die *dwarf.DWDie, offs int32) { + newattr(die, dwarf.DW_AT_data_member_location, dwarf.DW_CLS_CONSTANT, int64(offs), nil) +} + +// GDB doesn't like FORM_addr for AT_location, so emit a +// location expression that evals to a const. +func (d *dwctxt) newabslocexprattr(die *dwarf.DWDie, addr int64, symIdx loader.Sym) { + newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, dwSym(symIdx)) +} + +func (d *dwctxt) lookupOrDiag(n string) loader.Sym { + symIdx := d.ldr.Lookup(n, 0) + if symIdx == 0 { + Exitf("dwarf: missing type: %s", n) + } + if len(d.ldr.Data(symIdx)) == 0 { + Exitf("dwarf: missing type (no data): %s", n) + } + + return symIdx +} + +func (d *dwctxt) dotypedef(parent *dwarf.DWDie, gotype loader.Sym, name string, def *dwarf.DWDie) *dwarf.DWDie { + // Only emit typedefs for real names. + if strings.HasPrefix(name, "map[") { + return nil + } + if strings.HasPrefix(name, "struct {") { + return nil + } + if strings.HasPrefix(name, "chan ") { + return nil + } + if name[0] == '[' || name[0] == '*' { + return nil + } + if def == nil { + Errorf(nil, "dwarf: bad def in dotypedef") + } + + // Create a new loader symbol for the typedef. We no longer + // do lookups of typedef symbols by name, so this is going + // to be an anonymous symbol (we want this for perf reasons). + tds := d.ldr.CreateExtSym("", 0) + tdsu := d.ldr.MakeSymbolUpdater(tds) + tdsu.SetType(sym.SDWARFTYPE) + def.Sym = dwSym(tds) + d.ldr.SetAttrNotInSymbolTable(tds, true) + d.ldr.SetAttrReachable(tds, true) + + // The typedef entry must be created after the def, + // so that future lookups will find the typedef instead + // of the real definition. This hooks the typedef into any + // circular definition loops, so that gdb can understand them. + die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name, 0) + + d.newrefattr(die, dwarf.DW_AT_type, tds) + + return die +} + +// Define gotype, for composite ones recurse into constituents. +func (d *dwctxt) defgotype(gotype loader.Sym) loader.Sym { + if gotype == 0 { + return d.mustFind("<unspecified>") + } + + // If we already have a tdmap entry for the gotype, return it. + if ds, ok := d.tdmap[gotype]; ok { + return ds + } + + sn := d.ldr.SymName(gotype) + if !strings.HasPrefix(sn, "type.") { + d.linkctxt.Errorf(gotype, "dwarf: type name doesn't start with \"type.\"") + return d.mustFind("<unspecified>") + } + name := sn[5:] // could also decode from Type.string + + sdie := d.find(name) + if sdie != 0 { + return sdie + } + + gtdwSym := d.newtype(gotype) + d.tdmap[gotype] = loader.Sym(gtdwSym.Sym.(dwSym)) + return loader.Sym(gtdwSym.Sym.(dwSym)) +} + +func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { + sn := d.ldr.SymName(gotype) + name := sn[5:] // could also decode from Type.string + tdata := d.ldr.Data(gotype) + kind := decodetypeKind(d.arch, tdata) + bytesize := decodetypeSize(d.arch, tdata) + + var die, typedefdie *dwarf.DWDie + switch kind { + case objabi.KindBool: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindInt, + objabi.KindInt8, + objabi.KindInt16, + objabi.KindInt32, + objabi.KindInt64: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindUint, + objabi.KindUint8, + objabi.KindUint16, + objabi.KindUint32, + objabi.KindUint64, + objabi.KindUintptr: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindFloat32, + objabi.KindFloat64: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindComplex64, + objabi.KindComplex128: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindArray: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + s := decodetypeArrayElem(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) + fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0) + + // use actual length not upper bound; correct for 0-length arrays. + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(d.ldr, d.arch, gotype), 0) + + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + + case objabi.KindChan: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0) + s := decodetypeChanElem(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s)) + // Save elem type for synthesizechantypes. We could synthesize here + // but that would change the order of DIEs we output. + d.newrefattr(die, dwarf.DW_AT_type, s) + + case objabi.KindFunc: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + data := d.ldr.Data(gotype) + // FIXME: add caching or reuse reloc slice. + relocs := d.ldr.Relocs(gotype) + nfields := decodetypeFuncInCount(d.arch, data) + for i := 0; i < nfields; i++ { + s := decodetypeFuncInType(d.ldr, d.arch, gotype, &relocs, i) + sn := d.ldr.SymName(s) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) + } + + if decodetypeFuncDotdotdot(d.arch, data) { + d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0) + } + nfields = decodetypeFuncOutCount(d.arch, data) + for i := 0; i < nfields; i++ { + s := decodetypeFuncOutType(d.ldr, d.arch, gotype, &relocs, i) + sn := d.ldr.SymName(s) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s))) + } + + case objabi.KindInterface: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + data := d.ldr.Data(gotype) + nfields := int(decodetypeIfaceMethodCount(d.arch, data)) + var s loader.Sym + if nfields == 0 { + s = d.typeRuntimeEface + } else { + s = d.typeRuntimeIface + } + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) + + case objabi.KindMap: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0) + s := decodetypeMapKey(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s)) + s = decodetypeMapValue(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s)) + // Save gotype for use in synthesizemaptypes. We could synthesize here, + // but that would change the order of the DIEs. + d.newrefattr(die, dwarf.DW_AT_type, gotype) + + case objabi.KindPtr: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + s := decodetypePtrElem(d.ldr, d.arch, gotype) + d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) + + case objabi.KindSlice: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + s := decodetypeArrayElem(d.ldr, d.arch, gotype) + elem := d.defgotype(s) + d.newrefattr(die, dwarf.DW_AT_go_elem, elem) + + case objabi.KindString: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + + case objabi.KindStruct: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0) + typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) + nfields := decodetypeStructFieldCount(d.ldr, d.arch, gotype) + for i := 0; i < nfields; i++ { + f := decodetypeStructFieldName(d.ldr, d.arch, gotype, i) + s := decodetypeStructFieldType(d.ldr, d.arch, gotype, i) + if f == "" { + sn := d.ldr.SymName(s) + f = sn[5:] // skip "type." + } + fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f, 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) + offsetAnon := decodetypeStructFieldOffsAnon(d.ldr, d.arch, gotype, i) + newmemberoffsetattr(fld, int32(offsetAnon>>1)) + if offsetAnon&1 != 0 { // is embedded field + newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0) + } + } + + case objabi.KindUnsafePointer: + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0) + + default: + d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0) + d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("<unspecified>")) + } + + newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0) + + if d.ldr.AttrReachable(gotype) { + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gotype)) + } + + // Sanity check. + if _, ok := d.rtmap[gotype]; ok { + log.Fatalf("internal error: rtmap entry already installed\n") + } + + ds := loader.Sym(die.Sym.(dwSym)) + if typedefdie != nil { + ds = loader.Sym(typedefdie.Sym.(dwSym)) + } + d.rtmap[ds] = gotype + + if _, ok := prototypedies[sn]; ok { + prototypedies[sn] = die + } + + if typedefdie != nil { + return typedefdie + } + return die +} + +func (d *dwctxt) nameFromDIESym(dwtypeDIESym loader.Sym) string { + sn := d.ldr.SymName(dwtypeDIESym) + return sn[len(dwarf.InfoPrefix):] +} + +func (d *dwctxt) defptrto(dwtype loader.Sym) loader.Sym { + + // FIXME: it would be nice if the compiler attached an aux symbol + // ref from the element type to the pointer type -- it would be + // more efficient to do it this way as opposed to via name lookups. + + ptrname := "*" + d.nameFromDIESym(dwtype) + if die := d.find(ptrname); die != 0 { + return die + } + + pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0) + d.newrefattr(pdie, dwarf.DW_AT_type, dwtype) + + // The DWARF info synthesizes pointer types that don't exist at the + // language level, like *hash<...> and *bucket<...>, and the data + // pointers of slices. Link to the ones we can find. + gts := d.ldr.Lookup("type."+ptrname, 0) + if gts != 0 && d.ldr.AttrReachable(gts) { + newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gts)) + } + + if gts != 0 { + ds := loader.Sym(pdie.Sym.(dwSym)) + d.rtmap[ds] = gts + d.tdmap[gts] = ds + } + + return d.dtolsym(pdie.Sym) +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. If except is one of +// the top-level children, it will not be copied. +func (d *dwctxt) copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) { + for src = src.Child; src != nil; src = src.Link { + if src == except { + continue + } + c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0) + for a := src.Attr; a != nil; a = a.Link { + newattr(c, a.Atr, int(a.Cls), a.Value, a.Data) + } + d.copychildrenexcept(ctxt, c, src, nil) + } + + reverselist(&dst.Child) +} + +func (d *dwctxt) copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) { + d.copychildrenexcept(ctxt, dst, src, nil) +} + +// Search children (assumed to have TAG_member) for the one named +// field and set its AT_type to dwtype +func (d *dwctxt) substitutetype(structdie *dwarf.DWDie, field string, dwtype loader.Sym) { + child := findchild(structdie, field) + if child == nil { + Exitf("dwarf substitutetype: %s does not have member %s", + getattr(structdie, dwarf.DW_AT_name).Data, field) + return + } + + a := getattr(child, dwarf.DW_AT_type) + if a != nil { + a.Data = dwSym(dwtype) + } else { + d.newrefattr(child, dwarf.DW_AT_type, dwtype) + } +} + +func (d *dwctxt) findprotodie(ctxt *Link, name string) *dwarf.DWDie { + die, ok := prototypedies[name] + if ok && die == nil { + d.defgotype(d.lookupOrDiag(name)) + die = prototypedies[name] + } + if die == nil { + log.Fatalf("internal error: DIE generation failed for %s\n", name) + } + return die +} + +func (d *dwctxt) synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.stringStructDWARF")) + if prototype == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE { + continue + } + d.copychildren(ctxt, die, prototype) + } +} + +func (d *dwctxt) synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) { + prototype := walktypedef(d.findprotodie(ctxt, "type.runtime.slice")) + if prototype == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_SLICETYPE { + continue + } + d.copychildren(ctxt, die, prototype) + elem := loader.Sym(getattr(die, dwarf.DW_AT_go_elem).Data.(dwSym)) + d.substitutetype(die, "array", d.defptrto(elem)) + } +} + +func mkinternaltypename(base string, arg1 string, arg2 string) string { + if arg2 == "" { + return fmt.Sprintf("%s<%s>", base, arg1) + } + return fmt.Sprintf("%s<%s,%s>", base, arg1, arg2) +} + +// synthesizemaptypes is way too closely married to runtime/hashmap.c +const ( + MaxKeySize = 128 + MaxValSize = 128 + BucketSize = 8 +) + +func (d *dwctxt) mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) loader.Sym { + name := mkinternaltypename(typename, keyname, valname) + symname := dwarf.InfoPrefix + name + s := d.ldr.Lookup(symname, 0) + if s != 0 && d.ldr.SymType(s) == sym.SDWARFTYPE { + return s + } + die := d.newdie(&dwtypes, abbrev, name, 0) + f(die) + return d.dtolsym(die.Sym) +} + +func (d *dwctxt) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { + hash := walktypedef(d.findprotodie(ctxt, "type.runtime.hmap")) + bucket := walktypedef(d.findprotodie(ctxt, "type.runtime.bmap")) + + if hash == nil { + return + } + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_MAPTYPE { + continue + } + gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym)) + keytype := decodetypeMapKey(d.ldr, d.arch, gotype) + valtype := decodetypeMapValue(d.ldr, d.arch, gotype) + keydata := d.ldr.Data(keytype) + valdata := d.ldr.Data(valtype) + keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata) + keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype)) + + // compute size info like hashmap.c does. + indirectKey, indirectVal := false, false + if keysize > MaxKeySize { + keysize = int64(d.arch.PtrSize) + indirectKey = true + } + if valsize > MaxValSize { + valsize = int64(d.arch.PtrSize) + indirectVal = true + } + + // Construct type to represent an array of BucketSize keys + keyname := d.nameFromDIESym(keytype) + dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) { + newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0) + t := keytype + if indirectKey { + t = d.defptrto(keytype) + } + d.newrefattr(dwhk, dwarf.DW_AT_type, t) + fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + }) + + // Construct type to represent an array of BucketSize values + valname := d.nameFromDIESym(valtype) + dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) { + newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0) + t := valtype + if indirectVal { + t = d.defptrto(valtype) + } + d.newrefattr(dwhv, dwarf.DW_AT_type, t) + fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + }) + + // Construct bucket<K,V> + dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) { + // Copy over all fields except the field "data" from the generic + // bucket. "data" will be replaced with keys/values below. + d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) + + fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0) + d.newrefattr(fld, dwarf.DW_AT_type, dwhks) + newmemberoffsetattr(fld, BucketSize) + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0) + d.newrefattr(fld, dwarf.DW_AT_type, dwhvs) + newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym))) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) + if d.arch.RegSize > d.arch.PtrSize { + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0) + d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize)) + } + + newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(d.arch.RegSize), 0) + }) + + // Construct hash<K,V> + dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) { + d.copychildren(ctxt, dwh, hash) + d.substitutetype(dwh, "buckets", d.defptrto(dwhbs)) + d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs)) + newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil) + }) + + // make map type a pointer to hash<K,V> + d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) + } +} + +func (d *dwctxt) synthesizechantypes(ctxt *Link, die *dwarf.DWDie) { + sudog := walktypedef(d.findprotodie(ctxt, "type.runtime.sudog")) + waitq := walktypedef(d.findprotodie(ctxt, "type.runtime.waitq")) + hchan := walktypedef(d.findprotodie(ctxt, "type.runtime.hchan")) + if sudog == nil || waitq == nil || hchan == nil { + return + } + + sudogsize := int(getattr(sudog, dwarf.DW_AT_byte_size).Value) + + for ; die != nil; die = die.Link { + if die.Abbrev != dwarf.DW_ABRV_CHANTYPE { + continue + } + elemgotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym)) + tname := d.ldr.SymName(elemgotype) + elemname := tname[5:] + elemtype := d.walksymtypedef(d.defgotype(d.lookupOrDiag(tname))) + + // sudog<T> + dwss := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) { + d.copychildren(ctxt, dws, sudog) + d.substitutetype(dws, "elem", d.defptrto(elemtype)) + newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize), nil) + }) + + // waitq<T> + dwws := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) { + + d.copychildren(ctxt, dww, waitq) + d.substitutetype(dww, "first", d.defptrto(dwss)) + d.substitutetype(dww, "last", d.defptrto(dwss)) + newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil) + }) + + // hchan<T> + dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) { + d.copychildren(ctxt, dwh, hchan) + d.substitutetype(dwh, "recvq", dwws) + d.substitutetype(dwh, "sendq", dwws) + newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil) + }) + + d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs)) + } +} + +func (d *dwctxt) dwarfDefineGlobal(ctxt *Link, symIdx loader.Sym, str string, v int64, gotype loader.Sym) { + // Find a suitable CU DIE to include the global. + // One would think it's as simple as just looking at the unit, but that might + // not have any reachable code. So, we go to the runtime's CU if our unit + // isn't otherwise reachable. + unit := d.ldr.SymUnit(symIdx) + if unit == nil { + unit = ctxt.runtimeCU + } + ver := d.ldr.SymVersion(symIdx) + dv := d.newdie(unit.DWInfo, dwarf.DW_ABRV_VARIABLE, str, int(ver)) + d.newabslocexprattr(dv, v, symIdx) + if d.ldr.SymVersion(symIdx) < sym.SymVerStatic { + newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0) + } + dt := d.defgotype(gotype) + d.newrefattr(dv, dwarf.DW_AT_type, dt) +} + +// createUnitLength creates the initial length field with value v and update +// offset of unit_length if needed. +func (d *dwctxt) createUnitLength(su *loader.SymbolBuilder, v uint64) { + if isDwarf64(d.linkctxt) { + su.AddUint32(d.arch, 0xFFFFFFFF) + } + d.addDwarfAddrField(su, v) +} + +// addDwarfAddrField adds a DWARF field in DWARF 64bits or 32bits. +func (d *dwctxt) addDwarfAddrField(sb *loader.SymbolBuilder, v uint64) { + if isDwarf64(d.linkctxt) { + sb.AddUint(d.arch, v) + } else { + sb.AddUint32(d.arch, uint32(v)) + } +} + +// addDwarfAddrRef adds a DWARF pointer in DWARF 64bits or 32bits. +func (d *dwctxt) addDwarfAddrRef(sb *loader.SymbolBuilder, t loader.Sym) { + if isDwarf64(d.linkctxt) { + d.adddwarfref(sb, t, 8) + } else { + d.adddwarfref(sb, t, 4) + } +} + +// calcCompUnitRanges calculates the PC ranges of the compilation units. +func (d *dwctxt) calcCompUnitRanges() { + var prevUnit *sym.CompilationUnit + for _, s := range d.linkctxt.Textp { + sym := loader.Sym(s) + + fi := d.ldr.FuncInfo(sym) + if !fi.Valid() { + continue + } + + // Skip linker-created functions (ex: runtime.addmoduledata), since they + // don't have DWARF to begin with. + unit := d.ldr.SymUnit(sym) + if unit == nil { + continue + } + + // Update PC ranges. + // + // We don't simply compare the end of the previous + // symbol with the start of the next because there's + // often a little padding between them. Instead, we + // only create boundaries between symbols from + // different units. + sval := d.ldr.SymValue(sym) + u0val := d.ldr.SymValue(loader.Sym(unit.Textp[0])) + if prevUnit != unit { + unit.PCs = append(unit.PCs, dwarf.Range{Start: sval - u0val}) + prevUnit = unit + } + unit.PCs[len(unit.PCs)-1].End = sval - u0val + int64(len(d.ldr.Data(sym))) + } +} + +func movetomodule(ctxt *Link, parent *dwarf.DWDie) { + die := ctxt.runtimeCU.DWInfo.Child + if die == nil { + ctxt.runtimeCU.DWInfo.Child = parent.Child + return + } + for die.Link != nil { + die = die.Link + } + die.Link = parent.Child +} + +/* + * Generate a sequence of opcodes that is as short as possible. + * See section 6.2.5 + */ +const ( + LINE_BASE = -4 + LINE_RANGE = 10 + PC_RANGE = (255 - OPCODE_BASE) / LINE_RANGE + OPCODE_BASE = 11 +) + +/* + * Walk prog table, emit line program and build DIE tree. + */ + +func getCompilationDir() string { + // OSX requires this be set to something, but it's not easy to choose + // a value. Linking takes place in a temporary directory, so there's + // no point including it here. Paths in the file table are usually + // absolute, in which case debuggers will ignore this value. -trimpath + // produces relative paths, but we don't know where they start, so + // all we can do here is try not to make things worse. + return "." +} + +func (d *dwctxt) importInfoSymbol(dsym loader.Sym) { + d.ldr.SetAttrReachable(dsym, true) + d.ldr.SetAttrNotInSymbolTable(dsym, true) + dst := d.ldr.SymType(dsym) + if dst != sym.SDWARFCONST && dst != sym.SDWARFABSFCN { + log.Fatalf("error: DWARF info sym %d/%s with incorrect type %s", dsym, d.ldr.SymName(dsym), d.ldr.SymType(dsym).String()) + } + relocs := d.ldr.Relocs(dsym) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At(i) + if r.Type() != objabi.R_DWARFSECREF { + continue + } + rsym := r.Sym() + // If there is an entry for the symbol in our rtmap, then it + // means we've processed the type already, and can skip this one. + if _, ok := d.rtmap[rsym]; ok { + // type already generated + continue + } + // FIXME: is there a way we could avoid materializing the + // symbol name here? + sn := d.ldr.SymName(rsym) + tn := sn[len(dwarf.InfoPrefix):] + ts := d.ldr.Lookup("type."+tn, 0) + d.defgotype(ts) + } +} + +func expandFile(fname string) string { + if strings.HasPrefix(fname, src.FileSymPrefix) { + fname = fname[len(src.FileSymPrefix):] + } + return expandGoroot(fname) +} + +// writeDirFileTables emits the portion of the DWARF line table +// prologue containing the include directories and file names, +// described in section 6.2.4 of the DWARF 4 standard. It walks the +// filepaths for the unit to discover any common directories, which +// are emitted to the directory table first, then the file table is +// emitted after that. +func (d *dwctxt) writeDirFileTables(unit *sym.CompilationUnit, lsu *loader.SymbolBuilder) { + type fileDir struct { + base string + dir int + } + dirNums := make(map[string]int) + dirs := []string{""} + files := []fileDir{} + + // Preprocess files to collect directories. This assumes that the + // file table is already de-duped. + for i, name := range unit.FileTable { + name := expandFile(name) + if len(name) == 0 { + // Can't have empty filenames, and having a unique + // filename is quite useful for debugging. + name = fmt.Sprintf("<missing>_%d", i) + } + // Note the use of "path" here and not "filepath". The compiler + // hard-codes to use "/" in DWARF paths (even for Windows), so we + // want to maintain that here. + file := path.Base(name) + dir := path.Dir(name) + dirIdx, ok := dirNums[dir] + if !ok && dir != "." { + dirIdx = len(dirNums) + 1 + dirNums[dir] = dirIdx + dirs = append(dirs, dir) + } + files = append(files, fileDir{base: file, dir: dirIdx}) + + // We can't use something that may be dead-code + // eliminated from a binary here. proc.go contains + // main and the scheduler, so it's not going anywhere. + if i := strings.Index(name, "runtime/proc.go"); i >= 0 { + d.dwmu.Lock() + if gdbscript == "" { + k := strings.Index(name, "runtime/proc.go") + gdbscript = name[:k] + "runtime/runtime-gdb.py" + } + d.dwmu.Unlock() + } + } + + // Emit directory section. This is a series of nul terminated + // strings, followed by a single zero byte. + lsDwsym := dwSym(lsu.Sym()) + for k := 1; k < len(dirs); k++ { + d.AddString(lsDwsym, dirs[k]) + } + lsu.AddUint8(0) // terminator + + // Emit file section. + for k := 0; k < len(files); k++ { + d.AddString(lsDwsym, files[k].base) + dwarf.Uleb128put(d, lsDwsym, int64(files[k].dir)) + lsu.AddUint8(0) // mtime + lsu.AddUint8(0) // length + } + lsu.AddUint8(0) // terminator +} + +// writelines collects up and chains together the symbols needed to +// form the DWARF line table for the specified compilation unit, +// returning a list of symbols. The returned list will include an +// initial symbol containing the line table header and prologue (with +// file table), then a series of compiler-emitted line table symbols +// (one per live function), and finally an epilog symbol containing an +// end-of-sequence operator. The prologue and epilog symbols are passed +// in (having been created earlier); here we add content to them. +func (d *dwctxt) writelines(unit *sym.CompilationUnit, lineProlog loader.Sym) []loader.Sym { + is_stmt := uint8(1) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles. + + unitstart := int64(-1) + headerstart := int64(-1) + headerend := int64(-1) + + syms := make([]loader.Sym, 0, len(unit.Textp)+2) + syms = append(syms, lineProlog) + lsu := d.ldr.MakeSymbolUpdater(lineProlog) + lsDwsym := dwSym(lineProlog) + newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, 0, lsDwsym) + + // Write .debug_line Line Number Program Header (sec 6.2.4) + // Fields marked with (*) must be changed for 64-bit dwarf + unitLengthOffset := lsu.Size() + d.createUnitLength(lsu, 0) // unit_length (*), filled in at end + unitstart = lsu.Size() + lsu.AddUint16(d.arch, 2) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05 + headerLengthOffset := lsu.Size() + d.addDwarfAddrField(lsu, 0) // header_length (*), filled in at end + headerstart = lsu.Size() + + // cpos == unitstart + 4 + 2 + 4 + lsu.AddUint8(1) // minimum_instruction_length + lsu.AddUint8(is_stmt) // default_is_stmt + lsu.AddUint8(LINE_BASE & 0xFF) // line_base + lsu.AddUint8(LINE_RANGE) // line_range + lsu.AddUint8(OPCODE_BASE) // opcode_base + lsu.AddUint8(0) // standard_opcode_lengths[1] + lsu.AddUint8(1) // standard_opcode_lengths[2] + lsu.AddUint8(1) // standard_opcode_lengths[3] + lsu.AddUint8(1) // standard_opcode_lengths[4] + lsu.AddUint8(1) // standard_opcode_lengths[5] + lsu.AddUint8(0) // standard_opcode_lengths[6] + lsu.AddUint8(0) // standard_opcode_lengths[7] + lsu.AddUint8(0) // standard_opcode_lengths[8] + lsu.AddUint8(1) // standard_opcode_lengths[9] + lsu.AddUint8(0) // standard_opcode_lengths[10] + + // Call helper to emit dir and file sections. + d.writeDirFileTables(unit, lsu) + + // capture length at end of file names. + headerend = lsu.Size() + unitlen := lsu.Size() - unitstart + + // Output the state machine for each function remaining. + for _, s := range unit.Textp { + fnSym := loader.Sym(s) + _, _, _, lines := d.ldr.GetFuncDwarfAuxSyms(fnSym) + + // Chain the line symbol onto the list. + if lines != 0 { + syms = append(syms, lines) + unitlen += int64(len(d.ldr.Data(lines))) + } + } + + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(unitlen)) + } + + if isDwarf64(d.linkctxt) { + lsu.SetUint(d.arch, unitLengthOffset+4, uint64(unitlen)) // +4 because of 0xFFFFFFFF + lsu.SetUint(d.arch, headerLengthOffset, uint64(headerend-headerstart)) + } else { + lsu.SetUint32(d.arch, unitLengthOffset, uint32(unitlen)) + lsu.SetUint32(d.arch, headerLengthOffset, uint32(headerend-headerstart)) + } + + return syms +} + +// writepcranges generates the DW_AT_ranges table for compilation unit +// "unit", and returns a collection of ranges symbols (one for the +// compilation unit DIE itself and the remainder from functions in the unit). +func (d *dwctxt) writepcranges(unit *sym.CompilationUnit, base loader.Sym, pcs []dwarf.Range, rangeProlog loader.Sym) []loader.Sym { + + syms := make([]loader.Sym, 0, len(unit.RangeSyms)+1) + syms = append(syms, rangeProlog) + rsu := d.ldr.MakeSymbolUpdater(rangeProlog) + rDwSym := dwSym(rangeProlog) + + // Create PC ranges for the compilation unit DIE. + newattr(unit.DWInfo, dwarf.DW_AT_ranges, dwarf.DW_CLS_PTR, rsu.Size(), rDwSym) + newattr(unit.DWInfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, 0, dwSym(base)) + dwarf.PutBasedRanges(d, rDwSym, pcs) + + // Collect up the ranges for functions in the unit. + rsize := uint64(rsu.Size()) + for _, ls := range unit.RangeSyms { + s := loader.Sym(ls) + syms = append(syms, s) + rsize += uint64(d.ldr.SymSize(s)) + } + + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_ranges", unit.Lib.Pkg, rsize) + } + + return syms +} + +/* + * Emit .debug_frame + */ +const ( + dataAlignmentFactor = -4 +) + +// appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice. +func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte { + b = append(b, dwarf.DW_CFA_def_cfa_offset_sf) + b = dwarf.AppendSleb128(b, cfa/dataAlignmentFactor) + + switch { + case deltapc < 0x40: + b = append(b, uint8(dwarf.DW_CFA_advance_loc+deltapc)) + case deltapc < 0x100: + b = append(b, dwarf.DW_CFA_advance_loc1) + b = append(b, uint8(deltapc)) + case deltapc < 0x10000: + b = append(b, dwarf.DW_CFA_advance_loc2, 0, 0) + arch.ByteOrder.PutUint16(b[len(b)-2:], uint16(deltapc)) + default: + b = append(b, dwarf.DW_CFA_advance_loc4, 0, 0, 0, 0) + arch.ByteOrder.PutUint32(b[len(b)-4:], uint32(deltapc)) + } + return b +} + +func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo { + fsd := dwSym(fs) + fsu := d.ldr.MakeSymbolUpdater(fs) + fsu.SetType(sym.SDWARFSECT) + isdw64 := isDwarf64(d.linkctxt) + haslr := haslinkregister(d.linkctxt) + + // Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64 + lengthFieldSize := int64(4) + if isdw64 { + lengthFieldSize += 8 + } + + // Emit the CIE, Section 6.4.1 + cieReserve := uint32(16) + if haslr { + cieReserve = 32 + } + if isdw64 { + cieReserve += 4 // 4 bytes added for cid + } + d.createUnitLength(fsu, uint64(cieReserve)) // initial length, must be multiple of thearch.ptrsize + d.addDwarfAddrField(fsu, ^uint64(0)) // cid + fsu.AddUint8(3) // dwarf version (appendix F) + fsu.AddUint8(0) // augmentation "" + dwarf.Uleb128put(d, fsd, 1) // code_alignment_factor + dwarf.Sleb128put(d, fsd, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // return_address_register + + fsu.AddUint8(dwarf.DW_CFA_def_cfa) // Set the current frame address.. + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)... + if haslr { + dwarf.Uleb128put(d, fsd, int64(0)) // ...plus a 0 offset. + + fsu.AddUint8(dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue. + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) + + fsu.AddUint8(dwarf.DW_CFA_val_offset) // The previous value... + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfregsp)) // ...of the platform's SP register... + dwarf.Uleb128put(d, fsd, int64(0)) // ...is CFA+0. + } else { + dwarf.Uleb128put(d, fsd, int64(d.arch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame). + + fsu.AddUint8(dwarf.DW_CFA_offset_extended) // The previous value... + dwarf.Uleb128put(d, fsd, int64(thearch.Dwarfreglr)) // ...of the return address... + dwarf.Uleb128put(d, fsd, int64(-d.arch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)]. + } + + pad := int64(cieReserve) + lengthFieldSize - int64(len(d.ldr.Data(fs))) + + if pad < 0 { + Exitf("dwarf: cieReserve too small by %d bytes.", -pad) + } + + internalExec := d.linkctxt.BuildMode == BuildModeExe && d.linkctxt.IsInternal() + addAddrPlus := loader.GenAddAddrPlusFunc(internalExec) + + fsu.AddBytes(zeros[:pad]) + + var deltaBuf []byte + pcsp := obj.NewPCIter(uint32(d.arch.MinLC)) + for _, s := range d.linkctxt.Textp { + fn := loader.Sym(s) + fi := d.ldr.FuncInfo(fn) + if !fi.Valid() { + continue + } + fpcsp := fi.Pcsp() + + // Emit a FDE, Section 6.4.1. + // First build the section contents into a byte buffer. + deltaBuf = deltaBuf[:0] + if haslr && d.ldr.AttrTopFrame(fn) { + // Mark the link register as having an undefined value. + // This stops call stack unwinders progressing any further. + // TODO: similar mark on non-LR architectures. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_undefined) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + } + + for pcsp.Init(d.linkctxt.loader.Data(fpcsp)); !pcsp.Done; pcsp.Next() { + nextpc := pcsp.NextPC + + // pciterinit goes up to the end of the function, + // but DWARF expects us to stop just before the end. + if int64(nextpc) == int64(len(d.ldr.Data(fn))) { + nextpc-- + if nextpc < pcsp.PC { + continue + } + } + + spdelta := int64(pcsp.Value) + if !haslr { + // Return address has been pushed onto stack. + spdelta += int64(d.arch.PtrSize) + } + + if haslr && !d.ldr.AttrTopFrame(fn) { + // TODO(bryanpkc): This is imprecise. In general, the instruction + // that stores the return address to the stack frame is not the + // same one that allocates the frame. + if pcsp.Value > 0 { + // The return address is preserved at (CFA-frame_size) + // after a stack frame has been allocated. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_offset_extended_sf) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + deltaBuf = dwarf.AppendSleb128(deltaBuf, -spdelta/dataAlignmentFactor) + } else { + // The return address is restored into the link register + // when a stack frame has been de-allocated. + deltaBuf = append(deltaBuf, dwarf.DW_CFA_same_value) + deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) + } + } + + deltaBuf = appendPCDeltaCFA(d.arch, deltaBuf, int64(nextpc)-int64(pcsp.PC), spdelta) + } + pad := int(Rnd(int64(len(deltaBuf)), int64(d.arch.PtrSize))) - len(deltaBuf) + deltaBuf = append(deltaBuf, zeros[:pad]...) + + // Emit the FDE header, Section 6.4.1. + // 4 bytes: length, must be multiple of thearch.ptrsize + // 4/8 bytes: Pointer to the CIE above, at offset 0 + // ptrsize: initial location + // ptrsize: address range + + fdeLength := uint64(4 + 2*d.arch.PtrSize + len(deltaBuf)) + if isdw64 { + fdeLength += 4 // 4 bytes added for CIE pointer + } + d.createUnitLength(fsu, fdeLength) + + if d.linkctxt.LinkMode == LinkExternal { + d.addDwarfAddrRef(fsu, fs) + } else { + d.addDwarfAddrField(fsu, 0) // CIE offset + } + addAddrPlus(fsu, d.arch, s, 0) + fsu.AddUintXX(d.arch, uint64(len(d.ldr.Data(fn))), d.arch.PtrSize) // address range + fsu.AddBytes(deltaBuf) + + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_frame", d.ldr.SymPkg(fn), fdeLength+uint64(lengthFieldSize)) + } + } + + return dwarfSecInfo{syms: []loader.Sym{fs}} +} + +/* + * Walk DWarfDebugInfoEntries, and emit .debug_info + */ + +const ( + COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 +) + +// appendSyms appends the syms from 'src' into 'syms' and returns the +// result. This can go away once we do away with sym.LoaderSym +// entirely. +func appendSyms(syms []loader.Sym, src []sym.LoaderSym) []loader.Sym { + for _, s := range src { + syms = append(syms, loader.Sym(s)) + } + return syms +} + +func (d *dwctxt) writeUnitInfo(u *sym.CompilationUnit, abbrevsym loader.Sym, infoEpilog loader.Sym) []loader.Sym { + syms := []loader.Sym{} + if len(u.Textp) == 0 && u.DWInfo.Child == nil { + return syms + } + + compunit := u.DWInfo + s := d.dtolsym(compunit.Sym) + su := d.ldr.MakeSymbolUpdater(s) + + // Write .debug_info Compilation Unit Header (sec 7.5.1) + // Fields marked with (*) must be changed for 64-bit dwarf + // This must match COMPUNITHEADERSIZE above. + d.createUnitLength(su, 0) // unit_length (*), will be filled in later. + su.AddUint16(d.arch, 4) // dwarf version (appendix F) + + // debug_abbrev_offset (*) + d.addDwarfAddrRef(su, abbrevsym) + + su.AddUint8(uint8(d.arch.PtrSize)) // address_size + + ds := dwSym(s) + dwarf.Uleb128put(d, ds, int64(compunit.Abbrev)) + dwarf.PutAttrs(d, ds, compunit.Abbrev, compunit.Attr) + + // This is an under-estimate; more will be needed for type DIEs. + cu := make([]loader.Sym, 0, len(u.AbsFnDIEs)+len(u.FuncDIEs)) + cu = append(cu, s) + cu = appendSyms(cu, u.AbsFnDIEs) + cu = appendSyms(cu, u.FuncDIEs) + if u.Consts != 0 { + cu = append(cu, loader.Sym(u.Consts)) + } + var cusize int64 + for _, child := range cu { + cusize += int64(len(d.ldr.Data(child))) + } + + for die := compunit.Child; die != nil; die = die.Link { + l := len(cu) + lastSymSz := int64(len(d.ldr.Data(cu[l-1]))) + cu = d.putdie(cu, die) + if lastSymSz != int64(len(d.ldr.Data(cu[l-1]))) { + // putdie will sometimes append directly to the last symbol of the list + cusize = cusize - lastSymSz + int64(len(d.ldr.Data(cu[l-1]))) + } + for _, child := range cu[l:] { + cusize += int64(len(d.ldr.Data(child))) + } + } + + culu := d.ldr.MakeSymbolUpdater(infoEpilog) + culu.AddUint8(0) // closes compilation unit DIE + cu = append(cu, infoEpilog) + cusize++ + + // Save size for AIX symbol table. + if d.linkctxt.HeadType == objabi.Haix { + addDwsectCUSize(".debug_info", d.getPkgFromCUSym(s), uint64(cusize)) + } + if isDwarf64(d.linkctxt) { + cusize -= 12 // exclude the length field. + su.SetUint(d.arch, 4, uint64(cusize)) // 4 because of 0XFFFFFFFF + } else { + cusize -= 4 // exclude the length field. + su.SetUint32(d.arch, 0, uint32(cusize)) + } + return append(syms, cu...) +} + +func (d *dwctxt) writegdbscript() dwarfSecInfo { + // TODO (aix): make it available + if d.linkctxt.HeadType == objabi.Haix { + return dwarfSecInfo{} + } + if d.linkctxt.LinkMode == LinkExternal && d.linkctxt.HeadType == objabi.Hwindows && d.linkctxt.BuildMode == BuildModeCArchive { + // gcc on Windows places .debug_gdb_scripts in the wrong location, which + // causes the program not to run. See https://golang.org/issue/20183 + // Non c-archives can avoid this issue via a linker script + // (see fix near writeGDBLinkerScript). + // c-archive users would need to specify the linker script manually. + // For UX it's better not to deal with this. + return dwarfSecInfo{} + } + if gdbscript == "" { + return dwarfSecInfo{} + } + + gs := d.ldr.CreateSymForUpdate(".debug_gdb_scripts", 0) + gs.SetType(sym.SDWARFSECT) + + gs.AddUint8(1) // magic 1 byte? + gs.Addstring(gdbscript) + return dwarfSecInfo{syms: []loader.Sym{gs.Sym()}} +} + +// FIXME: might be worth looking replacing this map with a function +// that switches based on symbol instead. + +var prototypedies map[string]*dwarf.DWDie + +func dwarfEnabled(ctxt *Link) bool { + if *FlagW { // disable dwarf + return false + } + if *FlagS && ctxt.HeadType != objabi.Hdarwin { + return false + } + if ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hjs { + return false + } + + if ctxt.LinkMode == LinkExternal { + switch { + case ctxt.IsELF: + case ctxt.HeadType == objabi.Hdarwin: + case ctxt.HeadType == objabi.Hwindows: + case ctxt.HeadType == objabi.Haix: + res, err := dwarf.IsDWARFEnabledOnAIXLd(ctxt.extld()) + if err != nil { + Exitf("%v", err) + } + return res + default: + return false + } + } + + return true +} + +// mkBuiltinType populates the dwctxt2 sym lookup maps for the +// newly created builtin type DIE 'typeDie'. +func (d *dwctxt) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie { + // create type DIE + die := d.newdie(&dwtypes, abrv, tname, 0) + + // Look up type symbol. + gotype := d.lookupOrDiag("type." + tname) + + // Map from die sym to type sym + ds := loader.Sym(die.Sym.(dwSym)) + d.rtmap[ds] = gotype + + // Map from type to def sym + d.tdmap[gotype] = ds + + return die +} + +// dwarfVisitFunction takes a function (text) symbol and processes the +// subprogram DIE for the function and picks up any other DIEs +// (absfns, types) that it references. +func (d *dwctxt) dwarfVisitFunction(fnSym loader.Sym, unit *sym.CompilationUnit) { + // The DWARF subprogram DIE symbol is listed as an aux sym + // of the text (fcn) symbol, so ask the loader to retrieve it, + // as well as the associated range symbol. + infosym, _, rangesym, _ := d.ldr.GetFuncDwarfAuxSyms(fnSym) + if infosym == 0 { + return + } + d.ldr.SetAttrNotInSymbolTable(infosym, true) + d.ldr.SetAttrReachable(infosym, true) + unit.FuncDIEs = append(unit.FuncDIEs, sym.LoaderSym(infosym)) + if rangesym != 0 { + d.ldr.SetAttrNotInSymbolTable(rangesym, true) + d.ldr.SetAttrReachable(rangesym, true) + unit.RangeSyms = append(unit.RangeSyms, sym.LoaderSym(rangesym)) + } + + // Walk the relocations of the subprogram DIE symbol to discover + // references to abstract function DIEs, Go type DIES, and + // (via R_USETYPE relocs) types that were originally assigned to + // locals/params but were optimized away. + drelocs := d.ldr.Relocs(infosym) + for ri := 0; ri < drelocs.Count(); ri++ { + r := drelocs.At(ri) + // Look for "use type" relocs. + if r.Type() == objabi.R_USETYPE { + d.defgotype(r.Sym()) + continue + } + if r.Type() != objabi.R_DWARFSECREF { + continue + } + + rsym := r.Sym() + rst := d.ldr.SymType(rsym) + + // Look for abstract function references. + if rst == sym.SDWARFABSFCN { + if !d.ldr.AttrOnList(rsym) { + // abstract function + d.ldr.SetAttrOnList(rsym, true) + unit.AbsFnDIEs = append(unit.AbsFnDIEs, sym.LoaderSym(rsym)) + d.importInfoSymbol(rsym) + } + continue + } + + // Look for type references. + if rst != sym.SDWARFTYPE && rst != sym.Sxxx { + continue + } + if _, ok := d.rtmap[rsym]; ok { + // type already generated + continue + } + + rsn := d.ldr.SymName(rsym) + tn := rsn[len(dwarf.InfoPrefix):] + ts := d.ldr.Lookup("type."+tn, 0) + d.defgotype(ts) + } +} + +// dwarfGenerateDebugInfo generated debug info entries for all types, +// variables and functions in the program. +// Along with dwarfGenerateDebugSyms they are the two main entry points into +// dwarf generation: dwarfGenerateDebugInfo does all the work that should be +// done before symbol names are mangled while dwarfGenerateDebugSyms does +// all the work that can only be done after addresses have been assigned to +// text symbols. +func dwarfGenerateDebugInfo(ctxt *Link) { + if !dwarfEnabled(ctxt) { + return + } + + d := newdwctxt(ctxt, true) + + if ctxt.HeadType == objabi.Haix { + // Initial map used to store package size for each DWARF section. + dwsectCUSize = make(map[string]uint64) + } + + // For ctxt.Diagnostic messages. + newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") + + // Unspecified type. There are no references to this in the symbol table. + d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0) + + // Some types that must exist to define other ones (uintptr in particular + // is needed for array size) + d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer") + die := d.mkBuiltinType(ctxt, dwarf.DW_ABRV_BASETYPE, "uintptr") + newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(d.arch.PtrSize), 0) + newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, objabi.KindUintptr, 0) + newattr(die, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_ADDRESS, 0, dwSym(d.lookupOrDiag("type.uintptr"))) + + d.uintptrInfoSym = d.mustFind("uintptr") + + // Prototypes needed for type synthesis. + prototypedies = map[string]*dwarf.DWDie{ + "type.runtime.stringStructDWARF": nil, + "type.runtime.slice": nil, + "type.runtime.hmap": nil, + "type.runtime.bmap": nil, + "type.runtime.sudog": nil, + "type.runtime.waitq": nil, + "type.runtime.hchan": nil, + } + + // Needed by the prettyprinter code for interface inspection. + for _, typ := range []string{ + "type.runtime._type", + "type.runtime.arraytype", + "type.runtime.chantype", + "type.runtime.functype", + "type.runtime.maptype", + "type.runtime.ptrtype", + "type.runtime.slicetype", + "type.runtime.structtype", + "type.runtime.interfacetype", + "type.runtime.itab", + "type.runtime.imethod"} { + d.defgotype(d.lookupOrDiag(typ)) + } + + // fake root DIE for compile unit DIEs + var dwroot dwarf.DWDie + flagVariants := make(map[string]bool) + + for _, lib := range ctxt.Library { + + consts := d.ldr.Lookup(dwarf.ConstInfoPrefix+lib.Pkg, 0) + for _, unit := range lib.Units { + // We drop the constants into the first CU. + if consts != 0 { + unit.Consts = sym.LoaderSym(consts) + d.importInfoSymbol(consts) + consts = 0 + } + ctxt.compUnits = append(ctxt.compUnits, unit) + + // We need at least one runtime unit. + if unit.Lib.Pkg == "runtime" { + ctxt.runtimeCU = unit + } + + cuabrv := dwarf.DW_ABRV_COMPUNIT + if len(unit.Textp) == 0 { + cuabrv = dwarf.DW_ABRV_COMPUNIT_TEXTLESS + } + unit.DWInfo = d.newdie(&dwroot, cuabrv, unit.Lib.Pkg, 0) + newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0) + // OS X linker requires compilation dir or absolute path in comp unit name to output debug info. + compDir := getCompilationDir() + // TODO: Make this be the actual compilation directory, not + // the linker directory. If we move CU construction into the + // compiler, this should happen naturally. + newattr(unit.DWInfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir) + + var peData []byte + if producerExtra := d.ldr.Lookup(dwarf.CUInfoPrefix+"producer."+unit.Lib.Pkg, 0); producerExtra != 0 { + peData = d.ldr.Data(producerExtra) + } + producer := "Go cmd/compile " + objabi.Version + if len(peData) > 0 { + // We put a semicolon before the flags to clearly + // separate them from the version, which can be long + // and have lots of weird things in it in development + // versions. We promise not to put a semicolon in the + // version, so it should be safe for readers to scan + // forward to the semicolon. + producer += "; " + string(peData) + flagVariants[string(peData)] = true + } else { + flagVariants[""] = true + } + + newattr(unit.DWInfo, dwarf.DW_AT_producer, dwarf.DW_CLS_STRING, int64(len(producer)), producer) + + var pkgname string + if pnSymIdx := d.ldr.Lookup(dwarf.CUInfoPrefix+"packagename."+unit.Lib.Pkg, 0); pnSymIdx != 0 { + pnsData := d.ldr.Data(pnSymIdx) + pkgname = string(pnsData) + } + newattr(unit.DWInfo, dwarf.DW_AT_go_package_name, dwarf.DW_CLS_STRING, int64(len(pkgname)), pkgname) + + // Scan all functions in this compilation unit, create + // DIEs for all referenced types, find all referenced + // abstract functions, visit range symbols. Note that + // Textp has been dead-code-eliminated already. + for _, s := range unit.Textp { + d.dwarfVisitFunction(loader.Sym(s), unit) + } + } + } + + // Fix for 31034: if the objects feeding into this link were compiled + // with different sets of flags, then don't issue an error if + // the -strictdups checks fail. + if checkStrictDups > 1 && len(flagVariants) > 1 { + checkStrictDups = 1 + } + + // Create DIEs for global variables and the types they use. + // FIXME: ideally this should be done in the compiler, since + // for globals there isn't any abiguity about which package + // a global belongs to. + for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ { + if !d.ldr.AttrReachable(idx) || + d.ldr.AttrNotInSymbolTable(idx) || + d.ldr.SymVersion(idx) >= sym.SymVerStatic { + continue + } + t := d.ldr.SymType(idx) + switch t { + case sym.SRODATA, sym.SDATA, sym.SNOPTRDATA, sym.STYPE, sym.SBSS, sym.SNOPTRBSS, sym.STLSBSS: + // ok + default: + continue + } + // Skip things with no type + if d.ldr.SymGoType(idx) == 0 { + continue + } + // Skip file local symbols (this includes static tmps, stack + // object symbols, and local symbols in assembler src files). + if d.ldr.IsFileLocal(idx) { + continue + } + sn := d.ldr.SymName(idx) + if sn == "" { + // skip aux symbols + continue + } + + // Create DIE for global. + sv := d.ldr.SymValue(idx) + gt := d.ldr.SymGoType(idx) + d.dwarfDefineGlobal(ctxt, idx, sn, sv, gt) + } + + d.synthesizestringtypes(ctxt, dwtypes.Child) + d.synthesizeslicetypes(ctxt, dwtypes.Child) + d.synthesizemaptypes(ctxt, dwtypes.Child) + d.synthesizechantypes(ctxt, dwtypes.Child) +} + +// dwarfGenerateDebugSyms constructs debug_line, debug_frame, and +// debug_loc. It also writes out the debug_info section using symbols +// generated in dwarfGenerateDebugInfo2. +func dwarfGenerateDebugSyms(ctxt *Link) { + if !dwarfEnabled(ctxt) { + return + } + d := &dwctxt{ + linkctxt: ctxt, + ldr: ctxt.loader, + arch: ctxt.Arch, + dwmu: new(sync.Mutex), + } + d.dwarfGenerateDebugSyms() +} + +// dwUnitSyms stores input and output symbols for DWARF generation +// for a given compilation unit. +type dwUnitSyms struct { + // Inputs for a given unit. + lineProlog loader.Sym + rangeProlog loader.Sym + infoEpilog loader.Sym + + // Outputs for a given unit. + linesyms []loader.Sym + infosyms []loader.Sym + locsyms []loader.Sym + rangessyms []loader.Sym +} + +// dwUnitPortion assembles the DWARF content for a given compilation +// unit: debug_info, debug_lines, debug_ranges, debug_loc (debug_frame +// is handled elsewere). Order is important; the calls to writelines +// and writepcranges below make updates to the compilation unit DIE, +// hence they have to happen before the call to writeUnitInfo. +func (d *dwctxt) dwUnitPortion(u *sym.CompilationUnit, abbrevsym loader.Sym, us *dwUnitSyms) { + if u.DWInfo.Abbrev != dwarf.DW_ABRV_COMPUNIT_TEXTLESS { + us.linesyms = d.writelines(u, us.lineProlog) + base := loader.Sym(u.Textp[0]) + us.rangessyms = d.writepcranges(u, base, u.PCs, us.rangeProlog) + us.locsyms = d.collectUnitLocs(u) + } + us.infosyms = d.writeUnitInfo(u, abbrevsym, us.infoEpilog) +} + +func (d *dwctxt) dwarfGenerateDebugSyms() { + abbrevSec := d.writeabbrev() + dwarfp = append(dwarfp, abbrevSec) + d.calcCompUnitRanges() + sort.Sort(compilationUnitByStartPC(d.linkctxt.compUnits)) + + // newdie adds DIEs to the *beginning* of the parent's DIE list. + // Now that we're done creating DIEs, reverse the trees so DIEs + // appear in the order they were created. + for _, u := range d.linkctxt.compUnits { + reversetree(&u.DWInfo.Child) + } + reversetree(&dwtypes.Child) + movetomodule(d.linkctxt, &dwtypes) + + mkSecSym := func(name string) loader.Sym { + s := d.ldr.CreateSymForUpdate(name, 0) + s.SetType(sym.SDWARFSECT) + s.SetReachable(true) + return s.Sym() + } + mkAnonSym := func(kind sym.SymKind) loader.Sym { + s := d.ldr.MakeSymbolUpdater(d.ldr.CreateExtSym("", 0)) + s.SetType(kind) + s.SetReachable(true) + return s.Sym() + } + + // Create the section symbols. + frameSym := mkSecSym(".debug_frame") + locSym := mkSecSym(".debug_loc") + lineSym := mkSecSym(".debug_line") + rangesSym := mkSecSym(".debug_ranges") + infoSym := mkSecSym(".debug_info") + + // Create the section objects + lineSec := dwarfSecInfo{syms: []loader.Sym{lineSym}} + locSec := dwarfSecInfo{syms: []loader.Sym{locSym}} + rangesSec := dwarfSecInfo{syms: []loader.Sym{rangesSym}} + frameSec := dwarfSecInfo{syms: []loader.Sym{frameSym}} + infoSec := dwarfSecInfo{syms: []loader.Sym{infoSym}} + + // Create any new symbols that will be needed during the + // parallel portion below. + ncu := len(d.linkctxt.compUnits) + unitSyms := make([]dwUnitSyms, ncu) + for i := 0; i < ncu; i++ { + us := &unitSyms[i] + us.lineProlog = mkAnonSym(sym.SDWARFLINES) + us.rangeProlog = mkAnonSym(sym.SDWARFRANGE) + us.infoEpilog = mkAnonSym(sym.SDWARFFCN) + } + + var wg sync.WaitGroup + sema := make(chan struct{}, runtime.GOMAXPROCS(0)) + + // Kick off generation of .debug_frame, since it doesn't have + // any entanglements and can be started right away. + wg.Add(1) + go func() { + sema <- struct{}{} + defer func() { + <-sema + wg.Done() + }() + frameSec = d.writeframes(frameSym) + }() + + // Create a goroutine per comp unit to handle the generation that + // unit's portion of .debug_line, .debug_loc, .debug_ranges, and + // .debug_info. + wg.Add(len(d.linkctxt.compUnits)) + for i := 0; i < ncu; i++ { + go func(u *sym.CompilationUnit, us *dwUnitSyms) { + sema <- struct{}{} + defer func() { + <-sema + wg.Done() + }() + d.dwUnitPortion(u, abbrevSec.secSym(), us) + }(d.linkctxt.compUnits[i], &unitSyms[i]) + } + wg.Wait() + + markReachable := func(syms []loader.Sym) []loader.Sym { + for _, s := range syms { + d.ldr.SetAttrNotInSymbolTable(s, true) + d.ldr.SetAttrReachable(s, true) + } + return syms + } + + // Stitch together the results. + for i := 0; i < ncu; i++ { + r := &unitSyms[i] + lineSec.syms = append(lineSec.syms, markReachable(r.linesyms)...) + infoSec.syms = append(infoSec.syms, markReachable(r.infosyms)...) + locSec.syms = append(locSec.syms, markReachable(r.locsyms)...) + rangesSec.syms = append(rangesSec.syms, markReachable(r.rangessyms)...) + } + dwarfp = append(dwarfp, lineSec) + dwarfp = append(dwarfp, frameSec) + gdbScriptSec := d.writegdbscript() + if gdbScriptSec.secSym() != 0 { + dwarfp = append(dwarfp, gdbScriptSec) + } + dwarfp = append(dwarfp, infoSec) + if len(locSec.syms) > 1 { + dwarfp = append(dwarfp, locSec) + } + dwarfp = append(dwarfp, rangesSec) + + // Check to make sure we haven't listed any symbols more than once + // in the info section. This used to be done by setting and + // checking the OnList attribute in "putdie", but that strategy + // was not friendly for concurrency. + seen := loader.MakeBitmap(d.ldr.NSym()) + for _, s := range infoSec.syms { + if seen.Has(s) { + log.Fatalf("symbol %s listed multiple times", d.ldr.SymName(s)) + } + seen.Set(s) + } +} + +func (d *dwctxt) collectUnitLocs(u *sym.CompilationUnit) []loader.Sym { + syms := []loader.Sym{} + for _, fn := range u.FuncDIEs { + relocs := d.ldr.Relocs(loader.Sym(fn)) + for i := 0; i < relocs.Count(); i++ { + reloc := relocs.At(i) + if reloc.Type() != objabi.R_DWARFSECREF { + continue + } + rsym := reloc.Sym() + if d.ldr.SymType(rsym) == sym.SDWARFLOC { + syms = append(syms, rsym) + // One location list entry per function, but many relocations to it. Don't duplicate. + break + } + } + } + return syms +} + +/* + * Elf. + */ +func dwarfaddshstrings(ctxt *Link, shstrtab *loader.SymbolBuilder) { + if *FlagW { // disable dwarf + return + } + + secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts", "ranges"} + for _, sec := range secs { + shstrtab.Addstring(".debug_" + sec) + if ctxt.IsExternal() { + shstrtab.Addstring(elfRelType + ".debug_" + sec) + } else { + shstrtab.Addstring(".zdebug_" + sec) + } + } +} + +func dwarfaddelfsectionsyms(ctxt *Link) { + if *FlagW { // disable dwarf + return + } + if ctxt.LinkMode != LinkExternal { + return + } + + ldr := ctxt.loader + for _, si := range dwarfp { + s := si.secSym() + sect := ldr.SymSect(si.secSym()) + putelfsectionsym(ctxt, ctxt.Out, s, sect.Elfsect.(*ElfShdr).shnum) + } +} + +// dwarfcompress compresses the DWARF sections. Relocations are applied +// on the fly. After this, dwarfp will contain a different (new) set of +// symbols, and sections may have been replaced. +func dwarfcompress(ctxt *Link) { + // compressedSect is a helper type for parallelizing compression. + type compressedSect struct { + index int + compressed []byte + syms []loader.Sym + } + + supported := ctxt.IsELF || ctxt.IsWindows() || ctxt.IsDarwin() + if !ctxt.compressDWARF || !supported || ctxt.IsExternal() { + return + } + + var compressedCount int + resChannel := make(chan compressedSect) + for i := range dwarfp { + go func(resIndex int, syms []loader.Sym) { + resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms} + }(compressedCount, dwarfp[i].syms) + compressedCount++ + } + res := make([]compressedSect, compressedCount) + for ; compressedCount > 0; compressedCount-- { + r := <-resChannel + res[r.index] = r + } + + ldr := ctxt.loader + var newDwarfp []dwarfSecInfo + Segdwarf.Sections = Segdwarf.Sections[:0] + for _, z := range res { + s := z.syms[0] + if z.compressed == nil { + // Compression didn't help. + ds := dwarfSecInfo{syms: z.syms} + newDwarfp = append(newDwarfp, ds) + Segdwarf.Sections = append(Segdwarf.Sections, ldr.SymSect(s)) + } else { + compressedSegName := ".zdebug_" + ldr.SymSect(s).Name[len(".debug_"):] + sect := addsection(ctxt.loader, ctxt.Arch, &Segdwarf, compressedSegName, 04) + sect.Align = 1 + sect.Length = uint64(len(z.compressed)) + newSym := ldr.CreateSymForUpdate(compressedSegName, 0) + newSym.SetData(z.compressed) + newSym.SetSize(int64(len(z.compressed))) + ldr.SetSymSect(newSym.Sym(), sect) + ds := dwarfSecInfo{syms: []loader.Sym{newSym.Sym()}} + newDwarfp = append(newDwarfp, ds) + + // compressed symbols are no longer needed. + for _, s := range z.syms { + ldr.SetAttrReachable(s, false) + ldr.FreeSym(s) + } + } + } + dwarfp = newDwarfp + + // Re-compute the locations of the compressed DWARF symbols + // and sections, since the layout of these within the file is + // based on Section.Vaddr and Symbol.Value. + pos := Segdwarf.Vaddr + var prevSect *sym.Section + for _, si := range dwarfp { + for _, s := range si.syms { + ldr.SetSymValue(s, int64(pos)) + sect := ldr.SymSect(s) + if sect != prevSect { + sect.Vaddr = uint64(pos) + prevSect = sect + } + if ldr.SubSym(s) != 0 { + log.Fatalf("%s: unexpected sub-symbols", ldr.SymName(s)) + } + pos += uint64(ldr.SymSize(s)) + if ctxt.IsWindows() { + pos = uint64(Rnd(int64(pos), PEFILEALIGN)) + } + } + } + Segdwarf.Length = pos - Segdwarf.Vaddr +} + +type compilationUnitByStartPC []*sym.CompilationUnit + +func (v compilationUnitByStartPC) Len() int { return len(v) } +func (v compilationUnitByStartPC) Swap(i, j int) { v[i], v[j] = v[j], v[i] } + +func (v compilationUnitByStartPC) Less(i, j int) bool { + switch { + case len(v[i].Textp) == 0 && len(v[j].Textp) == 0: + return v[i].Lib.Pkg < v[j].Lib.Pkg + case len(v[i].Textp) != 0 && len(v[j].Textp) == 0: + return true + case len(v[i].Textp) == 0 && len(v[j].Textp) != 0: + return false + default: + return v[i].PCs[0].Start < v[j].PCs[0].Start + } +} + +// getPkgFromCUSym returns the package name for the compilation unit +// represented by s. +// The prefix dwarf.InfoPrefix+".pkg." needs to be removed in order to get +// the package name. +func (d *dwctxt) getPkgFromCUSym(s loader.Sym) string { + return strings.TrimPrefix(d.ldr.SymName(s), dwarf.InfoPrefix+".pkg.") +} + +// On AIX, the symbol table needs to know where are the compilation units parts +// for a specific package in each .dw section. +// dwsectCUSize map will save the size of a compilation unit for +// the corresponding .dw section. +// This size can later be retrieved with the index "sectionName.pkgName". +var dwsectCUSizeMu sync.Mutex +var dwsectCUSize map[string]uint64 + +// getDwsectCUSize retrieves the corresponding package size inside the current section. +func getDwsectCUSize(sname string, pkgname string) uint64 { + return dwsectCUSize[sname+"."+pkgname] +} + +func saveDwsectCUSize(sname string, pkgname string, size uint64) { + dwsectCUSizeMu.Lock() + defer dwsectCUSizeMu.Unlock() + dwsectCUSize[sname+"."+pkgname] = size +} + +func addDwsectCUSize(sname string, pkgname string, size uint64) { + dwsectCUSizeMu.Lock() + defer dwsectCUSizeMu.Unlock() + dwsectCUSize[sname+"."+pkgname] += size +} diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go new file mode 100644 index 0000000..a66506d --- /dev/null +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -0,0 +1,1621 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + intdwarf "cmd/internal/dwarf" + objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function + "debug/dwarf" + "debug/pe" + "errors" + "fmt" + "internal/testenv" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "reflect" + "runtime" + "strconv" + "strings" + "testing" +) + +const ( + DefaultOpt = "-gcflags=" + NoOpt = "-gcflags=-l -N" + OptInl4 = "-gcflags=-l=4" + OptAllInl4 = "-gcflags=all=-l=4" +) + +func TestRuntimeTypesPresent(t *testing.T) { + t.Parallel() + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + dir, err := ioutil.TempDir("", "TestRuntimeTypesPresent") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + f := gobuild(t, dir, `package main; func main() { }`, NoOpt) + defer f.Close() + + dwarf, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + want := map[string]bool{ + "runtime._type": true, + "runtime.arraytype": true, + "runtime.chantype": true, + "runtime.functype": true, + "runtime.maptype": true, + "runtime.ptrtype": true, + "runtime.slicetype": true, + "runtime.structtype": true, + "runtime.interfacetype": true, + "runtime.itab": true, + "runtime.imethod": true, + } + + found := findTypes(t, dwarf, want) + if len(found) != len(want) { + t.Errorf("found %v, want %v", found, want) + } +} + +func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) { + found = make(map[string]bool) + rdr := dw.Reader() + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + switch entry.Tag { + case dwarf.TagTypedef: + if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] { + found[name] = true + } + } + } + return +} + +type builtFile struct { + *objfilepkg.File + path string +} + +func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile { + src := filepath.Join(dir, "test.go") + dst := filepath.Join(dir, "out.exe") + + if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil { + t.Fatal(err) + } + + cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst, src) + if b, err := cmd.CombinedOutput(); err != nil { + t.Logf("build: %s\n", b) + t.Fatalf("build error: %v", err) + } + + f, err := objfilepkg.Open(dst) + if err != nil { + t.Fatal(err) + } + return &builtFile{f, dst} +} + +// Similar to gobuild() above, but uses a main package instead of a test.go file. + +func gobuildTestdata(t *testing.T, tdir string, pkgDir string, gcflags string) *builtFile { + dst := filepath.Join(tdir, "out.exe") + + // Run a build with an updated GOPATH + cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst) + cmd.Dir = pkgDir + if b, err := cmd.CombinedOutput(); err != nil { + t.Logf("build: %s\n", b) + t.Fatalf("build error: %v", err) + } + + f, err := objfilepkg.Open(dst) + if err != nil { + t.Fatal(err) + } + return &builtFile{f, dst} +} + +func TestEmbeddedStructMarker(t *testing.T) { + t.Parallel() + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + const prog = ` +package main + +import "fmt" + +type Foo struct { v int } +type Bar struct { + Foo + name string +} +type Baz struct { + *Foo + name string +} + +func main() { + bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"} + baz := Baz{ Foo: &bar.Foo, name: "123" } + fmt.Println(bar, baz) +}` + + want := map[string]map[string]bool{ + "main.Foo": {"v": false}, + "main.Bar": {"Foo": true, "name": false}, + "main.Baz": {"Foo": true, "name": false}, + } + + dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + f := gobuild(t, dir, prog, NoOpt) + + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + switch entry.Tag { + case dwarf.TagStructType: + name := entry.Val(dwarf.AttrName).(string) + wantMembers := want[name] + if wantMembers == nil { + continue + } + gotMembers, err := findMembers(rdr) + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + if !reflect.DeepEqual(gotMembers, wantMembers) { + t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers) + } + delete(want, name) + } + } + if len(want) != 0 { + t.Errorf("failed to check all expected types: missing types = %+v", want) + } +} + +func findMembers(rdr *dwarf.Reader) (map[string]bool, error) { + memberEmbedded := map[string]bool{} + // TODO(hyangah): define in debug/dwarf package + const goEmbeddedStruct = dwarf.Attr(intdwarf.DW_AT_go_embedded_field) + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + return nil, err + } + switch entry.Tag { + case dwarf.TagMember: + name := entry.Val(dwarf.AttrName).(string) + embedded := entry.Val(goEmbeddedStruct).(bool) + memberEmbedded[name] = embedded + case 0: + return memberEmbedded, nil + } + } + return memberEmbedded, nil +} + +func TestSizes(t *testing.T) { + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + // External linking may bring in C symbols with unknown size. Skip. + testenv.MustInternalLink(t) + + t.Parallel() + + // DWARF sizes should never be -1. + // See issue #21097 + const prog = ` +package main +var x func() +var y [4]func() +func main() { + x = nil + y[0] = nil +} +` + dir, err := ioutil.TempDir("", "TestSizes") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + f := gobuild(t, dir, prog, NoOpt) + defer f.Close() + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + rdr := d.Reader() + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + switch entry.Tag { + case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef: + default: + continue + } + typ, err := d.Type(entry.Offset) + if err != nil { + t.Fatalf("can't read type: %v", err) + } + if typ.Size() < 0 { + t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ) + } + } +} + +func TestFieldOverlap(t *testing.T) { + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + t.Parallel() + + // This test grew out of issue 21094, where specific sudog<T> DWARF types + // had elem fields set to values instead of pointers. + const prog = ` +package main + +var c chan string + +func main() { + c <- "foo" +} +` + dir, err := ioutil.TempDir("", "TestFieldOverlap") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + f := gobuild(t, dir, prog, NoOpt) + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if entry.Tag != dwarf.TagStructType { + continue + } + typ, err := d.Type(entry.Offset) + if err != nil { + t.Fatalf("can't read type: %v", err) + } + s := typ.(*dwarf.StructType) + for i := 0; i < len(s.Field); i++ { + end := s.Field[i].ByteOffset + s.Field[i].Type.Size() + var limit int64 + if i == len(s.Field)-1 { + limit = s.Size() + } else { + limit = s.Field[i+1].ByteOffset + } + if end > limit { + name := entry.Val(dwarf.AttrName).(string) + t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name) + } + } + } +} + +func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFile string, expectLine int, directive string) { + t.Parallel() + + prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive) + + dir, err := ioutil.TempDir("", testpoint) + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + f := gobuild(t, dir, prog, NoOpt) + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + ex := examiner{} + if err := ex.populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Locate the main.main DIE + mains := ex.Named("main.main") + if len(mains) == 0 { + t.Fatalf("unable to locate DIE for main.main") + } + if len(mains) != 1 { + t.Fatalf("more than one main.main DIE") + } + maindie := mains[0] + + // Vet the main.main DIE + if maindie.Tag != dwarf.TagSubprogram { + t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag) + } + + // Walk main's children and select variable "i". + mainIdx := ex.idxFromOffset(maindie.Offset) + childDies := ex.Children(mainIdx) + var iEntry *dwarf.Entry + for _, child := range childDies { + if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" { + iEntry = child + break + } + } + if iEntry == nil { + t.Fatalf("didn't find DW_TAG_variable for i in main.main") + } + + // Verify line/file attributes. + line := iEntry.Val(dwarf.AttrDeclLine) + if line == nil || line.(int64) != int64(expectLine) { + t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine) + } + + fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64) + if !fileIdxOK { + t.Errorf("missing or invalid DW_AT_decl_file for main") + } + file := ex.FileRef(t, d, mainIdx, fileIdx) + base := filepath.Base(file) + if base != expectFile { + t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile) + } +} + +func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoords", "test.go", 5, "") +} + +func TestVarDeclCoordsWithLineDirective(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoordsWithLineDirective", + "foobar.go", 202, "//line /foobar.go:200") +} + +// Helper class for supporting queries on DIEs within a DWARF .debug_info +// section. Invoke the populate() method below passing in a dwarf.Reader, +// which will read in all DIEs and keep track of parent/child +// relationships. Queries can then be made to ask for DIEs by name or +// by offset. This will hopefully reduce boilerplate for future test +// writing. + +type examiner struct { + dies []*dwarf.Entry + idxByOffset map[dwarf.Offset]int + kids map[int][]int + parent map[int]int + byname map[string][]int +} + +// Populate the examiner using the DIEs read from rdr. +func (ex *examiner) populate(rdr *dwarf.Reader) error { + ex.idxByOffset = make(map[dwarf.Offset]int) + ex.kids = make(map[int][]int) + ex.parent = make(map[int]int) + ex.byname = make(map[string][]int) + var nesting []int + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + return err + } + if entry.Tag == 0 { + // terminator + if len(nesting) == 0 { + return errors.New("nesting stack underflow") + } + nesting = nesting[:len(nesting)-1] + continue + } + idx := len(ex.dies) + ex.dies = append(ex.dies, entry) + if _, found := ex.idxByOffset[entry.Offset]; found { + return errors.New("DIE clash on offset") + } + ex.idxByOffset[entry.Offset] = idx + if name, ok := entry.Val(dwarf.AttrName).(string); ok { + ex.byname[name] = append(ex.byname[name], idx) + } + if len(nesting) > 0 { + parent := nesting[len(nesting)-1] + ex.kids[parent] = append(ex.kids[parent], idx) + ex.parent[idx] = parent + } + if entry.Children { + nesting = append(nesting, idx) + } + } + if len(nesting) > 0 { + return errors.New("unterminated child sequence") + } + return nil +} + +func indent(ilevel int) { + for i := 0; i < ilevel; i++ { + fmt.Printf(" ") + } +} + +// For debugging new tests +func (ex *examiner) dumpEntry(idx int, dumpKids bool, ilevel int) error { + if idx >= len(ex.dies) { + msg := fmt.Sprintf("bad DIE %d: index out of range\n", idx) + return errors.New(msg) + } + entry := ex.dies[idx] + indent(ilevel) + fmt.Printf("0x%x: %v\n", idx, entry.Tag) + for _, f := range entry.Field { + indent(ilevel) + fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val) + } + if dumpKids { + ksl := ex.kids[idx] + for _, k := range ksl { + ex.dumpEntry(k, true, ilevel+2) + } + } + return nil +} + +// Given a DIE offset, return the previously read dwarf.Entry, or nil +func (ex *examiner) entryFromOffset(off dwarf.Offset) *dwarf.Entry { + if idx, found := ex.idxByOffset[off]; found && idx != -1 { + return ex.entryFromIdx(idx) + } + return nil +} + +// Return the ID that examiner uses to refer to the DIE at offset off +func (ex *examiner) idxFromOffset(off dwarf.Offset) int { + if idx, found := ex.idxByOffset[off]; found { + return idx + } + return -1 +} + +// Return the dwarf.Entry pointer for the DIE with id 'idx' +func (ex *examiner) entryFromIdx(idx int) *dwarf.Entry { + if idx >= len(ex.dies) || idx < 0 { + return nil + } + return ex.dies[idx] +} + +// Returns a list of child entries for a die with ID 'idx' +func (ex *examiner) Children(idx int) []*dwarf.Entry { + sl := ex.kids[idx] + ret := make([]*dwarf.Entry, len(sl)) + for i, k := range sl { + ret[i] = ex.entryFromIdx(k) + } + return ret +} + +// Returns parent DIE for DIE 'idx', or nil if the DIE is top level +func (ex *examiner) Parent(idx int) *dwarf.Entry { + p, found := ex.parent[idx] + if !found { + return nil + } + return ex.entryFromIdx(p) +} + +// ParentCU returns the enclosing compilation unit DIE for the DIE +// with a given index, or nil if for some reason we can't establish a +// parent. +func (ex *examiner) ParentCU(idx int) *dwarf.Entry { + for { + parentDie := ex.Parent(idx) + if parentDie == nil { + return nil + } + if parentDie.Tag == dwarf.TagCompileUnit { + return parentDie + } + idx = ex.idxFromOffset(parentDie.Offset) + } +} + +// FileRef takes a given DIE by index and a numeric file reference +// (presumably from a decl_file or call_file attribute), looks up the +// reference in the .debug_line file table, and returns the proper +// string for it. We need to know which DIE is making the reference +// so as find the right compilation unit. +func (ex *examiner) FileRef(t *testing.T, dw *dwarf.Data, dieIdx int, fileRef int64) string { + + // Find the parent compilation unit DIE for the specified DIE. + cuDie := ex.ParentCU(dieIdx) + if cuDie == nil { + t.Fatalf("no parent CU DIE for DIE with idx %d?", dieIdx) + return "" + } + // Construct a line reader and then use it to get the file string. + lr, lrerr := dw.LineReader(cuDie) + if lrerr != nil { + t.Fatal("d.LineReader: ", lrerr) + return "" + } + files := lr.Files() + if fileRef < 0 || int(fileRef) > len(files)-1 { + t.Fatalf("examiner.FileRef: malformed file reference %d", fileRef) + return "" + } + return files[fileRef].Name +} + +// Return a list of all DIEs with name 'name'. When searching for DIEs +// by name, keep in mind that the returned results will include child +// DIEs such as params/variables. For example, asking for all DIEs named +// "p" for even a small program will give you 400-500 entries. +func (ex *examiner) Named(name string) []*dwarf.Entry { + sl := ex.byname[name] + ret := make([]*dwarf.Entry, len(sl)) + for i, k := range sl { + ret[i] = ex.entryFromIdx(k) + } + return ret +} + +func TestInlinedRoutineRecords(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" { + t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168") + } + + t.Parallel() + + const prog = ` +package main + +var G int + +func noinline(x int) int { + defer func() { G += x }() + return x +} + +func cand(x, y int) int { + return noinline(x+y) ^ (y - x) +} + +func main() { + x := cand(G*G,G|7%G) + G = x +} +` + dir, err := ioutil.TempDir("", "TestInlinedRoutineRecords") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + // Note: this is a build with "-l=4", as opposed to "-l -N". The + // test is intended to verify DWARF that is only generated when + // the inliner is active. We're only going to look at the DWARF for + // main.main, however, hence we build with "-gcflags=-l=4" as opposed + // to "-gcflags=all=-l=4". + f := gobuild(t, dir, prog, OptInl4) + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // The inlined subroutines we expect to visit + expectedInl := []string{"main.cand"} + + rdr := d.Reader() + ex := examiner{} + if err := ex.populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Locate the main.main DIE + mains := ex.Named("main.main") + if len(mains) == 0 { + t.Fatalf("unable to locate DIE for main.main") + } + if len(mains) != 1 { + t.Fatalf("more than one main.main DIE") + } + maindie := mains[0] + + // Vet the main.main DIE + if maindie.Tag != dwarf.TagSubprogram { + t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag) + } + + // Walk main's children and pick out the inlined subroutines + mainIdx := ex.idxFromOffset(maindie.Offset) + childDies := ex.Children(mainIdx) + exCount := 0 + for _, child := range childDies { + if child.Tag == dwarf.TagInlinedSubroutine { + // Found an inlined subroutine, locate abstract origin. + ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !originOK { + t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset) + } + originDIE := ex.entryFromOffset(ooff) + if originDIE == nil { + t.Fatalf("can't locate origin DIE at off %v", ooff) + } + + // Walk the children of the abstract subroutine. We expect + // to see child variables there, even if (perhaps due to + // optimization) there are no references to them from the + // inlined subroutine DIE. + absFcnIdx := ex.idxFromOffset(ooff) + absFcnChildDies := ex.Children(absFcnIdx) + if len(absFcnChildDies) != 2 { + t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies)) + } + formalCount := 0 + for _, absChild := range absFcnChildDies { + if absChild.Tag == dwarf.TagFormalParameter { + formalCount += 1 + continue + } + t.Fatalf("abstract function child DIE: expected formal, got %v", absChild.Tag) + } + if formalCount != 2 { + t.Fatalf("abstract function DIE: expected 2 formals, got %d", formalCount) + } + + if exCount >= len(expectedInl) { + t.Fatalf("too many inlined subroutines found in main.main") + } + + // Name should check out. + expected := expectedInl[exCount] + if name, ok := originDIE.Val(dwarf.AttrName).(string); ok { + if name != expected { + t.Fatalf("expected inlined routine %s got %s", name, expected) + } + } + exCount++ + + // Verify that the call_file attribute for the inlined + // instance is ok. In this case it should match the file + // for the main routine. To do this we need to locate the + // compilation unit DIE that encloses what we're looking + // at; this can be done with the examiner. + cf, cfOK := child.Val(dwarf.AttrCallFile).(int64) + if !cfOK { + t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset) + } + file := ex.FileRef(t, d, mainIdx, cf) + base := filepath.Base(file) + if base != "test.go" { + t.Errorf("bad call_file attribute, found '%s', want '%s'", + file, "test.go") + } + + omap := make(map[dwarf.Offset]bool) + + // Walk the child variables of the inlined routine. Each + // of them should have a distinct abstract origin-- if two + // vars point to the same origin things are definitely broken. + inlIdx := ex.idxFromOffset(child.Offset) + inlChildDies := ex.Children(inlIdx) + for _, k := range inlChildDies { + ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !originOK { + t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset) + } + if _, found := omap[ooff]; found { + t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset) + } + omap[ooff] = true + } + } + } + if exCount != len(expectedInl) { + t.Fatalf("not enough inlined subroutines found in main.main") + } +} + +func abstractOriginSanity(t *testing.T, pkgDir string, flags string) { + t.Parallel() + + dir, err := ioutil.TempDir("", "TestAbstractOriginSanity") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + // Build with inlining, to exercise DWARF inlining support. + f := gobuildTestdata(t, dir, filepath.Join(pkgDir, "main"), flags) + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + rdr := d.Reader() + ex := examiner{} + if err := ex.populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Make a pass through all DIEs looking for abstract origin + // references. + abscount := 0 + for i, die := range ex.dies { + // Does it have an abstract origin? + ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !originOK { + continue + } + + // All abstract origin references should be resolvable. + abscount += 1 + originDIE := ex.entryFromOffset(ooff) + if originDIE == nil { + ex.dumpEntry(i, false, 0) + t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset) + } + + // Suppose that DIE X has parameter/variable children {K1, + // K2, ... KN}. If X has an abstract origin of A, then for + // each KJ, the abstract origin of KJ should be a child of A. + // Note that this same rule doesn't hold for non-variable DIEs. + pidx := ex.idxFromOffset(die.Offset) + if pidx < 0 { + t.Fatalf("can't locate DIE id") + } + kids := ex.Children(pidx) + for _, kid := range kids { + if kid.Tag != dwarf.TagVariable && + kid.Tag != dwarf.TagFormalParameter { + continue + } + kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !originOK { + continue + } + childOriginDIE := ex.entryFromOffset(kooff) + if childOriginDIE == nil { + ex.dumpEntry(i, false, 0) + t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset) + } + coidx := ex.idxFromOffset(childOriginDIE.Offset) + childOriginParent := ex.Parent(coidx) + if childOriginParent != originDIE { + ex.dumpEntry(i, false, 0) + t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset) + } + } + } + if abscount == 0 { + t.Fatalf("no abstract origin refs found, something is wrong") + } +} + +func TestAbstractOriginSanity(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" { + t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168") + } + + if wd, err := os.Getwd(); err == nil { + gopathdir := filepath.Join(wd, "testdata", "httptest") + abstractOriginSanity(t, gopathdir, OptAllInl4) + } else { + t.Fatalf("os.Getwd() failed %v", err) + } +} + +func TestAbstractOriginSanityIssue25459(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" { + t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168") + } + if runtime.GOARCH != "amd64" && runtime.GOARCH != "x86" { + t.Skip("skipping on not-amd64 not-x86; location lists not supported") + } + + if wd, err := os.Getwd(); err == nil { + gopathdir := filepath.Join(wd, "testdata", "issue25459") + abstractOriginSanity(t, gopathdir, DefaultOpt) + } else { + t.Fatalf("os.Getwd() failed %v", err) + } +} + +func TestAbstractOriginSanityIssue26237(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" { + t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168") + } + if wd, err := os.Getwd(); err == nil { + gopathdir := filepath.Join(wd, "testdata", "issue26237") + abstractOriginSanity(t, gopathdir, DefaultOpt) + } else { + t.Fatalf("os.Getwd() failed %v", err) + } +} + +func TestRuntimeTypeAttrInternal(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustInternalLink(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + if runtime.GOOS == "windows" { + t.Skip("skipping on windows; test is incompatible with relocatable binaries") + } + + testRuntimeTypeAttr(t, "-ldflags=-linkmode=internal") +} + +// External linking requires a host linker (https://golang.org/src/cmd/cgo/doc.go l.732) +func TestRuntimeTypeAttrExternal(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + // Explicitly test external linking, for dsymutil compatibility on Darwin. + if runtime.GOARCH == "ppc64" { + t.Skip("-linkmode=external not supported on ppc64") + } + + if runtime.GOOS == "windows" { + t.Skip("skipping on windows; test is incompatible with relocatable binaries") + } + + testRuntimeTypeAttr(t, "-ldflags=-linkmode=external") +} + +func testRuntimeTypeAttr(t *testing.T, flags string) { + t.Parallel() + + const prog = ` +package main + +import "unsafe" + +type X struct{ _ int } + +func main() { + var x interface{} = &X{} + p := *(*uintptr)(unsafe.Pointer(&x)) + print(p) +} +` + dir, err := ioutil.TempDir("", "TestRuntimeType") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + f := gobuild(t, dir, prog, flags) + out, err := exec.Command(f.path).CombinedOutput() + if err != nil { + t.Fatalf("could not run test program: %v", err) + } + addr, err := strconv.ParseUint(string(out), 10, 64) + if err != nil { + t.Fatalf("could not parse type address from program output %q: %v", out, err) + } + + symbols, err := f.Symbols() + if err != nil { + t.Fatalf("error reading symbols: %v", err) + } + var types *objfilepkg.Sym + for _, sym := range symbols { + if sym.Name == "runtime.types" { + types = &sym + break + } + } + if types == nil { + t.Fatal("couldn't find runtime.types in symbols") + } + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + ex := examiner{} + if err := ex.populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + dies := ex.Named("*main.X") + if len(dies) != 1 { + t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies)) + } + rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type) + if rtAttr == nil { + t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0]) + } + + if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { + return // everything is PIE on ARM64, addresses are relocated + } + if rtAttr.(uint64)+types.Addr != addr { + t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr) + } +} + +func TestIssue27614(t *testing.T) { + // Type references in debug_info should always use the DW_TAG_typedef_type + // for the type, when that's generated. + + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + const prog = `package main + +import "fmt" + +type astruct struct { + X int +} + +type bstruct struct { + X float32 +} + +var globalptr *astruct +var globalvar astruct +var bvar0, bvar1, bvar2 bstruct + +func main() { + fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2) +} +` + + f := gobuild(t, dir, prog, NoOpt) + + defer f.Close() + + data, err := f.DWARF() + if err != nil { + t.Fatal(err) + } + + rdr := data.Reader() + + var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry + var globalptrDIE, globalvarDIE *dwarf.Entry + var bvarDIE [3]*dwarf.Entry + + for { + e, err := rdr.Next() + if err != nil { + t.Fatal(err) + } + if e == nil { + break + } + + name, _ := e.Val(dwarf.AttrName).(string) + + switch e.Tag { + case dwarf.TagTypedef: + switch name { + case "main.astruct": + astructTypeDIE = e + case "main.bstruct": + bstructTypeDIE = e + } + case dwarf.TagPointerType: + if name == "*main.astruct" { + ptrastructTypeDIE = e + } + case dwarf.TagVariable: + switch name { + case "main.globalptr": + globalptrDIE = e + case "main.globalvar": + globalvarDIE = e + default: + const bvarprefix = "main.bvar" + if strings.HasPrefix(name, bvarprefix) { + i, _ := strconv.Atoi(name[len(bvarprefix):]) + bvarDIE[i] = e + } + } + } + } + + typedieof := func(e *dwarf.Entry) dwarf.Offset { + return e.Val(dwarf.AttrType).(dwarf.Offset) + } + + if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset { + t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset) + } + + if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset { + t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset) + } + + if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset { + t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset) + } + + for i := range bvarDIE { + if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset { + t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset) + } + } +} + +func TestStaticTmp(t *testing.T) { + // Checks that statictmp variables do not appear in debug_info or the + // symbol table. + // Also checks that statictmp variables do not collide with user defined + // variables (issue #25113) + + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + const prog = `package main + +var stmp_0 string +var a []int + +func init() { + a = []int{ 7 } +} + +func main() { + println(a[0]) +} +` + + f := gobuild(t, dir, prog, NoOpt) + + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + for { + e, err := rdr.Next() + if err != nil { + t.Fatal(err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagVariable { + continue + } + name, ok := e.Val(dwarf.AttrName).(string) + if !ok { + continue + } + if strings.Contains(name, "stmp") { + t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset) + } + } + + // When external linking, we put all symbols in the symbol table (so the + // external linker can find them). Skip the symbol table check. + // TODO: maybe there is some way to tell the external linker not to put + // those symbols in the executable's symbol table? Prefix the symbol name + // with "." or "L" to pretend it is a label? + if !testenv.CanInternalLink() { + return + } + + syms, err := f.Symbols() + if err != nil { + t.Fatalf("error reading symbols: %v", err) + } + for _, sym := range syms { + if strings.Contains(sym.Name, "stmp") { + t.Errorf("statictmp variable found in symbol table: %s", sym.Name) + } + } +} + +func TestPackageNameAttr(t *testing.T) { + const dwarfAttrGoPackageName = dwarf.Attr(0x2905) + const dwarfGoLanguage = 22 + + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n" + + f := gobuild(t, dir, prog, NoOpt) + + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + runtimeUnitSeen := false + for { + e, err := rdr.Next() + if err != nil { + t.Fatal(err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagCompileUnit { + continue + } + if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage { + continue + } + + pn, ok := e.Val(dwarfAttrGoPackageName).(string) + if !ok { + name, _ := e.Val(dwarf.AttrName).(string) + t.Errorf("found compile unit without package name: %s", name) + + } + if pn == "" { + name, _ := e.Val(dwarf.AttrName).(string) + t.Errorf("found compile unit with empty package name: %s", name) + } else { + if pn == "runtime" { + runtimeUnitSeen = true + } + } + } + + // Something is wrong if there's no runtime compilation unit. + if !runtimeUnitSeen { + t.Errorf("no package name for runtime unit") + } +} + +func TestMachoIssue32233(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + if runtime.GOOS != "darwin" { + t.Skip("skipping; test only interesting on darwin") + } + + tmpdir, err := ioutil.TempDir("", "TestMachoIssue32233") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(tmpdir) + + wd, err2 := os.Getwd() + if err2 != nil { + t.Fatalf("where am I? %v", err) + } + pdir := filepath.Join(wd, "testdata", "issue32233", "main") + f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt) + f.Close() +} + +func TestWindowsIssue36495(t *testing.T) { + testenv.MustHaveGoBuild(t) + if runtime.GOOS != "windows" { + t.Skip("skipping: test only on windows") + } + + dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + prog := ` +package main + +import "fmt" + +func main() { + fmt.Println("Hello World") +}` + f := gobuild(t, dir, prog, NoOpt) + exe, err := pe.Open(f.path) + if err != nil { + t.Fatalf("error opening pe file: %v", err) + } + dw, err := exe.DWARF() + if err != nil { + t.Fatalf("error parsing DWARF: %v", err) + } + rdr := dw.Reader() + for { + e, err := rdr.Next() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagCompileUnit { + continue + } + lnrdr, err := dw.LineReader(e) + if err != nil { + t.Fatalf("error creating DWARF line reader: %v", err) + } + if lnrdr != nil { + var lne dwarf.LineEntry + for { + err := lnrdr.Next(&lne) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("error reading next DWARF line: %v", err) + } + if strings.Contains(lne.File.Name, `\`) { + t.Errorf("filename should not contain backslash: %v", lne.File.Name) + } + } + } + rdr.SkipChildren() + } +} + +func TestIssue38192(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + // Build a test program that contains a translation unit whose + // text (from am assembly source) contains only a single instruction. + tmpdir, err := ioutil.TempDir("", "TestIssue38192") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(tmpdir) + wd, err := os.Getwd() + if err != nil { + t.Fatalf("where am I? %v", err) + } + pdir := filepath.Join(wd, "testdata", "issue38192") + f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt) + + // Open the resulting binary and examine the DWARF it contains. + // Look for the function of interest ("main.singleInstruction") + // and verify that the line table has an entry not just for the + // single instruction but also a dummy instruction following it, + // so as to test that whoever is emitting the DWARF doesn't + // emit an end-sequence op immediately after the last instruction + // in the translation unit. + // + // NB: another way to write this test would have been to run the + // resulting executable under GDB, set a breakpoint in + // "main.singleInstruction", then verify that GDB displays the + // correct line/file information. Given the headache and flakiness + // associated with GDB-based tests these days, a direct read of + // the line table seems more desirable. + rows := []dwarf.LineEntry{} + dw, err := f.DWARF() + if err != nil { + t.Fatalf("error parsing DWARF: %v", err) + } + rdr := dw.Reader() + for { + e, err := rdr.Next() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagCompileUnit { + continue + } + // NB: there can be multiple compile units named "main". + name := e.Val(dwarf.AttrName).(string) + if name != "main" { + continue + } + lnrdr, err := dw.LineReader(e) + if err != nil { + t.Fatalf("error creating DWARF line reader: %v", err) + } + if lnrdr != nil { + var lne dwarf.LineEntry + for { + err := lnrdr.Next(&lne) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("error reading next DWARF line: %v", err) + } + if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") { + continue + } + rows = append(rows, lne) + } + } + rdr.SkipChildren() + } + f.Close() + + // Make sure that: + // - main.singleInstruction appears in the line table + // - more than one PC value appears the line table for + // that compilation unit. + // - at least one row has the correct line number (8) + pcs := make(map[uint64]bool) + line8seen := false + for _, r := range rows { + pcs[r.Address] = true + if r.Line == 8 { + line8seen = true + } + } + failed := false + if len(pcs) < 2 { + failed = true + t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs)) + } + if !line8seen { + failed = true + t.Errorf("line table does not contain correct line for main.singleInstruction") + } + if !failed { + return + } + for i, r := range rows { + t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line) + } +} + +func TestIssue39757(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + t.Parallel() + + // In this bug the DWARF line table contents for the last couple of + // instructions in a function were incorrect (bad file/line). This + // test verifies that all of the line table rows for a function + // of interest have the same file (no "autogenerated"). + // + // Note: the function in this test was written with an eye towards + // ensuring that there are no inlined routines from other packages + // (which could introduce other source files into the DWARF); it's + // possible that at some point things could evolve in the + // compiler/runtime in ways that aren't happening now, so this + // might be something to check for if it does start failing. + + tmpdir, err := ioutil.TempDir("", "TestIssue38192") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(tmpdir) + wd, err := os.Getwd() + if err != nil { + t.Fatalf("where am I? %v", err) + } + pdir := filepath.Join(wd, "testdata", "issue39757") + f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt) + + syms, err := f.Symbols() + if err != nil { + t.Fatal(err) + } + + var addr uint64 + for _, sym := range syms { + if sym.Name == "main.main" { + addr = sym.Addr + break + } + } + if addr == 0 { + t.Fatal("cannot find main.main in symbols") + } + + // Open the resulting binary and examine the DWARF it contains. + // Look for the function of interest ("main.main") + // and verify that all line table entries show the same source + // file. + dw, err := f.DWARF() + if err != nil { + t.Fatalf("error parsing DWARF: %v", err) + } + rdr := dw.Reader() + ex := examiner{} + if err := ex.populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + // Locate the main.main DIE + mains := ex.Named("main.main") + if len(mains) == 0 { + t.Fatalf("unable to locate DIE for main.main") + } + if len(mains) != 1 { + t.Fatalf("more than one main.main DIE") + } + maindie := mains[0] + + // Collect the start/end PC for main.main + lowpc := maindie.Val(dwarf.AttrLowpc).(uint64) + highpc := maindie.Val(dwarf.AttrHighpc).(uint64) + + // Now read the line table for the 'main' compilation unit. + mainIdx := ex.idxFromOffset(maindie.Offset) + cuentry := ex.Parent(mainIdx) + if cuentry == nil { + t.Fatalf("main.main DIE appears orphaned") + } + lnrdr, lerr := dw.LineReader(cuentry) + if lerr != nil { + t.Fatalf("error creating DWARF line reader: %v", err) + } + if lnrdr == nil { + t.Fatalf("no line table for main.main compilation unit") + } + rows := []dwarf.LineEntry{} + mainrows := 0 + var lne dwarf.LineEntry + for { + err := lnrdr.Next(&lne) + if err == io.EOF { + break + } + rows = append(rows, lne) + if err != nil { + t.Fatalf("error reading next DWARF line: %v", err) + } + if lne.Address < lowpc || lne.Address > highpc { + continue + } + if !strings.HasSuffix(lne.File.Name, "issue39757main.go") { + t.Errorf("found row with file=%s (not issue39757main.go)", lne.File.Name) + } + mainrows++ + } + f.Close() + + // Make sure we saw a few rows. + if mainrows < 3 { + t.Errorf("not enough line table rows for main.main (got %d, wanted > 3", mainrows) + for i, r := range rows { + t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line) + } + } +} diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go new file mode 100644 index 0000000..f5823a8 --- /dev/null +++ b/src/cmd/link/internal/ld/elf.go @@ -0,0 +1,2140 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "crypto/sha1" + "debug/elf" + "encoding/binary" + "encoding/hex" + "fmt" + "path/filepath" + "sort" + "strings" +) + +/* + * Derived from: + * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $ + * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $ + * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $ + * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ + * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ + * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ + * + * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. + * Copyright (c) 2001 David E. O'Brien + * Portions Copyright 2009 The Go Authors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * ELF definitions that are independent of architecture or word size. + */ + +/* + * Note header. The ".note" section contains an array of notes. Each + * begins with this header, aligned to a word boundary. Immediately + * following the note header is n_namesz bytes of name, padded to the + * next word boundary. Then comes n_descsz bytes of descriptor, again + * padded to a word boundary. The values of n_namesz and n_descsz do + * not include the padding. + */ +type elfNote struct { + nNamesz uint32 + nDescsz uint32 + nType uint32 +} + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Relocation types. + */ +const ( + ARM_MAGIC_TRAMP_NUMBER = 0x5c000003 +) + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * ELF header. + */ +type ElfEhdr elf.Header64 + +/* + * Section header. + */ +type ElfShdr struct { + elf.Section64 + shnum elf.SectionIndex +} + +/* + * Program header. + */ +type ElfPhdr elf.ProgHeader + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * Go linker interface + */ +const ( + ELF64HDRSIZE = 64 + ELF64PHDRSIZE = 56 + ELF64SHDRSIZE = 64 + ELF64RELSIZE = 16 + ELF64RELASIZE = 24 + ELF64SYMSIZE = 24 + ELF32HDRSIZE = 52 + ELF32PHDRSIZE = 32 + ELF32SHDRSIZE = 40 + ELF32SYMSIZE = 16 + ELF32RELSIZE = 8 +) + +/* + * The interface uses the 64-bit structures always, + * to avoid code duplication. The writers know how to + * marshal a 32-bit representation from the 64-bit structure. + */ + +var Elfstrdat []byte + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, SHeaders, and interp. + * May waste some. + * On FreeBSD, cannot be larger than a page. + */ +const ( + ELFRESERVE = 4096 +) + +/* + * We use the 64-bit data structures on both 32- and 64-bit machines + * in order to write the code just once. The 64-bit data structure is + * written in the 32-bit format on the 32-bit machines. + */ +const ( + NSECT = 400 +) + +var ( + Nelfsym = 1 + + elf64 bool + // Either ".rel" or ".rela" depending on which type of relocation the + // target platform uses. + elfRelType string + + ehdr ElfEhdr + phdr [NSECT]*ElfPhdr + shdr [NSECT]*ElfShdr + + interp string +) + +type Elfstring struct { + s string + off int +} + +var elfstr [100]Elfstring + +var nelfstr int + +var buildinfo []byte + +/* + Initialize the global variable that describes the ELF header. It will be updated as + we write section and prog headers. +*/ +func Elfinit(ctxt *Link) { + ctxt.IsELF = true + + if ctxt.Arch.InFamily(sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) { + elfRelType = ".rela" + } else { + elfRelType = ".rel" + } + + switch ctxt.Arch.Family { + // 64-bit architectures + case sys.PPC64, sys.S390X: + if ctxt.Arch.ByteOrder == binary.BigEndian { + ehdr.Flags = 1 /* Version 1 ABI */ + } else { + ehdr.Flags = 2 /* Version 2 ABI */ + } + fallthrough + case sys.AMD64, sys.ARM64, sys.MIPS64, sys.RISCV64: + if ctxt.Arch.Family == sys.MIPS64 { + 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 */ + + // 32-bit architectures + case sys.ARM, sys.MIPS: + if ctxt.Arch.Family == sys.ARM { + // we use EABI on linux/arm, freebsd/arm, netbsd/arm. + if ctxt.HeadType == objabi.Hlinux || ctxt.HeadType == objabi.Hfreebsd || ctxt.HeadType == objabi.Hnetbsd { + // We set a value here that makes no indication of which + // float ABI the object uses, because this is information + // used by the dynamic linker to compare executables and + // shared libraries -- so it only matters for cgo calls, and + // the information properly comes from the object files + // produced by the host C compiler. parseArmAttributes in + // ldelf.go reads that information and updates this field as + // appropriate. + ehdr.Flags = 0x5000002 // has entry point, Version5 EABI + } + } else if ctxt.Arch.Family == sys.MIPS { + ehdr.Flags = 0x50001004 /* MIPS 32 CPIC O32*/ + } + fallthrough + default: + ehdr.Phoff = ELF32HDRSIZE + /* Must be ELF32HDRSIZE: first PHdr must follow ELF header */ + ehdr.Shoff = ELF32HDRSIZE /* Will move as we add PHeaders */ + ehdr.Ehsize = ELF32HDRSIZE /* Must be ELF32HDRSIZE */ + ehdr.Phentsize = ELF32PHDRSIZE /* Must be ELF32PHDRSIZE */ + ehdr.Shentsize = ELF32SHDRSIZE /* Must be ELF32SHDRSIZE */ + } +} + +// Make sure PT_LOAD is aligned properly and +// that there is no gap, +// correct ELF loaders will do this implicitly, +// but buggy ELF loaders like the one in some +// versions of QEMU and UPX won't. +func fixElfPhdr(e *ElfPhdr) { + frag := int(e.Vaddr & (e.Align - 1)) + + e.Off -= uint64(frag) + e.Vaddr -= uint64(frag) + e.Paddr -= uint64(frag) + e.Filesz += uint64(frag) + e.Memsz += uint64(frag) +} + +func elf64phdr(out *OutBuf, e *ElfPhdr) { + if e.Type == elf.PT_LOAD { + fixElfPhdr(e) + } + + out.Write32(uint32(e.Type)) + out.Write32(uint32(e.Flags)) + out.Write64(e.Off) + out.Write64(e.Vaddr) + out.Write64(e.Paddr) + out.Write64(e.Filesz) + out.Write64(e.Memsz) + out.Write64(e.Align) +} + +func elf32phdr(out *OutBuf, e *ElfPhdr) { + if e.Type == elf.PT_LOAD { + fixElfPhdr(e) + } + + out.Write32(uint32(e.Type)) + out.Write32(uint32(e.Off)) + out.Write32(uint32(e.Vaddr)) + out.Write32(uint32(e.Paddr)) + out.Write32(uint32(e.Filesz)) + out.Write32(uint32(e.Memsz)) + out.Write32(uint32(e.Flags)) + out.Write32(uint32(e.Align)) +} + +func elf64shdr(out *OutBuf, e *ElfShdr) { + out.Write32(e.Name) + out.Write32(uint32(e.Type)) + out.Write64(uint64(e.Flags)) + out.Write64(e.Addr) + out.Write64(e.Off) + out.Write64(e.Size) + out.Write32(e.Link) + out.Write32(e.Info) + out.Write64(e.Addralign) + out.Write64(e.Entsize) +} + +func elf32shdr(out *OutBuf, e *ElfShdr) { + out.Write32(e.Name) + out.Write32(uint32(e.Type)) + out.Write32(uint32(e.Flags)) + out.Write32(uint32(e.Addr)) + out.Write32(uint32(e.Off)) + out.Write32(uint32(e.Size)) + out.Write32(e.Link) + out.Write32(e.Info) + out.Write32(uint32(e.Addralign)) + out.Write32(uint32(e.Entsize)) +} + +func elfwriteshdrs(out *OutBuf) uint32 { + if elf64 { + for i := 0; i < int(ehdr.Shnum); i++ { + elf64shdr(out, shdr[i]) + } + return uint32(ehdr.Shnum) * ELF64SHDRSIZE + } + + for i := 0; i < int(ehdr.Shnum); i++ { + elf32shdr(out, shdr[i]) + } + return uint32(ehdr.Shnum) * ELF32SHDRSIZE +} + +func elfsetstring(ctxt *Link, s loader.Sym, str string, off int) { + if nelfstr >= len(elfstr) { + ctxt.Errorf(s, "too many elf strings") + errorexit() + } + + elfstr[nelfstr].s = str + elfstr[nelfstr].off = off + nelfstr++ +} + +func elfwritephdrs(out *OutBuf) uint32 { + if elf64 { + for i := 0; i < int(ehdr.Phnum); i++ { + elf64phdr(out, phdr[i]) + } + return uint32(ehdr.Phnum) * ELF64PHDRSIZE + } + + for i := 0; i < int(ehdr.Phnum); i++ { + elf32phdr(out, phdr[i]) + } + return uint32(ehdr.Phnum) * ELF32PHDRSIZE +} + +func newElfPhdr() *ElfPhdr { + e := new(ElfPhdr) + if ehdr.Phnum >= NSECT { + Errorf(nil, "too many phdrs") + } else { + phdr[ehdr.Phnum] = e + ehdr.Phnum++ + } + if elf64 { + ehdr.Shoff += ELF64PHDRSIZE + } else { + ehdr.Shoff += ELF32PHDRSIZE + } + return e +} + +func newElfShdr(name int64) *ElfShdr { + e := new(ElfShdr) + e.Name = uint32(name) + e.shnum = elf.SectionIndex(ehdr.Shnum) + if ehdr.Shnum >= NSECT { + Errorf(nil, "too many shdrs") + } else { + shdr[ehdr.Shnum] = e + ehdr.Shnum++ + } + + return e +} + +func getElfEhdr() *ElfEhdr { + return &ehdr +} + +func elf64writehdr(out *OutBuf) uint32 { + out.Write(ehdr.Ident[:]) + out.Write16(uint16(ehdr.Type)) + out.Write16(uint16(ehdr.Machine)) + out.Write32(uint32(ehdr.Version)) + out.Write64(ehdr.Entry) + out.Write64(ehdr.Phoff) + out.Write64(ehdr.Shoff) + out.Write32(ehdr.Flags) + out.Write16(ehdr.Ehsize) + out.Write16(ehdr.Phentsize) + out.Write16(ehdr.Phnum) + out.Write16(ehdr.Shentsize) + out.Write16(ehdr.Shnum) + out.Write16(ehdr.Shstrndx) + return ELF64HDRSIZE +} + +func elf32writehdr(out *OutBuf) uint32 { + out.Write(ehdr.Ident[:]) + out.Write16(uint16(ehdr.Type)) + out.Write16(uint16(ehdr.Machine)) + out.Write32(uint32(ehdr.Version)) + out.Write32(uint32(ehdr.Entry)) + out.Write32(uint32(ehdr.Phoff)) + out.Write32(uint32(ehdr.Shoff)) + out.Write32(ehdr.Flags) + out.Write16(ehdr.Ehsize) + out.Write16(ehdr.Phentsize) + out.Write16(ehdr.Phnum) + out.Write16(ehdr.Shentsize) + out.Write16(ehdr.Shnum) + out.Write16(ehdr.Shstrndx) + return ELF32HDRSIZE +} + +func elfwritehdr(out *OutBuf) uint32 { + if elf64 { + return elf64writehdr(out) + } + return elf32writehdr(out) +} + +/* Taken directly from the definition document for ELF64 */ +func elfhash(name string) uint32 { + var h uint32 + for i := 0; i < len(name); i++ { + h = (h << 4) + uint32(name[i]) + if g := h & 0xf0000000; g != 0 { + h ^= g >> 24 + } + h &= 0x0fffffff + } + return h +} + +func elfWriteDynEntSym(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) { + Elfwritedynentsymplus(ctxt, s, tag, t, 0) +} + +func Elfwritedynent(arch *sys.Arch, s *loader.SymbolBuilder, tag elf.DynTag, val uint64) { + if elf64 { + s.AddUint64(arch, uint64(tag)) + s.AddUint64(arch, val) + } else { + s.AddUint32(arch, uint32(tag)) + s.AddUint32(arch, uint32(val)) + } +} + +func 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 elf.DynTag, t loader.Sym, add int64) { + if elf64 { + s.AddUint64(ctxt.Arch, uint64(tag)) + } else { + s.AddUint32(ctxt.Arch, uint32(tag)) + } + s.AddAddrPlus(ctxt.Arch, t, add) +} + +func elfwritedynentsymsize(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) { + if elf64 { + s.AddUint64(ctxt.Arch, uint64(tag)) + } else { + s.AddUint32(ctxt.Arch, uint32(tag)) + } + s.AddSize(ctxt.Arch, t) +} + +func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int { + interp = p + n := len(interp) + 1 + sh.Addr = startva + resoff - uint64(n) + sh.Off = resoff - uint64(n) + sh.Size = uint64(n) + + return n +} + +func elfwriteinterp(out *OutBuf) int { + sh := elfshname(".interp") + out.SeekSet(int64(sh.Off)) + out.WriteString(interp) + out.Write8(0) + return int(sh.Size) +} + +func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sz int) int { + n := 3*4 + uint64(sz) + 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) +} + +func elfwritenotehdr(out *OutBuf, str string, namesz uint32, descsz uint32, tag uint32) *ElfShdr { + sh := elfshname(str) + + // Write Elf_Note header. + out.SeekSet(int64(sh.Off)) + + out.Write32(namesz) + out.Write32(descsz) + out.Write32(tag) + + return sh +} + +// NetBSD Signature (as per sys/exec_elf.h) +const ( + ELF_NOTE_NETBSD_NAMESZ = 7 + ELF_NOTE_NETBSD_DESCSZ = 4 + ELF_NOTE_NETBSD_TAG = 1 + ELF_NOTE_NETBSD_VERSION = 700000000 /* NetBSD 7.0 */ +) + +var ELF_NOTE_NETBSD_NAME = []byte("NetBSD\x00") + +func elfnetbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(Rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + Rnd(ELF_NOTE_NETBSD_DESCSZ, 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritenetbsdsig(out *OutBuf) int { + // Write Elf_Note header. + sh := elfwritenotehdr(out, ".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by NetBSD string and version. + out.Write(ELF_NOTE_NETBSD_NAME) + out.Write8(0) + out.Write32(ELF_NOTE_NETBSD_VERSION) + + return int(sh.Size) +} + +// The race detector can't handle ASLR (address space layout randomization). +// ASLR is on by default for NetBSD, so we turn the ASLR off explicitly +// using a magic elf Note when building race binaries. + +func elfnetbsdpax(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(Rnd(4, 4) + Rnd(4, 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritenetbsdpax(out *OutBuf) int { + sh := elfwritenotehdr(out, ".note.netbsd.pax", 4 /* length of PaX\x00 */, 4 /* length of flags */, 0x03 /* PaX type */) + if sh == nil { + return 0 + } + out.Write([]byte("PaX\x00")) + out.Write32(0x20) // 0x20 = Force disable ASLR + return int(sh.Size) +} + +// OpenBSD Signature +const ( + ELF_NOTE_OPENBSD_NAMESZ = 8 + ELF_NOTE_OPENBSD_DESCSZ = 4 + ELF_NOTE_OPENBSD_TAG = 1 + ELF_NOTE_OPENBSD_VERSION = 0 +) + +var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00") + +func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ + return elfnote(sh, startva, resoff, n) +} + +func elfwriteopenbsdsig(out *OutBuf) int { + // Write Elf_Note header. + sh := elfwritenotehdr(out, ".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by OpenBSD string and version. + out.Write(ELF_NOTE_OPENBSD_NAME) + + out.Write32(ELF_NOTE_OPENBSD_VERSION) + + return int(sh.Size) +} + +func addbuildinfo(val string) { + if !strings.HasPrefix(val, "0x") { + Exitf("-B argument must start with 0x: %s", val) + } + + ov := val + val = val[2:] + + const maxLen = 32 + if hex.DecodedLen(len(val)) > maxLen { + Exitf("-B option too long (max %d digits): %s", maxLen, ov) + } + + b, err := hex.DecodeString(val) + if err != nil { + if err == hex.ErrLength { + Exitf("-B argument must have even number of digits: %s", ov) + } + if inv, ok := err.(hex.InvalidByteError); ok { + Exitf("-B argument contains invalid hex digit %c: %s", byte(inv), ov) + } + Exitf("-B argument contains invalid hex: %s", ov) + } + + buildinfo = b +} + +// Build info note +const ( + ELF_NOTE_BUILDINFO_NAMESZ = 4 + ELF_NOTE_BUILDINFO_TAG = 3 +) + +var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00") + +func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfgobuildid(sh *ElfShdr, startva uint64, resoff uint64) int { + n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(*flagBuildid)), 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritebuildinfo(out *OutBuf) int { + sh := elfwritenotehdr(out, ".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG) + if sh == nil { + return 0 + } + + out.Write(ELF_NOTE_BUILDINFO_NAME) + out.Write(buildinfo) + var zero = make([]byte, 4) + out.Write(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))]) + + return int(sh.Size) +} + +func elfwritegobuildid(out *OutBuf) int { + sh := elfwritenotehdr(out, ".note.go.buildid", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(*flagBuildid)), ELF_NOTE_GOBUILDID_TAG) + if sh == nil { + return 0 + } + + out.Write(ELF_NOTE_GO_NAME) + out.Write([]byte(*flagBuildid)) + var zero = make([]byte, 4) + out.Write(zero[:int(Rnd(int64(len(*flagBuildid)), 4)-int64(len(*flagBuildid)))]) + + return int(sh.Size) +} + +// Go specific notes +const ( + ELF_NOTE_GOPKGLIST_TAG = 1 + ELF_NOTE_GOABIHASH_TAG = 2 + ELF_NOTE_GODEPS_TAG = 3 + ELF_NOTE_GOBUILDID_TAG = 4 +) + +var ELF_NOTE_GO_NAME = []byte("Go\x00\x00") + +var elfverneed int + +type Elfaux struct { + next *Elfaux + num int + vers string +} + +type Elflib struct { + next *Elflib + aux *Elfaux + file string +} + +func addelflib(list **Elflib, file string, vers string) *Elfaux { + var lib *Elflib + + for lib = *list; lib != nil; lib = lib.next { + if lib.file == file { + goto havelib + } + } + lib = new(Elflib) + lib.next = *list + lib.file = file + *list = lib + +havelib: + for aux := lib.aux; aux != nil; aux = aux.next { + if aux.vers == vers { + return aux + } + } + aux := new(Elfaux) + aux.next = lib.aux + aux.vers = vers + lib.aux = aux + + return aux +} + +func elfdynhash(ctxt *Link) { + if !ctxt.IsELF { + return + } + + nsym := Nelfsym + ldr := ctxt.loader + s := ldr.CreateSymForUpdate(".hash", 0) + s.SetType(sym.SELFROSECT) + + i := nsym + nbucket := 1 + for i > 0 { + nbucket++ + i >>= 1 + } + + var needlib *Elflib + need := make([]*Elfaux, nsym) + chain := make([]uint32, nsym) + buckets := make([]uint32, nbucket) + + for _, sy := range ldr.DynidSyms() { + + dynid := ldr.SymDynid(sy) + if ldr.SymDynimpvers(sy) != "" { + need[dynid] = addelflib(&needlib, ldr.SymDynimplib(sy), ldr.SymDynimpvers(sy)) + } + + name := ldr.SymExtname(sy) + hc := elfhash(name) + + b := hc % uint32(nbucket) + chain[dynid] = buckets[b] + buckets[b] = uint32(dynid) + } + + // s390x (ELF64) hash table entries are 8 bytes + if ctxt.Arch.Family == sys.S390X { + s.AddUint64(ctxt.Arch, uint64(nbucket)) + s.AddUint64(ctxt.Arch, uint64(nsym)) + for i := 0; i < nbucket; i++ { + s.AddUint64(ctxt.Arch, uint64(buckets[i])) + } + for i := 0; i < nsym; i++ { + s.AddUint64(ctxt.Arch, uint64(chain[i])) + } + } else { + s.AddUint32(ctxt.Arch, uint32(nbucket)) + s.AddUint32(ctxt.Arch, uint32(nsym)) + for i := 0; i < nbucket; i++ { + s.AddUint32(ctxt.Arch, buckets[i]) + } + for i := 0; i < nsym; i++ { + s.AddUint32(ctxt.Arch, chain[i]) + } + } + + dynstr := ldr.CreateSymForUpdate(".dynstr", 0) + + // version symbols + gnuVersionR := ldr.CreateSymForUpdate(".gnu.version_r", 0) + s = gnuVersionR + i = 2 + nfile := 0 + for l := needlib; l != nil; l = l.next { + nfile++ + + // header + s.AddUint16(ctxt.Arch, 1) // table version + j := 0 + for x := l.aux; x != nil; x = x.next { + j++ + } + s.AddUint16(ctxt.Arch, uint16(j)) // aux count + s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(l.file))) // file string offset + s.AddUint32(ctxt.Arch, 16) // offset from header to first aux + if l.next != nil { + s.AddUint32(ctxt.Arch, 16+uint32(j)*16) // offset from this header to next + } else { + s.AddUint32(ctxt.Arch, 0) + } + + for x := l.aux; x != nil; x = x.next { + x.num = i + i++ + + // aux struct + s.AddUint32(ctxt.Arch, elfhash(x.vers)) // hash + s.AddUint16(ctxt.Arch, 0) // flags + s.AddUint16(ctxt.Arch, uint16(x.num)) // other - index we refer to this by + s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(x.vers))) // version string offset + if x.next != nil { + s.AddUint32(ctxt.Arch, 16) // offset from this aux to next + } else { + s.AddUint32(ctxt.Arch, 0) + } + } + } + + // version references + gnuVersion := ldr.CreateSymForUpdate(".gnu.version", 0) + s = gnuVersion + + for i := 0; i < nsym; i++ { + if i == 0 { + s.AddUint16(ctxt.Arch, 0) // first entry - no symbol + } else if need[i] == nil { + s.AddUint16(ctxt.Arch, 1) // global + } else { + s.AddUint16(ctxt.Arch, uint16(need[i].num)) + } + } + + s = ldr.CreateSymForUpdate(".dynamic", 0) + elfverneed = nfile + if elfverneed != 0 { + elfWriteDynEntSym(ctxt, s, elf.DT_VERNEED, gnuVersionR.Sym()) + Elfwritedynent(ctxt.Arch, s, elf.DT_VERNEEDNUM, uint64(nfile)) + elfWriteDynEntSym(ctxt, s, elf.DT_VERSYM, gnuVersion.Sym()) + } + + sy := ldr.CreateSymForUpdate(elfRelType+".plt", 0) + if sy.Size() > 0 { + if elfRelType == ".rela" { + Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_RELA)) + } else { + Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_REL)) + } + elfwritedynentsymsize(ctxt, s, elf.DT_PLTRELSZ, sy.Sym()) + elfWriteDynEntSym(ctxt, s, elf.DT_JMPREL, sy.Sym()) + } + + Elfwritedynent(ctxt.Arch, s, elf.DT_NULL, 0) +} + +func elfphload(seg *sym.Segment) *ElfPhdr { + ph := newElfPhdr() + ph.Type = elf.PT_LOAD + if seg.Rwx&4 != 0 { + ph.Flags |= elf.PF_R + } + if seg.Rwx&2 != 0 { + ph.Flags |= elf.PF_W + } + if seg.Rwx&1 != 0 { + ph.Flags |= elf.PF_X + } + ph.Vaddr = seg.Vaddr + ph.Paddr = seg.Vaddr + ph.Memsz = seg.Length + ph.Off = seg.Fileoff + ph.Filesz = seg.Filelen + ph.Align = uint64(*FlagRound) + + return ph +} + +func elfphrelro(seg *sym.Segment) { + ph := newElfPhdr() + ph.Type = elf.PT_GNU_RELRO + ph.Vaddr = seg.Vaddr + ph.Paddr = seg.Vaddr + ph.Memsz = seg.Length + ph.Off = seg.Fileoff + ph.Filesz = seg.Filelen + ph.Align = uint64(*FlagRound) +} + +func elfshname(name string) *ElfShdr { + for i := 0; i < nelfstr; i++ { + if name != elfstr[i].s { + continue + } + off := elfstr[i].off + for i = 0; i < int(ehdr.Shnum); i++ { + sh := shdr[i] + if sh.Name == uint32(off) { + return sh + } + } + return newElfShdr(int64(off)) + } + Exitf("cannot find elf name %s", name) + return nil +} + +// Create an ElfShdr for the section with name. +// Create a duplicate if one already exists with that name +func elfshnamedup(name string) *ElfShdr { + for i := 0; i < nelfstr; i++ { + if name == elfstr[i].s { + off := elfstr[i].off + return newElfShdr(int64(off)) + } + } + + Errorf(nil, "cannot find elf name %s", name) + errorexit() + return nil +} + +func elfshalloc(sect *sym.Section) *ElfShdr { + sh := elfshname(sect.Name) + sect.Elfsect = sh + return sh +} + +func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr { + var sh *ElfShdr + + if sect.Name == ".text" { + if sect.Elfsect == nil { + sect.Elfsect = elfshnamedup(sect.Name) + } + sh = sect.Elfsect.(*ElfShdr) + } else { + sh = elfshalloc(sect) + } + + // If this section has already been set up as a note, we assume type_ and + // flags are already correct, but the other fields still need filling in. + if sh.Type == uint32(elf.SHT_NOTE) { + if linkmode != LinkExternal { + // TODO(mwhudson): the approach here will work OK when + // linking internally for notes that we want to be included + // in a loadable segment (e.g. the abihash note) but not for + // notes that we do not want to be mapped (e.g. the package + // list note). The real fix is probably to define new values + // for Symbol.Type corresponding to mapped and unmapped notes + // and handle them in dodata(). + Errorf(nil, "sh.Type == SHT_NOTE in elfshbits when linking internally") + } + sh.Addralign = uint64(sect.Align) + sh.Size = sect.Length + sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr + return sh + } + if sh.Type > 0 { + return sh + } + + if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { + sh.Type = uint32(elf.SHT_PROGBITS) + } else { + sh.Type = uint32(elf.SHT_NOBITS) + } + sh.Flags = uint64(elf.SHF_ALLOC) + if sect.Rwx&1 != 0 { + sh.Flags |= uint64(elf.SHF_EXECINSTR) + } + if sect.Rwx&2 != 0 { + sh.Flags |= uint64(elf.SHF_WRITE) + } + if sect.Name == ".tbss" { + sh.Flags |= uint64(elf.SHF_TLS) + sh.Type = uint32(elf.SHT_NOBITS) + } + if strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") { + sh.Flags = 0 + } + + if linkmode != LinkExternal { + sh.Addr = sect.Vaddr + } + sh.Addralign = uint64(sect.Align) + sh.Size = sect.Length + if sect.Name != ".tbss" { + sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr + } + + return sh +} + +func elfshreloc(arch *sys.Arch, sect *sym.Section) *ElfShdr { + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab or notes. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return nil + } + if sect.Name == ".shstrtab" || sect.Name == ".tbss" { + return nil + } + if sect.Elfsect.(*ElfShdr).Type == uint32(elf.SHT_NOTE) { + return nil + } + + typ := elf.SHT_REL + if elfRelType == ".rela" { + typ = elf.SHT_RELA + } + + sh := elfshname(elfRelType + sect.Name) + // There could be multiple text sections but each needs + // its own .rela.text. + + if sect.Name == ".text" { + if sh.Info != 0 && sh.Info != uint32(sect.Elfsect.(*ElfShdr).shnum) { + sh = elfshnamedup(elfRelType + sect.Name) + } + } + + sh.Type = uint32(typ) + sh.Entsize = uint64(arch.RegSize) * 2 + if typ == elf.SHT_RELA { + sh.Entsize += uint64(arch.RegSize) + } + sh.Link = uint32(elfshname(".symtab").shnum) + sh.Info = uint32(sect.Elfsect.(*ElfShdr).shnum) + sh.Off = sect.Reloff + sh.Size = sect.Rellen + sh.Addralign = uint64(arch.RegSize) + return sh +} + +func elfrelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) { + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + if sect.Name == ".shstrtab" { + return + } + + ldr := ctxt.loader + for i, s := range syms { + if !ldr.AttrReachable(s) { + panic("should never happen") + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + + eaddr := sect.Vaddr + sect.Length + for _, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymValue(s) >= int64(eaddr) { + break + } + + // Compute external relocations on the go, and pass to Elfreloc1 + // to stream out. + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + rr, ok := extreloc(ctxt, ldr, s, r) + if !ok { + continue + } + if rr.Xsym == 0 { + ldr.Errorf(s, "missing xsym in relocation") + continue + } + esr := ElfSymForReloc(ctxt, rr.Xsym) + if esr == 0 { + ldr.Errorf(s, "reloc %d (%s) to non-elf symbol %s (outer=%s) %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymType(r.Sym()).String()) + } + if !ldr.AttrReachable(rr.Xsym) { + ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym)) + } + if !thearch.Elfreloc1(ctxt, out, ldr, s, rr, ri, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) { + ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym())) + } + } + } + + // sanity check + if uint64(out.Offset()) != sect.Reloff+sect.Rellen { + panic(fmt.Sprintf("elfrelocsect: size mismatch %d != %d + %d", out.Offset(), sect.Reloff, sect.Rellen)) + } +} + +func elfEmitReloc(ctxt *Link) { + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + sizeExtRelocs(ctxt, thearch.ElfrelocSize) + relocSect, wg := relocSectFn(ctxt, elfrelocsect) + + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + relocSect(ctxt, sect, ctxt.Textp) + } else { + relocSect(ctxt, sect, ctxt.datap) + } + } + + for _, sect := range Segrodata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for _, sect := range Segrelrodata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for _, sect := range Segdata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for i := 0; i < len(Segdwarf.Sections); i++ { + sect := Segdwarf.Sections[i] + si := dwarfp[i] + if si.secSym() != loader.Sym(sect.Sym) || + ctxt.loader.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + relocSect(ctxt, sect, si.syms) + } + wg.Wait() +} + +func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) { + ldr := ctxt.loader + s := ldr.CreateSymForUpdate(sectionName, 0) + s.SetType(sym.SELFROSECT) + // namesz + s.AddUint32(ctxt.Arch, uint32(len(ELF_NOTE_GO_NAME))) + // descsz + s.AddUint32(ctxt.Arch, uint32(len(desc))) + // tag + s.AddUint32(ctxt.Arch, tag) + // name + padding + s.AddBytes(ELF_NOTE_GO_NAME) + for len(s.Data())%4 != 0 { + s.AddUint8(0) + } + // desc + padding + s.AddBytes(desc) + for len(s.Data())%4 != 0 { + s.AddUint8(0) + } + s.SetSize(int64(len(s.Data()))) + s.SetAlign(4) +} + +func (ctxt *Link) doelf() { + ldr := ctxt.loader + + /* predefine strings we need for section headers */ + shstrtab := ldr.CreateSymForUpdate(".shstrtab", 0) + + shstrtab.SetType(sym.SELFROSECT) + + shstrtab.Addstring("") + shstrtab.Addstring(".text") + shstrtab.Addstring(".noptrdata") + shstrtab.Addstring(".data") + shstrtab.Addstring(".bss") + shstrtab.Addstring(".noptrbss") + shstrtab.Addstring("__libfuzzer_extra_counters") + shstrtab.Addstring(".go.buildinfo") + + // generate .tbss section for dynamic internal linker or external + // linking, so that various binutils could correctly calculate + // PT_TLS size. See https://golang.org/issue/5200. + if !*FlagD || ctxt.IsExternal() { + shstrtab.Addstring(".tbss") + } + if ctxt.IsNetbsd() { + shstrtab.Addstring(".note.netbsd.ident") + if *flagRace { + shstrtab.Addstring(".note.netbsd.pax") + } + } + if ctxt.IsOpenbsd() { + shstrtab.Addstring(".note.openbsd.ident") + } + if len(buildinfo) > 0 { + shstrtab.Addstring(".note.gnu.build-id") + } + if *flagBuildid != "" { + shstrtab.Addstring(".note.go.buildid") + } + shstrtab.Addstring(".elfdata") + shstrtab.Addstring(".rodata") + // See the comment about data.rel.ro.FOO section names in data.go. + relro_prefix := "" + if ctxt.UseRelro() { + shstrtab.Addstring(".data.rel.ro") + relro_prefix = ".data.rel.ro" + } + shstrtab.Addstring(relro_prefix + ".typelink") + shstrtab.Addstring(relro_prefix + ".itablink") + shstrtab.Addstring(relro_prefix + ".gosymtab") + shstrtab.Addstring(relro_prefix + ".gopclntab") + + if ctxt.IsExternal() { + *FlagD = true + + shstrtab.Addstring(elfRelType + ".text") + shstrtab.Addstring(elfRelType + ".rodata") + shstrtab.Addstring(elfRelType + relro_prefix + ".typelink") + shstrtab.Addstring(elfRelType + relro_prefix + ".itablink") + shstrtab.Addstring(elfRelType + relro_prefix + ".gosymtab") + shstrtab.Addstring(elfRelType + relro_prefix + ".gopclntab") + shstrtab.Addstring(elfRelType + ".noptrdata") + shstrtab.Addstring(elfRelType + ".data") + if ctxt.UseRelro() { + shstrtab.Addstring(elfRelType + ".data.rel.ro") + } + shstrtab.Addstring(elfRelType + ".go.buildinfo") + + // add a .note.GNU-stack section to mark the stack as non-executable + shstrtab.Addstring(".note.GNU-stack") + + if ctxt.IsShared() { + shstrtab.Addstring(".note.go.abihash") + shstrtab.Addstring(".note.go.pkg-list") + shstrtab.Addstring(".note.go.deps") + } + } + + hasinitarr := ctxt.linkShared + + /* shared library initializer */ + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin: + hasinitarr = true + } + + if hasinitarr { + shstrtab.Addstring(".init_array") + shstrtab.Addstring(elfRelType + ".init_array") + } + + if !*FlagS { + shstrtab.Addstring(".symtab") + shstrtab.Addstring(".strtab") + dwarfaddshstrings(ctxt, shstrtab) + } + + shstrtab.Addstring(".shstrtab") + + if !*FlagD { /* -d suppresses dynamic loader format */ + shstrtab.Addstring(".interp") + shstrtab.Addstring(".hash") + shstrtab.Addstring(".got") + if ctxt.IsPPC64() { + shstrtab.Addstring(".glink") + } + shstrtab.Addstring(".got.plt") + shstrtab.Addstring(".dynamic") + shstrtab.Addstring(".dynsym") + shstrtab.Addstring(".dynstr") + shstrtab.Addstring(elfRelType) + shstrtab.Addstring(elfRelType + ".plt") + + shstrtab.Addstring(".plt") + shstrtab.Addstring(".gnu.version") + shstrtab.Addstring(".gnu.version_r") + + /* dynamic symbol table - first entry all zeros */ + dynsym := ldr.CreateSymForUpdate(".dynsym", 0) + + dynsym.SetType(sym.SELFROSECT) + if elf64 { + dynsym.SetSize(dynsym.Size() + ELF64SYMSIZE) + } else { + dynsym.SetSize(dynsym.Size() + ELF32SYMSIZE) + } + + /* dynamic string table */ + dynstr := ldr.CreateSymForUpdate(".dynstr", 0) + + dynstr.SetType(sym.SELFROSECT) + if dynstr.Size() == 0 { + dynstr.Addstring("") + } + + /* relocation table */ + s := ldr.CreateSymForUpdate(elfRelType, 0) + s.SetType(sym.SELFROSECT) + + /* global offset table */ + got := ldr.CreateSymForUpdate(".got", 0) + got.SetType(sym.SELFGOT) // writable + + /* ppc64 glink resolver */ + if ctxt.IsPPC64() { + s := ldr.CreateSymForUpdate(".glink", 0) + s.SetType(sym.SELFRXSECT) + } + + /* hash */ + hash := ldr.CreateSymForUpdate(".hash", 0) + hash.SetType(sym.SELFROSECT) + + gotplt := ldr.CreateSymForUpdate(".got.plt", 0) + gotplt.SetType(sym.SELFSECT) // writable + + plt := ldr.CreateSymForUpdate(".plt", 0) + if ctxt.IsPPC64() { + // In the ppc64 ABI, .plt is a data section + // written by the dynamic linker. + plt.SetType(sym.SELFSECT) + } else { + plt.SetType(sym.SELFRXSECT) + } + + s = ldr.CreateSymForUpdate(elfRelType+".plt", 0) + s.SetType(sym.SELFROSECT) + + s = ldr.CreateSymForUpdate(".gnu.version", 0) + s.SetType(sym.SELFROSECT) + + s = ldr.CreateSymForUpdate(".gnu.version_r", 0) + s.SetType(sym.SELFROSECT) + + /* define dynamic elf table */ + dynamic := ldr.CreateSymForUpdate(".dynamic", 0) + dynamic.SetType(sym.SELFSECT) // writable + + if ctxt.IsS390X() { + // S390X uses .got instead of .got.plt + gotplt = got + } + thearch.Elfsetupplt(ctxt, plt, gotplt, dynamic.Sym()) + + /* + * .dynamic table + */ + elfwritedynentsym(ctxt, dynamic, elf.DT_HASH, hash.Sym()) + + elfwritedynentsym(ctxt, dynamic, elf.DT_SYMTAB, dynsym.Sym()) + if elf64 { + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF64SYMSIZE) + } else { + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF32SYMSIZE) + } + elfwritedynentsym(ctxt, dynamic, elf.DT_STRTAB, dynstr.Sym()) + elfwritedynentsymsize(ctxt, dynamic, elf.DT_STRSZ, dynstr.Sym()) + if elfRelType == ".rela" { + rela := ldr.LookupOrCreateSym(".rela", 0) + elfwritedynentsym(ctxt, dynamic, elf.DT_RELA, rela) + elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELASZ, rela) + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELAENT, ELF64RELASIZE) + } else { + rel := ldr.LookupOrCreateSym(".rel", 0) + elfwritedynentsym(ctxt, dynamic, elf.DT_REL, rel) + elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELSZ, rel) + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELENT, ELF32RELSIZE) + } + + if rpath.val != "" { + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RUNPATH, uint64(dynstr.Addstring(rpath.val))) + } + + if ctxt.IsPPC64() { + elfwritedynentsym(ctxt, dynamic, elf.DT_PLTGOT, plt.Sym()) + } else { + elfwritedynentsym(ctxt, dynamic, elf.DT_PLTGOT, gotplt.Sym()) + } + + if ctxt.IsPPC64() { + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_PPC64_OPT, 0) + } + + // Solaris dynamic linker can't handle an empty .rela.plt if + // DT_JMPREL is emitted so we have to defer generation of elf.DT_PLTREL, + // DT_PLTRELSZ, and elf.DT_JMPREL dynamic entries until after we know the + // size of .rel(a).plt section. + Elfwritedynent(ctxt.Arch, dynamic, elf.DT_DEBUG, 0) + } + + if ctxt.IsShared() { + // The go.link.abihashbytes symbol will be pointed at the appropriate + // part of the .note.go.abihash section in data.go:func address(). + s := ldr.LookupOrCreateSym("go.link.abihashbytes", 0) + sb := ldr.MakeSymbolUpdater(s) + ldr.SetAttrLocal(s, true) + sb.SetType(sym.SRODATA) + ldr.SetAttrSpecial(s, true) + sb.SetReachable(true) + sb.SetSize(sha1.Size) + + sort.Sort(byPkg(ctxt.Library)) + h := sha1.New() + for _, l := range ctxt.Library { + h.Write(l.Fingerprint[:]) + } + addgonote(ctxt, ".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{})) + addgonote(ctxt, ".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote) + var deplist []string + for _, shlib := range ctxt.Shlibs { + deplist = append(deplist, filepath.Base(shlib.Path)) + } + addgonote(ctxt, ".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n"))) + } + + if ctxt.LinkMode == LinkExternal && *flagBuildid != "" { + addgonote(ctxt, ".note.go.buildid", ELF_NOTE_GOBUILDID_TAG, []byte(*flagBuildid)) + } +} + +// Do not write DT_NULL. elfdynhash will finish it. +func shsym(sh *ElfShdr, ldr *loader.Loader, s loader.Sym) { + if s == 0 { + panic("bad symbol in shsym2") + } + addr := ldr.SymValue(s) + if sh.Flags&uint64(elf.SHF_ALLOC) != 0 { + sh.Addr = uint64(addr) + } + sh.Off = uint64(datoff(ldr, s, addr)) + sh.Size = uint64(ldr.SymSize(s)) +} + +func phsh(ph *ElfPhdr, sh *ElfShdr) { + ph.Vaddr = sh.Addr + ph.Paddr = ph.Vaddr + ph.Off = sh.Off + ph.Filesz = sh.Size + ph.Memsz = sh.Size + ph.Align = sh.Addralign +} + +func Asmbelfsetup() { + /* This null SHdr must appear before all others */ + elfshname("") + + for _, sect := range Segtext.Sections { + // There could be multiple .text sections. Instead check the Elfsect + // field to determine if already has an ElfShdr and if not, create one. + if sect.Name == ".text" { + if sect.Elfsect == nil { + sect.Elfsect = elfshnamedup(sect.Name) + } + } else { + elfshalloc(sect) + } + } + for _, sect := range Segrodata.Sections { + elfshalloc(sect) + } + for _, sect := range Segrelrodata.Sections { + elfshalloc(sect) + } + for _, sect := range Segdata.Sections { + elfshalloc(sect) + } + for _, sect := range Segdwarf.Sections { + elfshalloc(sect) + } +} + +func asmbElf(ctxt *Link) { + var symo int64 + if !*FlagS { + symo = int64(Segdwarf.Fileoff + Segdwarf.Filelen) + symo = Rnd(symo, int64(ctxt.Arch.PtrSize)) + ctxt.Out.SeekSet(symo) + asmElfSym(ctxt) + ctxt.Out.Write(Elfstrdat) + if ctxt.IsExternal() { + elfEmitReloc(ctxt) + } + } + ctxt.Out.SeekSet(0) + + ldr := ctxt.loader + eh := getElfEhdr() + switch ctxt.Arch.Family { + default: + Exitf("unknown architecture in asmbelf: %v", ctxt.Arch.Family) + case sys.MIPS, sys.MIPS64: + eh.Machine = uint16(elf.EM_MIPS) + case sys.ARM: + eh.Machine = uint16(elf.EM_ARM) + case sys.AMD64: + eh.Machine = uint16(elf.EM_X86_64) + case sys.ARM64: + eh.Machine = uint16(elf.EM_AARCH64) + case sys.I386: + eh.Machine = uint16(elf.EM_386) + case sys.PPC64: + eh.Machine = uint16(elf.EM_PPC64) + case sys.RISCV64: + eh.Machine = uint16(elf.EM_RISCV) + case sys.S390X: + eh.Machine = uint16(elf.EM_S390) + } + + elfreserve := int64(ELFRESERVE) + + numtext := int64(0) + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + numtext++ + } + } + + // If there are multiple text sections, extra space is needed + // in the elfreserve for the additional .text and .rela.text + // section headers. It can handle 4 extra now. Headers are + // 64 bytes. + + if numtext > 4 { + elfreserve += elfreserve + numtext*64*2 + } + + startva := *FlagTextAddr - int64(HEADR) + resoff := elfreserve + + var pph *ElfPhdr + var pnote *ElfPhdr + if *flagRace && ctxt.IsNetbsd() { + sh := elfshname(".note.netbsd.pax") + resoff -= int64(elfnetbsdpax(sh, uint64(startva), uint64(resoff))) + pnote = newElfPhdr() + pnote.Type = elf.PT_NOTE + pnote.Flags = elf.PF_R + phsh(pnote, sh) + } + if ctxt.LinkMode == LinkExternal { + /* skip program headers */ + eh.Phoff = 0 + + eh.Phentsize = 0 + + if ctxt.BuildMode == BuildModeShared { + sh := elfshname(".note.go.pkg-list") + sh.Type = uint32(elf.SHT_NOTE) + sh = elfshname(".note.go.abihash") + sh.Type = uint32(elf.SHT_NOTE) + sh.Flags = uint64(elf.SHF_ALLOC) + sh = elfshname(".note.go.deps") + sh.Type = uint32(elf.SHT_NOTE) + } + + if *flagBuildid != "" { + sh := elfshname(".note.go.buildid") + sh.Type = uint32(elf.SHT_NOTE) + sh.Flags = uint64(elf.SHF_ALLOC) + } + + goto elfobj + } + + /* program header info */ + pph = newElfPhdr() + + pph.Type = elf.PT_PHDR + pph.Flags = elf.PF_R + pph.Off = uint64(eh.Ehsize) + pph.Vaddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off + pph.Paddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off + pph.Align = uint64(*FlagRound) + + /* + * PHDR must be in a loaded segment. Adjust the text + * segment boundaries downwards to include it. + */ + { + o := int64(Segtext.Vaddr - pph.Vaddr) + Segtext.Vaddr -= uint64(o) + Segtext.Length += uint64(o) + o = int64(Segtext.Fileoff - pph.Off) + Segtext.Fileoff -= uint64(o) + Segtext.Filelen += uint64(o) + } + + if !*FlagD { /* -d suppresses dynamic loader format */ + /* interpreter */ + sh := elfshname(".interp") + + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = 1 + + if interpreter == "" && objabi.GO_LDSO != "" { + interpreter = objabi.GO_LDSO + } + + if interpreter == "" { + switch ctxt.HeadType { + case objabi.Hlinux: + if objabi.GOOS == "android" { + interpreter = thearch.Androiddynld + if interpreter == "" { + Exitf("ELF interpreter not set") + } + } else { + interpreter = thearch.Linuxdynld + } + + case objabi.Hfreebsd: + interpreter = thearch.Freebsddynld + + case objabi.Hnetbsd: + interpreter = thearch.Netbsddynld + + case objabi.Hopenbsd: + interpreter = thearch.Openbsddynld + + case objabi.Hdragonfly: + interpreter = thearch.Dragonflydynld + + case objabi.Hsolaris: + interpreter = thearch.Solarisdynld + } + } + + resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter)) + + ph := newElfPhdr() + ph.Type = elf.PT_INTERP + ph.Flags = elf.PF_R + phsh(ph, sh) + } + + pnote = nil + if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd { + var sh *ElfShdr + switch ctxt.HeadType { + case objabi.Hnetbsd: + sh = elfshname(".note.netbsd.ident") + resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff))) + + case objabi.Hopenbsd: + sh = elfshname(".note.openbsd.ident") + resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff))) + } + + pnote = newElfPhdr() + pnote.Type = elf.PT_NOTE + pnote.Flags = elf.PF_R + phsh(pnote, sh) + } + + if len(buildinfo) > 0 { + sh := elfshname(".note.gnu.build-id") + resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff))) + + if pnote == nil { + pnote = newElfPhdr() + pnote.Type = elf.PT_NOTE + pnote.Flags = elf.PF_R + } + + phsh(pnote, sh) + } + + if *flagBuildid != "" { + sh := elfshname(".note.go.buildid") + resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff))) + + pnote := newElfPhdr() + pnote.Type = elf.PT_NOTE + pnote.Flags = elf.PF_R + phsh(pnote, sh) + } + + // Additions to the reserved area must be above this line. + + elfphload(&Segtext) + if len(Segrodata.Sections) > 0 { + elfphload(&Segrodata) + } + if len(Segrelrodata.Sections) > 0 { + elfphload(&Segrelrodata) + elfphrelro(&Segrelrodata) + } + elfphload(&Segdata) + + /* Dynamic linking sections */ + if !*FlagD { + sh := elfshname(".dynsym") + sh.Type = uint32(elf.SHT_DYNSYM) + sh.Flags = uint64(elf.SHF_ALLOC) + if elf64 { + sh.Entsize = ELF64SYMSIZE + } else { + sh.Entsize = ELF32SYMSIZE + } + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".dynstr").shnum) + + // sh.info is the index of first non-local symbol (number of local symbols) + s := ldr.Lookup(".dynsym", 0) + i := uint32(0) + for sub := s; sub != 0; sub = ldr.SubSym(sub) { + i++ + if !ldr.AttrLocal(sub) { + break + } + } + sh.Info = i + shsym(sh, ldr, s) + + sh = elfshname(".dynstr") + sh.Type = uint32(elf.SHT_STRTAB) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = 1 + shsym(sh, ldr, ldr.Lookup(".dynstr", 0)) + + if elfverneed != 0 { + sh := elfshname(".gnu.version") + sh.Type = uint32(elf.SHT_GNU_VERSYM) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = 2 + sh.Link = uint32(elfshname(".dynsym").shnum) + sh.Entsize = 2 + shsym(sh, ldr, ldr.Lookup(".gnu.version", 0)) + + sh = elfshname(".gnu.version_r") + sh.Type = uint32(elf.SHT_GNU_VERNEED) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Info = uint32(elfverneed) + sh.Link = uint32(elfshname(".dynstr").shnum) + shsym(sh, ldr, ldr.Lookup(".gnu.version_r", 0)) + } + + if elfRelType == ".rela" { + sh := elfshname(".rela.plt") + sh.Type = uint32(elf.SHT_RELA) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = ELF64RELASIZE + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".dynsym").shnum) + sh.Info = uint32(elfshname(".plt").shnum) + shsym(sh, ldr, ldr.Lookup(".rela.plt", 0)) + + sh = elfshname(".rela") + sh.Type = uint32(elf.SHT_RELA) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = ELF64RELASIZE + sh.Addralign = 8 + sh.Link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ldr, ldr.Lookup(".rela", 0)) + } else { + sh := elfshname(".rel.plt") + sh.Type = uint32(elf.SHT_REL) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = ELF32RELSIZE + sh.Addralign = 4 + sh.Link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ldr, ldr.Lookup(".rel.plt", 0)) + + sh = elfshname(".rel") + sh.Type = uint32(elf.SHT_REL) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = ELF32RELSIZE + sh.Addralign = 4 + sh.Link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ldr, ldr.Lookup(".rel", 0)) + } + + if elf.Machine(eh.Machine) == elf.EM_PPC64 { + sh := elfshname(".glink") + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR) + sh.Addralign = 4 + shsym(sh, ldr, ldr.Lookup(".glink", 0)) + } + + sh = elfshname(".plt") + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR) + if elf.Machine(eh.Machine) == elf.EM_X86_64 { + sh.Entsize = 16 + } else if elf.Machine(eh.Machine) == elf.EM_S390 { + sh.Entsize = 32 + } else if elf.Machine(eh.Machine) == elf.EM_PPC64 { + // On ppc64, this is just a table of addresses + // filled by the dynamic linker + sh.Type = uint32(elf.SHT_NOBITS) + + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE) + sh.Entsize = 8 + } else { + sh.Entsize = 4 + } + sh.Addralign = sh.Entsize + shsym(sh, ldr, ldr.Lookup(".plt", 0)) + + // On ppc64, .got comes from the input files, so don't + // create it here, and .got.plt is not used. + if elf.Machine(eh.Machine) != elf.EM_PPC64 { + sh := elfshname(".got") + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE) + sh.Entsize = uint64(ctxt.Arch.RegSize) + sh.Addralign = uint64(ctxt.Arch.RegSize) + shsym(sh, ldr, ldr.Lookup(".got", 0)) + + sh = elfshname(".got.plt") + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE) + sh.Entsize = uint64(ctxt.Arch.RegSize) + sh.Addralign = uint64(ctxt.Arch.RegSize) + shsym(sh, ldr, ldr.Lookup(".got.plt", 0)) + } + + sh = elfshname(".hash") + sh.Type = uint32(elf.SHT_HASH) + sh.Flags = uint64(elf.SHF_ALLOC) + sh.Entsize = 4 + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".dynsym").shnum) + shsym(sh, ldr, ldr.Lookup(".hash", 0)) + + /* sh and elf.PT_DYNAMIC for .dynamic section */ + sh = elfshname(".dynamic") + + sh.Type = uint32(elf.SHT_DYNAMIC) + sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE) + sh.Entsize = 2 * uint64(ctxt.Arch.RegSize) + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".dynstr").shnum) + shsym(sh, ldr, ldr.Lookup(".dynamic", 0)) + ph := newElfPhdr() + ph.Type = elf.PT_DYNAMIC + ph.Flags = elf.PF_R + elf.PF_W + phsh(ph, sh) + + /* + * Thread-local storage segment (really just size). + */ + tlssize := uint64(0) + for _, sect := range Segdata.Sections { + if sect.Name == ".tbss" { + tlssize = sect.Length + } + } + if tlssize != 0 { + ph := newElfPhdr() + ph.Type = elf.PT_TLS + ph.Flags = elf.PF_R + ph.Memsz = tlssize + ph.Align = uint64(ctxt.Arch.RegSize) + } + } + + if ctxt.HeadType == objabi.Hlinux { + ph := newElfPhdr() + ph.Type = elf.PT_GNU_STACK + ph.Flags = elf.PF_W + elf.PF_R + ph.Align = uint64(ctxt.Arch.RegSize) + + ph = newElfPhdr() + ph.Type = elf.PT_PAX_FLAGS + ph.Flags = 0x2a00 // mprotect, randexec, emutramp disabled + ph.Align = uint64(ctxt.Arch.RegSize) + } else if ctxt.HeadType == objabi.Hsolaris { + ph := newElfPhdr() + ph.Type = elf.PT_SUNWSTACK + ph.Flags = elf.PF_W + elf.PF_R + } + +elfobj: + sh := elfshname(".shstrtab") + sh.Type = uint32(elf.SHT_STRTAB) + sh.Addralign = 1 + shsym(sh, ldr, ldr.Lookup(".shstrtab", 0)) + eh.Shstrndx = uint16(sh.shnum) + + // put these sections early in the list + if !*FlagS { + elfshname(".symtab") + elfshname(".strtab") + } + + for _, sect := range Segtext.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segrodata.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segrelrodata.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segdata.Sections { + elfshbits(ctxt.LinkMode, sect) + } + for _, sect := range Segdwarf.Sections { + elfshbits(ctxt.LinkMode, sect) + } + + if ctxt.LinkMode == LinkExternal { + for _, sect := range Segtext.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, sect := range Segrodata.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, sect := range Segrelrodata.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, sect := range Segdata.Sections { + elfshreloc(ctxt.Arch, sect) + } + for _, si := range dwarfp { + sect := ldr.SymSect(si.secSym()) + elfshreloc(ctxt.Arch, sect) + } + // add a .note.GNU-stack section to mark the stack as non-executable + sh := elfshname(".note.GNU-stack") + + sh.Type = uint32(elf.SHT_PROGBITS) + sh.Addralign = 1 + sh.Flags = 0 + } + + if !*FlagS { + sh := elfshname(".symtab") + sh.Type = uint32(elf.SHT_SYMTAB) + sh.Off = uint64(symo) + sh.Size = uint64(symSize) + sh.Addralign = uint64(ctxt.Arch.RegSize) + sh.Entsize = 8 + 2*uint64(ctxt.Arch.RegSize) + sh.Link = uint32(elfshname(".strtab").shnum) + sh.Info = uint32(elfglobalsymndx) + + sh = elfshname(".strtab") + sh.Type = uint32(elf.SHT_STRTAB) + sh.Off = uint64(symo) + uint64(symSize) + sh.Size = uint64(len(Elfstrdat)) + sh.Addralign = 1 + } + + /* Main header */ + copy(eh.Ident[:], elf.ELFMAG) + + var osabi elf.OSABI + switch ctxt.HeadType { + case objabi.Hfreebsd: + osabi = elf.ELFOSABI_FREEBSD + case objabi.Hnetbsd: + osabi = elf.ELFOSABI_NETBSD + case objabi.Hopenbsd: + osabi = elf.ELFOSABI_OPENBSD + case objabi.Hdragonfly: + osabi = elf.ELFOSABI_NONE + } + eh.Ident[elf.EI_OSABI] = byte(osabi) + + if elf64 { + eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS64) + } else { + eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS32) + } + if ctxt.Arch.ByteOrder == binary.BigEndian { + eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2MSB) + } else { + eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2LSB) + } + eh.Ident[elf.EI_VERSION] = byte(elf.EV_CURRENT) + + if ctxt.LinkMode == LinkExternal { + eh.Type = uint16(elf.ET_REL) + } else if ctxt.BuildMode == BuildModePIE { + eh.Type = uint16(elf.ET_DYN) + } else { + eh.Type = uint16(elf.ET_EXEC) + } + + if ctxt.LinkMode != LinkExternal { + eh.Entry = uint64(Entryvalue(ctxt)) + } + + eh.Version = uint32(elf.EV_CURRENT) + + if pph != nil { + pph.Filesz = uint64(eh.Phnum) * uint64(eh.Phentsize) + pph.Memsz = pph.Filesz + } + + ctxt.Out.SeekSet(0) + a := int64(0) + a += int64(elfwritehdr(ctxt.Out)) + a += int64(elfwritephdrs(ctxt.Out)) + a += int64(elfwriteshdrs(ctxt.Out)) + if !*FlagD { + a += int64(elfwriteinterp(ctxt.Out)) + } + if ctxt.LinkMode != LinkExternal { + if ctxt.HeadType == objabi.Hnetbsd { + a += int64(elfwritenetbsdsig(ctxt.Out)) + } + if ctxt.HeadType == objabi.Hopenbsd { + a += int64(elfwriteopenbsdsig(ctxt.Out)) + } + if len(buildinfo) > 0 { + a += int64(elfwritebuildinfo(ctxt.Out)) + } + if *flagBuildid != "" { + a += int64(elfwritegobuildid(ctxt.Out)) + } + } + if *flagRace && ctxt.IsNetbsd() { + a += int64(elfwritenetbsdpax(ctxt.Out)) + } + + if a > elfreserve { + Errorf(nil, "ELFRESERVE too small: %d > %d with %d text sections", a, elfreserve, numtext) + } +} + +func elfadddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) { + ldr.SetSymDynid(s, int32(Nelfsym)) + Nelfsym++ + d := ldr.MakeSymbolUpdater(syms.DynSym) + name := ldr.SymExtname(s) + dstru := ldr.MakeSymbolUpdater(syms.DynStr) + st := ldr.SymType(s) + cgoeStatic := ldr.AttrCgoExportStatic(s) + cgoeDynamic := ldr.AttrCgoExportDynamic(s) + cgoexp := (cgoeStatic || cgoeDynamic) + + d.AddUint32(target.Arch, uint32(dstru.Addstring(name))) + + if elf64 { + + /* type */ + var t uint8 + + if cgoexp && st == sym.STEXT { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC) + } else { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT) + } + d.AddUint8(t) + + /* reserved */ + d.AddUint8(0) + + /* section where symbol is defined */ + if st == sym.SDYNIMPORT { + d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF)) + } else { + d.AddUint16(target.Arch, 1) + } + + /* value */ + if st == sym.SDYNIMPORT { + d.AddUint64(target.Arch, 0) + } else { + d.AddAddrPlus(target.Arch, s, 0) + } + + /* size of object */ + d.AddUint64(target.Arch, uint64(len(ldr.Data(s)))) + + dil := ldr.SymDynimplib(s) + + if target.Arch.Family == sys.AMD64 && !cgoeDynamic && dil != "" && !seenlib[dil] { + du := ldr.MakeSymbolUpdater(syms.Dynamic) + Elfwritedynent(target.Arch, du, elf.DT_NEEDED, uint64(dstru.Addstring(dil))) + seenlib[dil] = true + } + } else { + + /* value */ + if st == sym.SDYNIMPORT { + d.AddUint32(target.Arch, 0) + } else { + d.AddAddrPlus(target.Arch, s, 0) + } + + /* size of object */ + d.AddUint32(target.Arch, uint32(len(ldr.Data(s)))) + + /* type */ + var t uint8 + + // TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386. + if target.Arch.Family == sys.I386 && cgoexp && st == sym.STEXT { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC) + } else if target.Arch.Family == sys.ARM && cgoeDynamic && st == sym.STEXT { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC) + } else { + t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT) + } + d.AddUint8(t) + d.AddUint8(0) + + /* shndx */ + if st == sym.SDYNIMPORT { + d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF)) + } else { + d.AddUint16(target.Arch, 1) + } + } +} diff --git a/src/cmd/link/internal/ld/elf_test.go b/src/cmd/link/internal/ld/elf_test.go new file mode 100644 index 0000000..776fc1b --- /dev/null +++ b/src/cmd/link/internal/ld/elf_test.go @@ -0,0 +1,135 @@ +// +build cgo + +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "debug/elf" + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestDynSymShInfo(t *testing.T) { + t.Parallel() + testenv.MustHaveGoBuild(t) + dir, err := ioutil.TempDir("", "go-build-issue33358") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + const prog = ` +package main + +import "net" + +func main() { + net.Dial("", "") +} +` + src := filepath.Join(dir, "issue33358.go") + if err := ioutil.WriteFile(src, []byte(prog), 0666); err != nil { + t.Fatal(err) + } + + binFile := filepath.Join(dir, "issue33358") + cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", binFile, src) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("%v: %v:\n%s", cmd.Args, err, out) + } + + fi, err := os.Open(binFile) + if err != nil { + t.Fatalf("failed to open built file: %v", err) + } + + elfFile, err := elf.NewFile(fi) + if err != nil { + t.Skip("The system may not support ELF, skipped.") + } + + section := elfFile.Section(".dynsym") + if section == nil { + t.Fatal("no dynsym") + } + + symbols, err := elfFile.DynamicSymbols() + if err != nil { + t.Fatalf("failed to get dynamic symbols: %v", err) + } + + var numLocalSymbols uint32 + for i, s := range symbols { + if elf.ST_BIND(s.Info) != elf.STB_LOCAL { + numLocalSymbols = uint32(i + 1) + break + } + } + + if section.Info != numLocalSymbols { + t.Fatalf("Unexpected sh info, want greater than 0, got: %d", section.Info) + } +} + +func TestNoDuplicateNeededEntries(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + // run this test on just a small set of platforms (no need to test it + // across the board given the nature of the test). + pair := runtime.GOOS + "-" + runtime.GOARCH + switch pair { + case "linux-amd64", "freebsd-amd64", "openbsd-amd64": + default: + t.Skip("no need for test on " + pair) + } + + t.Parallel() + + dir, err := ioutil.TempDir("", "no-dup-needed") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(dir) + + wd, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get working directory: %v", err) + } + + path := filepath.Join(dir, "x") + argv := []string{"build", "-o", path, filepath.Join(wd, "testdata", "issue39256")} + out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput() + if err != nil { + t.Fatalf("Build failure: %s\n%s\n", err, string(out)) + } + + f, err := elf.Open(path) + if err != nil { + t.Fatalf("Failed to open ELF file: %v", err) + } + libs, err := f.ImportedLibraries() + if err != nil { + t.Fatalf("Failed to read imported libraries: %v", err) + } + + var count int + for _, lib := range libs { + if lib == "libc.so" || strings.HasPrefix(lib, "libc.so.") { + count++ + } + } + + if got, want := count, 1; got != want { + t.Errorf("Got %d entries for `libc.so`, want %d", got, want) + } +} diff --git a/src/cmd/link/internal/ld/errors.go b/src/cmd/link/internal/ld/errors.go new file mode 100644 index 0000000..d6e8ff2 --- /dev/null +++ b/src/cmd/link/internal/ld/errors.go @@ -0,0 +1,66 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/obj" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "sync" +) + +type unresolvedSymKey struct { + from loader.Sym // Symbol that referenced unresolved "to" + to loader.Sym // Unresolved symbol referenced by "from" +} + +type symNameFn func(s loader.Sym) string + +// ErrorReporter is used to make error reporting thread safe. +type ErrorReporter struct { + loader.ErrorReporter + unresOnce sync.Once + unresSyms map[unresolvedSymKey]bool + unresMutex sync.Mutex + SymName symNameFn +} + +// errorUnresolved prints unresolved symbol error for rs that is referenced from s. +func (reporter *ErrorReporter) errorUnresolved(ldr *loader.Loader, s, rs loader.Sym) { + reporter.unresOnce.Do(func() { reporter.unresSyms = make(map[unresolvedSymKey]bool) }) + + k := unresolvedSymKey{from: s, to: rs} + reporter.unresMutex.Lock() + defer reporter.unresMutex.Unlock() + if !reporter.unresSyms[k] { + reporter.unresSyms[k] = true + name := ldr.SymName(rs) + + // Try to find symbol under another ABI. + var reqABI, haveABI obj.ABI + haveABI = ^obj.ABI(0) + reqABI, ok := sym.VersionToABI(ldr.SymVersion(rs)) + if ok { + for abi := obj.ABI(0); abi < obj.ABICount; abi++ { + v := sym.ABIToVersion(abi) + if v == -1 { + continue + } + if rs1 := ldr.Lookup(name, v); rs1 != 0 && ldr.SymType(rs1) != sym.Sxxx && ldr.SymType(rs1) != sym.SXREF { + haveABI = abi + } + } + } + + // Give a special error message for main symbol (see #24809). + if name == "main.main" { + reporter.Errorf(s, "function main is undeclared in the main package") + } else if haveABI != ^obj.ABI(0) { + reporter.Errorf(s, "relocation target %s not defined for %s (but is defined for %s)", name, reqABI, haveABI) + } else { + reporter.Errorf(s, "relocation target %s not defined", name) + } + } +} diff --git a/src/cmd/link/internal/ld/execarchive.go b/src/cmd/link/internal/ld/execarchive.go new file mode 100644 index 0000000..4687c62 --- /dev/null +++ b/src/cmd/link/internal/ld/execarchive.go @@ -0,0 +1,37 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !wasm,!windows + +package ld + +import ( + exec "internal/execabs" + "os" + "path/filepath" + "syscall" +) + +const syscallExecSupported = true + +// execArchive invokes the archiver tool with syscall.Exec(), with +// the expectation that this is the last thing that takes place +// in the linking operation. +func (ctxt *Link) execArchive(argv []string) { + var err error + argv0 := argv[0] + if filepath.Base(argv0) == argv0 { + argv0, err = exec.LookPath(argv0) + if err != nil { + Exitf("cannot find %s: %v", argv[0], err) + } + } + if ctxt.Debugvlog != 0 { + ctxt.Logf("invoking archiver with syscall.Exec()\n") + } + err = syscall.Exec(argv0, argv, os.Environ()) + if err != nil { + Exitf("running %s failed: %v", argv[0], err) + } +} diff --git a/src/cmd/link/internal/ld/execarchive_noexec.go b/src/cmd/link/internal/ld/execarchive_noexec.go new file mode 100644 index 0000000..a70dea9 --- /dev/null +++ b/src/cmd/link/internal/ld/execarchive_noexec.go @@ -0,0 +1,13 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build wasm windows + +package ld + +const syscallExecSupported = false + +func (ctxt *Link) execArchive(argv []string) { + panic("should never arrive here") +} diff --git a/src/cmd/link/internal/ld/fallocate_test.go b/src/cmd/link/internal/ld/fallocate_test.go new file mode 100644 index 0000000..244b70f --- /dev/null +++ b/src/cmd/link/internal/ld/fallocate_test.go @@ -0,0 +1,69 @@ +// 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 linux + +package ld + +import ( + "io/ioutil" + "os" + "path/filepath" + "syscall" + "testing" +) + +func TestFallocate(t *testing.T) { + dir, err := ioutil.TempDir("", "TestFallocate") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + filename := filepath.Join(dir, "a.out") + out := NewOutBuf(nil) + err = out.Open(filename) + if err != nil { + t.Fatalf("Open file failed: %v", err) + } + defer out.Close() + + // Try fallocate first. + for { + err = out.fallocate(1 << 10) + if err == syscall.EOPNOTSUPP { // The underlying file system may not support fallocate + t.Skip("fallocate is not supported") + } + if err == syscall.EINTR { + continue // try again + } + if err != nil { + t.Fatalf("fallocate failed: %v", err) + } + break + } + + // Mmap 1 MiB initially, and grow to 2 and 3 MiB. + // Check if the file size and disk usage is expected. + for _, sz := range []int64{1 << 20, 2 << 20, 3 << 20} { + err = out.Mmap(uint64(sz)) + if err != nil { + t.Fatalf("Mmap failed: %v", err) + } + stat, err := os.Stat(filename) + if err != nil { + t.Fatalf("Stat failed: %v", err) + } + if got := stat.Size(); got != sz { + t.Errorf("unexpected file size: got %d, want %d", got, sz) + } + // The number of blocks must be enough for the requested size. + // We used to require an exact match, but it appears that + // some file systems allocate a few extra blocks in some cases. + // See issue #41127. + if got, want := stat.Sys().(*syscall.Stat_t).Blocks, (sz+511)/512; got < want { + t.Errorf("unexpected disk usage: got %d blocks, want at least %d", got, want) + } + out.munmap() + } +} diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go new file mode 100644 index 0000000..fbc7a78 --- /dev/null +++ b/src/cmd/link/internal/ld/go.go @@ -0,0 +1,494 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go-specific code shared across loaders (5l, 6l, 8l). + +package ld + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "encoding/json" + "fmt" + "io" + "os" + "sort" + "strconv" + "strings" +) + +// go-specific code shared across loaders (5l, 6l, 8l). + +// replace all "". with pkg. +func expandpkg(t0 string, pkg string) string { + return strings.Replace(t0, `"".`, pkg+".", -1) +} + +// TODO: +// generate debugging section in binary. +// once the dust settles, try to move some code to +// libmach, so that other linkers and ar can share. + +func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename string) { + if *flagG { + return + } + + if int64(int(length)) != length { + fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename) + return + } + + bdata := make([]byte, length) + if _, err := io.ReadFull(f, bdata); err != nil { + fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) + return + } + data := string(bdata) + + // process header lines + for data != "" { + var line string + if i := strings.Index(data, "\n"); i >= 0 { + line, data = data[:i], data[i+1:] + } else { + line, data = data, "" + } + if line == "main" { + lib.Main = true + } + if line == "" { + break + } + } + + // look for cgo section + p0 := strings.Index(data, "\n$$ // cgo") + var p1 int + if p0 >= 0 { + p0 += p1 + i := strings.IndexByte(data[p0+1:], '\n') + if i < 0 { + fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename) + return + } + p0 += 1 + i + + p1 = strings.Index(data[p0:], "\n$$") + if p1 < 0 { + p1 = strings.Index(data[p0:], "\n!\n") + } + if p1 < 0 { + fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename) + return + } + p1 += p0 + loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1]) + } +} + +func loadcgo(ctxt *Link, file string, pkg string, p string) { + var directives [][]string + if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil { + fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err) + nerrors++ + return + } + + // Find cgo_export symbols. They are roots in the deadcode pass. + for _, f := range directives { + switch f[0] { + case "cgo_export_static", "cgo_export_dynamic": + if len(f) < 2 || len(f) > 3 { + continue + } + local := f[1] + switch ctxt.BuildMode { + case BuildModeCShared, BuildModeCArchive, BuildModePlugin: + if local == "main" { + continue + } + } + local = expandpkg(local, pkg) + if f[0] == "cgo_export_static" { + ctxt.cgo_export_static[local] = true + } else { + ctxt.cgo_export_dynamic[local] = true + } + } + } + + // Record the directives. We'll process them later after Symbols are created. + ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives}) +} + +// Set symbol attributes or flags based on cgo directives. +// Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'. +func setCgoAttr(ctxt *Link, lookup func(string, int) loader.Sym, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) { + l := ctxt.loader + for _, f := range directives { + switch f[0] { + case "cgo_import_dynamic": + if len(f) < 2 || len(f) > 4 { + break + } + + local := f[1] + remote := local + if len(f) > 2 { + remote = f[2] + } + lib := "" + if len(f) > 3 { + lib = f[3] + } + + if *FlagD { + fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file) + nerrors++ + return + } + + if local == "_" && remote == "_" { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + havedynamic = 1 + + if ctxt.HeadType == objabi.Hdarwin { + machoadddynlib(lib, ctxt.LinkMode) + } else { + dynlib = append(dynlib, lib) + } + continue + } + + local = expandpkg(local, pkg) + q := "" + if i := strings.Index(remote, "#"); i >= 0 { + remote, q = remote[:i], remote[i+1:] + } + s := lookup(local, 0) + st := l.SymType(s) + if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ { + l.SetSymDynimplib(s, lib) + l.SetSymExtname(s, remote) + l.SetSymDynimpvers(s, q) + if st != sym.SHOSTOBJ { + su := l.MakeSymbolUpdater(s) + su.SetType(sym.SDYNIMPORT) + } else { + hostObjSyms[s] = struct{}{} + } + havedynamic = 1 + if lib != "" && ctxt.IsDarwin() { + machoadddynlib(lib, ctxt.LinkMode) + } + } + + continue + + case "cgo_import_static": + if len(f) != 2 { + break + } + local := f[1] + + s := lookup(local, 0) + su := l.MakeSymbolUpdater(s) + su.SetType(sym.SHOSTOBJ) + su.SetSize(0) + hostObjSyms[s] = struct{}{} + continue + + case "cgo_export_static", "cgo_export_dynamic": + if len(f) < 2 || len(f) > 3 { + break + } + local := f[1] + remote := local + if len(f) > 2 { + remote = f[2] + } + local = expandpkg(local, pkg) + + // The compiler arranges for an ABI0 wrapper + // to be available for all cgo-exported + // functions. Link.loadlib will resolve any + // ABI aliases we find here (since we may not + // yet know it's an alias). + s := lookup(local, 0) + + if l.SymType(s) == sym.SHOSTOBJ { + hostObjSyms[s] = struct{}{} + } + + switch ctxt.BuildMode { + case BuildModeCShared, BuildModeCArchive, BuildModePlugin: + if s == lookup("main", 0) { + continue + } + } + + // export overrides import, for openbsd/cgo. + // see issue 4878. + if l.SymDynimplib(s) != "" { + l.SetSymDynimplib(s, "") + l.SetSymDynimpvers(s, "") + l.SetSymExtname(s, "") + var su *loader.SymbolBuilder + su = l.MakeSymbolUpdater(s) + su.SetType(0) + } + + if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) { + l.SetSymExtname(s, remote) + } else if l.SymExtname(s) != remote { + fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote) + nerrors++ + return + } + + if f[0] == "cgo_export_static" { + l.SetAttrCgoExportStatic(s, true) + } else { + l.SetAttrCgoExportDynamic(s, true) + } + continue + + case "cgo_dynamic_linker": + if len(f) != 2 { + break + } + + if *flagInterpreter == "" { + if interpreter != "" && interpreter != f[1] { + fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1]) + nerrors++ + return + } + + interpreter = f[1] + } + continue + + case "cgo_ldflag": + if len(f) != 2 { + break + } + ldflag = append(ldflag, f[1]) + continue + } + + fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f) + nerrors++ + } + return +} + +// openbsdTrimLibVersion indicates whether a shared library is +// versioned and if it is, returns the unversioned name. The +// OpenBSD library naming scheme is lib<name>.so.<major>.<minor> +func openbsdTrimLibVersion(lib string) (string, bool) { + parts := strings.Split(lib, ".") + if len(parts) != 4 { + return "", false + } + if parts[1] != "so" { + return "", false + } + if _, err := strconv.Atoi(parts[2]); err != nil { + return "", false + } + if _, err := strconv.Atoi(parts[3]); err != nil { + return "", false + } + return fmt.Sprintf("%s.%s", parts[0], parts[1]), true +} + +// dedupLibrariesOpenBSD dedups a list of shared libraries, treating versioned +// and unversioned libraries as equivalents. Versioned libraries are preferred +// and retained over unversioned libraries. This avoids the situation where +// the use of cgo results in a DT_NEEDED for a versioned library (for example, +// libc.so.96.1), while a dynamic import specifies an unversioned library (for +// example, libc.so) - this would otherwise result in two DT_NEEDED entries +// for the same library, resulting in a failure when ld.so attempts to load +// the Go binary. +func dedupLibrariesOpenBSD(ctxt *Link, libs []string) []string { + libraries := make(map[string]string) + for _, lib := range libs { + if name, ok := openbsdTrimLibVersion(lib); ok { + // Record unversioned name as seen. + seenlib[name] = true + libraries[name] = lib + } else if _, ok := libraries[lib]; !ok { + libraries[lib] = lib + } + } + + libs = nil + for _, lib := range libraries { + libs = append(libs, lib) + } + sort.Strings(libs) + + return libs +} + +func dedupLibraries(ctxt *Link, libs []string) []string { + if ctxt.Target.IsOpenbsd() { + return dedupLibrariesOpenBSD(ctxt, libs) + } + return libs +} + +var seenlib = make(map[string]bool) + +func adddynlib(ctxt *Link, lib string) { + if seenlib[lib] || ctxt.LinkMode == LinkExternal { + return + } + seenlib[lib] = true + + if ctxt.IsELF { + dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr) + if dsu.Size() == 0 { + dsu.Addstring("") + } + du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic) + Elfwritedynent(ctxt.Arch, du, elf.DT_NEEDED, uint64(dsu.Addstring(lib))) + } else { + Errorf(nil, "adddynlib: unsupported binary format") + } +} + +func Adddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) { + if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal { + return + } + + if target.IsELF { + elfadddynsym(ldr, target, syms, s) + } else if target.HeadType == objabi.Hdarwin { + ldr.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s)) + } else if target.HeadType == objabi.Hwindows { + // already taken care of + } else { + ldr.Errorf(s, "adddynsym: unsupported binary format") + } +} + +func fieldtrack(arch *sys.Arch, l *loader.Loader) { + var buf bytes.Buffer + for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ { + if name := l.SymName(i); strings.HasPrefix(name, "go.track.") { + if l.AttrReachable(i) { + l.SetAttrSpecial(i, true) + l.SetAttrNotInSymbolTable(i, true) + buf.WriteString(name[9:]) + for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] { + buf.WriteString("\t") + buf.WriteString(l.SymName(p)) + } + buf.WriteString("\n") + } + } + } + l.Reachparent = nil // we are done with it + if *flagFieldTrack == "" { + return + } + s := l.Lookup(*flagFieldTrack, 0) + if s == 0 || !l.AttrReachable(s) { + return + } + bld := l.MakeSymbolUpdater(s) + bld.SetType(sym.SDATA) + addstrdata(arch, l, *flagFieldTrack, buf.String()) +} + +func (ctxt *Link) addexport() { + // Track undefined external symbols during external link. + if ctxt.LinkMode == LinkExternal { + for _, s := range ctxt.Textp { + if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) { + continue + } + relocs := ctxt.loader.Relocs(s) + for i := 0; i < relocs.Count(); i++ { + if rs := relocs.At(i).Sym(); rs != 0 { + if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) { + // sanity check + if len(ctxt.loader.Data(rs)) != 0 { + panic("expected no data on undef symbol") + } + su := ctxt.loader.MakeSymbolUpdater(rs) + su.SetType(sym.SUNDEFEXT) + } + } + } + } + } + + // TODO(aix) + if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix { + return + } + + for _, exp := range ctxt.dynexp { + Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, exp) + } + for _, lib := range dedupLibraries(ctxt, dynlib) { + adddynlib(ctxt, lib) + } +} + +type Pkg struct { + mark bool + checked bool + path string + impby []*Pkg +} + +var pkgall []*Pkg + +func (p *Pkg) cycle() *Pkg { + if p.checked { + return nil + } + + if p.mark { + nerrors++ + fmt.Printf("import cycle:\n") + fmt.Printf("\t%s\n", p.path) + return p + } + + p.mark = true + for _, q := range p.impby { + if bad := q.cycle(); bad != nil { + p.mark = false + p.checked = true + fmt.Printf("\timports %s\n", p.path) + if bad == p { + return nil + } + return bad + } + } + + p.checked = true + p.mark = false + return nil +} + +func importcycles() { + for _, p := range pkgall { + p.cycle() + } +} diff --git a/src/cmd/link/internal/ld/go_test.go b/src/cmd/link/internal/ld/go_test.go new file mode 100644 index 0000000..0197196 --- /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/heap.go b/src/cmd/link/internal/ld/heap.go new file mode 100644 index 0000000..ea2d772 --- /dev/null +++ b/src/cmd/link/internal/ld/heap.go @@ -0,0 +1,54 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import "cmd/link/internal/loader" + +// Min-heap implementation, for the deadcode pass. +// Specialized for loader.Sym elements. + +type heap []loader.Sym + +func (h *heap) push(s loader.Sym) { + *h = append(*h, s) + // sift up + n := len(*h) - 1 + for n > 0 { + p := (n - 1) / 2 // parent + if (*h)[p] <= (*h)[n] { + break + } + (*h)[n], (*h)[p] = (*h)[p], (*h)[n] + n = p + } +} + +func (h *heap) pop() loader.Sym { + r := (*h)[0] + n := len(*h) - 1 + (*h)[0] = (*h)[n] + *h = (*h)[:n] + + // sift down + i := 0 + for { + c := 2*i + 1 // left child + if c >= n { + break + } + if c1 := c + 1; c1 < n && (*h)[c1] < (*h)[c] { + c = c1 // right child + } + if (*h)[i] <= (*h)[c] { + break + } + (*h)[i], (*h)[c] = (*h)[c], (*h)[i] + i = c + } + + return r +} + +func (h *heap) empty() bool { return len(*h) == 0 } diff --git a/src/cmd/link/internal/ld/heap_test.go b/src/cmd/link/internal/ld/heap_test.go new file mode 100644 index 0000000..08c9030 --- /dev/null +++ b/src/cmd/link/internal/ld/heap_test.go @@ -0,0 +1,90 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/link/internal/loader" + "testing" +) + +func TestHeap(t *testing.T) { + tests := [][]loader.Sym{ + {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, + {100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, + {30, 50, 80, 20, 60, 70, 10, 100, 90, 40}, + } + for _, s := range tests { + h := heap{} + for _, i := range s { + h.push(i) + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + } + for j := 0; j < len(s); j++ { + x := h.pop() + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + // pop should return elements in ascending order. + if want := loader.Sym((j + 1) * 10); x != want { + t.Errorf("pop returns wrong element: want %d, got %d", want, x) + } + } + if !h.empty() { + t.Errorf("heap is not empty after all pops") + } + } + + // Also check that mixed pushes and pops work correctly. + for _, s := range tests { + h := heap{} + for i := 0; i < len(s)/2; i++ { + // two pushes, one pop + h.push(s[2*i]) + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + h.push(s[2*i+1]) + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + h.pop() + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + } + for !h.empty() { // pop remaining elements + h.pop() + if !verify(&h, 0) { + t.Errorf("heap invariant violated: %v", h) + } + } + } +} + +// recursively verify heap-ness, starting at element i. +func verify(h *heap, i int) bool { + n := len(*h) + c1 := 2*i + 1 // left child + c2 := 2*i + 2 // right child + if c1 < n { + if (*h)[c1] < (*h)[i] { + return false + } + if !verify(h, c1) { + return false + } + } + if c2 < n { + if (*h)[c2] < (*h)[i] { + return false + } + if !verify(h, c2) { + return false + } + } + return true +} diff --git a/src/cmd/link/internal/ld/issue33808_test.go b/src/cmd/link/internal/ld/issue33808_test.go new file mode 100644 index 0000000..92a47fa --- /dev/null +++ b/src/cmd/link/internal/ld/issue33808_test.go @@ -0,0 +1,55 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "internal/testenv" + "io/ioutil" + "os" + "runtime" + "strings" + "testing" +) + +const prog = ` +package main + +import "log" + +func main() { + log.Fatalf("HERE") +} +` + +func TestIssue33808(t *testing.T) { + if runtime.GOOS != "darwin" { + return + } + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + t.Parallel() + + dir, err := ioutil.TempDir("", "TestIssue33808") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + f := gobuild(t, dir, prog, "-ldflags=-linkmode=external") + f.Close() + + syms, err := f.Symbols() + if err != nil { + t.Fatalf("Error reading symbols: %v", err) + } + + name := "log.Fatalf" + for _, sym := range syms { + if strings.Contains(sym.Name, name) { + return + } + } + t.Fatalf("Didn't find %v", name) +} diff --git a/src/cmd/link/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go new file mode 100644 index 0000000..7ff9c41 --- /dev/null +++ b/src/cmd/link/internal/ld/ld.go @@ -0,0 +1,271 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/goobj" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strconv" + "strings" +) + +func (ctxt *Link) readImportCfg(file string) { + ctxt.PackageFile = make(map[string]string) + ctxt.PackageShlib = make(map[string]string) + data, err := ioutil.ReadFile(file) + if err != nil { + log.Fatalf("-importcfg: %v", err) + } + + for lineNum, line := range strings.Split(string(data), "\n") { + lineNum++ // 1-based + line = strings.TrimSpace(line) + if line == "" { + continue + } + if line == "" || strings.HasPrefix(line, "#") { + continue + } + + var verb, args string + if i := strings.Index(line, " "); i < 0 { + verb = line + } else { + verb, args = line[:i], strings.TrimSpace(line[i+1:]) + } + var before, after string + if i := strings.Index(args, "="); i >= 0 { + before, after = args[:i], args[i+1:] + } + switch verb { + default: + log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb) + case "packagefile": + if before == "" || after == "" { + log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum) + } + ctxt.PackageFile[before] = after + case "packageshlib": + if before == "" || after == "" { + log.Fatalf(`%s:%d: invalid packageshlib: syntax is "packageshlib path=filename"`, file, lineNum) + } + ctxt.PackageShlib[before] = after + } + } +} + +func pkgname(ctxt *Link, lib string) string { + name := path.Clean(lib) + + // When using importcfg, we have the final package name. + if ctxt.PackageFile != nil { + return name + } + + // runtime.a -> runtime, runtime.6 -> runtime + pkg := name + if len(pkg) >= 2 && pkg[len(pkg)-2] == '.' { + pkg = pkg[:len(pkg)-2] + } + return pkg +} + +func findlib(ctxt *Link, lib string) (string, bool) { + name := path.Clean(lib) + + var pname string + isshlib := false + + if ctxt.linkShared && ctxt.PackageShlib[name] != "" { + pname = ctxt.PackageShlib[name] + isshlib = true + } else if ctxt.PackageFile != nil { + pname = ctxt.PackageFile[name] + if pname == "" { + ctxt.Logf("cannot find package %s (using -importcfg)\n", name) + return "", false + } + } else { + if filepath.IsAbs(name) { + pname = name + } else { + pkg := pkgname(ctxt, lib) + // Add .a if needed; the new -importcfg modes + // do not put .a into the package name anymore. + // This only matters when people try to mix + // compiles using -importcfg with links not using -importcfg, + // such as when running quick things like + // 'go tool compile x.go && go tool link x.o' + // by hand against a standard library built using -importcfg. + if !strings.HasSuffix(name, ".a") && !strings.HasSuffix(name, ".o") { + name += ".a" + } + // try dot, -L "libdir", and then goroot. + for _, dir := range ctxt.Libdir { + if ctxt.linkShared { + pname = filepath.Join(dir, pkg+".shlibname") + if _, err := os.Stat(pname); err == nil { + isshlib = true + break + } + } + pname = filepath.Join(dir, name) + if _, err := os.Stat(pname); err == nil { + break + } + } + } + pname = filepath.Clean(pname) + } + + return pname, isshlib +} + +func addlib(ctxt *Link, src, obj, lib string, fingerprint goobj.FingerprintType) *sym.Library { + pkg := pkgname(ctxt, lib) + + // already loaded? + if l := ctxt.LibraryByPkg[pkg]; l != nil && !l.Fingerprint.IsZero() { + // Normally, packages are loaded in dependency order, and if l != nil + // l is already loaded with the actual fingerprint. In shared build mode, + // however, packages may be added not in dependency order, and it is + // possible that l's fingerprint is not yet loaded -- exclude it in + // checking. + checkFingerprint(l, l.Fingerprint, src, fingerprint) + return l + } + + pname, isshlib := findlib(ctxt, lib) + + if ctxt.Debugvlog > 1 { + ctxt.Logf("addlib: %s %s pulls in %s isshlib %v\n", obj, src, pname, isshlib) + } + + if isshlib { + return addlibpath(ctxt, src, obj, "", pkg, pname, fingerprint) + } + return addlibpath(ctxt, src, obj, pname, pkg, "", fingerprint) +} + +/* + * add library to library list, return added library. + * srcref: src file referring to package + * objref: object file referring to package + * file: object file, e.g., /home/rsc/go/pkg/container/vector.a + * pkg: package import path, e.g. container/vector + * shlib: path to shared library, or .shlibname file holding path + * fingerprint: if not 0, expected fingerprint for import from srcref + * fingerprint is 0 if the library is not imported (e.g. main) + */ +func addlibpath(ctxt *Link, srcref, objref, file, pkg, shlib string, fingerprint goobj.FingerprintType) *sym.Library { + if l := ctxt.LibraryByPkg[pkg]; l != nil { + return l + } + + if ctxt.Debugvlog > 1 { + ctxt.Logf("addlibpath: srcref: %s objref: %s file: %s pkg: %s shlib: %s fingerprint: %x\n", srcref, objref, file, pkg, shlib, fingerprint) + } + + l := &sym.Library{} + ctxt.LibraryByPkg[pkg] = l + ctxt.Library = append(ctxt.Library, l) + l.Objref = objref + l.Srcref = srcref + l.File = file + l.Pkg = pkg + l.Fingerprint = fingerprint + if shlib != "" { + if strings.HasSuffix(shlib, ".shlibname") { + data, err := ioutil.ReadFile(shlib) + if err != nil { + Errorf(nil, "cannot read %s: %v", shlib, err) + } + shlib = strings.TrimSpace(string(data)) + } + l.Shlib = shlib + } + return l +} + +func atolwhex(s string) int64 { + n, _ := strconv.ParseInt(s, 0, 64) + return n +} + +// PrepareAddmoduledata returns a symbol builder that target-specific +// code can use to build up the linker-generated go.link.addmoduledata +// function, along with the sym for runtime.addmoduledata itself. If +// this function is not needed (for example in cases where we're +// linking a module that contains the runtime) the returned builder +// will be nil. +func PrepareAddmoduledata(ctxt *Link) (*loader.SymbolBuilder, loader.Sym) { + if !ctxt.DynlinkingGo() { + return nil, 0 + } + amd := ctxt.loader.LookupOrCreateSym("runtime.addmoduledata", 0) + if ctxt.loader.SymType(amd) == sym.STEXT && ctxt.BuildMode != BuildModePlugin { + // we're linking a module containing the runtime -> no need for + // an init function + return nil, 0 + } + ctxt.loader.SetAttrReachable(amd, true) + + // Create a new init func text symbol. Caller will populate this + // sym with arch-specific content. + ifs := ctxt.loader.LookupOrCreateSym("go.link.addmoduledata", 0) + initfunc := ctxt.loader.MakeSymbolUpdater(ifs) + ctxt.loader.SetAttrReachable(ifs, true) + ctxt.loader.SetAttrLocal(ifs, true) + initfunc.SetType(sym.STEXT) + + // Add the init func and/or addmoduledata to Textp. + if ctxt.BuildMode == BuildModePlugin { + ctxt.Textp = append(ctxt.Textp, amd) + } + ctxt.Textp = append(ctxt.Textp, initfunc.Sym()) + + // Create an init array entry + amdi := ctxt.loader.LookupOrCreateSym("go.link.addmoduledatainit", 0) + initarray_entry := ctxt.loader.MakeSymbolUpdater(amdi) + ctxt.loader.SetAttrReachable(amdi, true) + ctxt.loader.SetAttrLocal(amdi, true) + initarray_entry.SetType(sym.SINITARR) + initarray_entry.AddAddr(ctxt.Arch, ifs) + + return initfunc, amd +} diff --git a/src/cmd/link/internal/ld/ld_test.go b/src/cmd/link/internal/ld/ld_test.go new file mode 100644 index 0000000..cdfaadb --- /dev/null +++ b/src/cmd/link/internal/ld/ld_test.go @@ -0,0 +1,244 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "debug/pe" + "fmt" + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestUndefinedRelocErrors(t *testing.T) { + testenv.MustHaveGoBuild(t) + + // When external linking, symbols may be defined externally, so we allow + // undefined symbols and let external linker resolve. Skip the test. + testenv.MustInternalLink(t) + + t.Parallel() + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + out, err := exec.Command(testenv.GoToolPath(t), "build", "./testdata/issue10978").CombinedOutput() + if err == nil { + t.Fatal("expected build to fail") + } + + wantErrors := map[string]int{ + // Main function has dedicated error message. + "function main is undeclared in the main package": 1, + + // Single error reporting per each symbol. + // This way, duplicated messages are not reported for + // multiple relocations with a same name. + "main.defined1: relocation target main.undefined not defined": 1, + "main.defined2: relocation target main.undefined not defined": 1, + } + unexpectedErrors := map[string]int{} + + for _, l := range strings.Split(string(out), "\n") { + if strings.HasPrefix(l, "#") || l == "" { + continue + } + matched := "" + for want := range wantErrors { + if strings.Contains(l, want) { + matched = want + break + } + } + if matched != "" { + wantErrors[matched]-- + } else { + unexpectedErrors[l]++ + } + } + + for want, n := range wantErrors { + switch { + case n > 0: + t.Errorf("unmatched error: %s (x%d)", want, n) + case n < 0: + t.Errorf("extra errors: %s (x%d)", want, -n) + } + } + for unexpected, n := range unexpectedErrors { + t.Errorf("unexpected error: %s (x%d)", unexpected, n) + } +} + +const carchiveSrcText = ` +package main + +//export GoFunc +func GoFunc() { + println(42) +} + +func main() { +} +` + +func TestArchiveBuildInvokeWithExec(t *testing.T) { + t.Parallel() + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + // run this test on just a small set of platforms (no need to test it + // across the board given the nature of the test). + pair := runtime.GOOS + "-" + runtime.GOARCH + switch pair { + case "darwin-amd64", "darwin-arm64", "linux-amd64", "freebsd-amd64": + default: + t.Skip("no need for test on " + pair) + } + switch runtime.GOOS { + case "openbsd", "windows": + t.Skip("c-archive unsupported") + } + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + srcfile := filepath.Join(dir, "test.go") + arfile := filepath.Join(dir, "test.a") + if err := ioutil.WriteFile(srcfile, []byte(carchiveSrcText), 0666); err != nil { + t.Fatal(err) + } + + ldf := fmt.Sprintf("-ldflags=-v -tmpdir=%s", dir) + argv := []string{"build", "-buildmode=c-archive", "-o", arfile, ldf, srcfile} + out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput() + if err != nil { + t.Fatalf("build failure: %s\n%s\n", err, string(out)) + } + + found := false + const want = "invoking archiver with syscall.Exec" + for _, l := range strings.Split(string(out), "\n") { + if strings.HasPrefix(l, want) { + found = true + break + } + } + + if !found { + t.Errorf("expected '%s' in -v output, got:\n%s\n", want, string(out)) + } +} + +func TestPPC64LargeTextSectionSplitting(t *testing.T) { + // The behavior we're checking for is of interest only on ppc64. + if !strings.HasPrefix(runtime.GOARCH, "ppc64") { + t.Skip("test useful only for ppc64") + } + + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + t.Parallel() + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + // NB: the use of -ldflags=-debugppc64textsize=1048576 tells the linker to + // split text sections at a size threshold of 1M instead of the + // architected limit of 67M. The choice of building cmd/go is + // arbitrary; we just need something sufficiently large that uses + // external linking. + exe := filepath.Join(dir, "go.exe") + out, eerr := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "-ldflags=-linkmode=external -debugppc64textsize=1048576", "cmd/go").CombinedOutput() + if eerr != nil { + t.Fatalf("build failure: %s\n%s\n", eerr, string(out)) + } + + // Result should be runnable. + _, err = exec.Command(exe, "version").CombinedOutput() + if err != nil { + t.Fatal(err) + } +} + +func TestWindowsBuildmodeCSharedASLR(t *testing.T) { + platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) + switch platform { + case "windows/amd64", "windows/386": + default: + t.Skip("skipping windows amd64/386 only test") + } + + 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 new file mode 100644 index 0000000..18db567 --- /dev/null +++ b/src/cmd/link/internal/ld/lib.go @@ -0,0 +1,2573 @@ +// Inferno utils/8l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/goobj" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loadelf" + "cmd/link/internal/loader" + "cmd/link/internal/loadmacho" + "cmd/link/internal/loadpe" + "cmd/link/internal/loadxcoff" + "cmd/link/internal/sym" + "crypto/sha1" + "debug/elf" + "debug/macho" + "encoding/base64" + "encoding/binary" + "fmt" + exec "internal/execabs" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "runtime" + "sort" + "strings" + "sync" +) + +// Data layout and relocation. + +// Derived from Inferno utils/6l/l.h +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// ArchSyms holds a number of architecture specific symbols used during +// relocation. Rather than allowing them universal access to all symbols, +// we keep a subset for relocation application. +type ArchSyms struct { + Rel loader.Sym + Rela loader.Sym + RelPLT loader.Sym + RelaPLT loader.Sym + + LinkEditGOT loader.Sym + LinkEditPLT loader.Sym + + TOC loader.Sym + DotTOC []loader.Sym // for each version + + GOT loader.Sym + PLT loader.Sym + GOTPLT loader.Sym + + Tlsg loader.Sym + Tlsoffset int + + Dynamic loader.Sym + DynSym loader.Sym + DynStr loader.Sym +} + +// mkArchSym is a helper for setArchSyms, to set up a special symbol. +func (ctxt *Link) mkArchSym(name string, ver int, ls *loader.Sym) { + *ls = ctxt.loader.LookupOrCreateSym(name, ver) + ctxt.loader.SetAttrReachable(*ls, true) +} + +// mkArchVecSym is similar to setArchSyms, but operates on elements within +// a slice, where each element corresponds to some symbol version. +func (ctxt *Link) mkArchSymVec(name string, ver int, ls []loader.Sym) { + ls[ver] = ctxt.loader.LookupOrCreateSym(name, ver) + ctxt.loader.SetAttrReachable(ls[ver], true) +} + +// setArchSyms sets up the ArchSyms structure, and must be called before +// relocations are applied. +func (ctxt *Link) setArchSyms() { + ctxt.mkArchSym(".got", 0, &ctxt.GOT) + ctxt.mkArchSym(".plt", 0, &ctxt.PLT) + ctxt.mkArchSym(".got.plt", 0, &ctxt.GOTPLT) + ctxt.mkArchSym(".dynamic", 0, &ctxt.Dynamic) + ctxt.mkArchSym(".dynsym", 0, &ctxt.DynSym) + ctxt.mkArchSym(".dynstr", 0, &ctxt.DynStr) + + if ctxt.IsPPC64() { + ctxt.mkArchSym("TOC", 0, &ctxt.TOC) + + // NB: note the +2 below for DotTOC2 compared to the +1 for + // DocTOC. This is because loadlibfull() creates an additional + // syms version during conversion of loader.Sym symbols to + // *sym.Symbol symbols. Symbols that are assigned this final + // version are not going to have TOC references, so it should + // be ok for them to inherit an invalid .TOC. symbol. + // TODO: revisit the +2, now that loadlibfull is gone. + ctxt.DotTOC = make([]loader.Sym, ctxt.MaxVersion()+2) + for i := 0; i <= ctxt.MaxVersion(); i++ { + if i >= 2 && i < sym.SymVerStatic { // these versions are not used currently + continue + } + ctxt.mkArchSymVec(".TOC.", i, ctxt.DotTOC) + } + } + if ctxt.IsElf() { + ctxt.mkArchSym(".rel", 0, &ctxt.Rel) + ctxt.mkArchSym(".rela", 0, &ctxt.Rela) + ctxt.mkArchSym(".rel.plt", 0, &ctxt.RelPLT) + ctxt.mkArchSym(".rela.plt", 0, &ctxt.RelaPLT) + } + if ctxt.IsDarwin() { + ctxt.mkArchSym(".linkedit.got", 0, &ctxt.LinkEditGOT) + ctxt.mkArchSym(".linkedit.plt", 0, &ctxt.LinkEditPLT) + } +} + +type Arch struct { + Funcalign int + Maxalign int + Minalign int + Dwarfregsp int + Dwarfreglr int + Androiddynld string + Linuxdynld string + Freebsddynld string + Netbsddynld string + Openbsddynld string + Dragonflydynld string + Solarisdynld string + + // Empty spaces between codeblocks will be padded with this value. + // For example an architecture might want to pad with a trap instruction to + // catch wayward programs. Architectures that do not define a padding value + // are padded with zeros. + CodePad []byte + + // Set to true to write all text blocks in with CodeBlkWrite + WriteTextBlocks bool + + // Plan 9 variables. + Plan9Magic uint32 + Plan9_64Bit bool + + Adddynrel func(*Target, *loader.Loader, *ArchSyms, loader.Sym, loader.Reloc, int) bool + Archinit func(*Link) + // Archreloc is an arch-specific hook that assists in relocation processing + // (invoked by 'relocsym'); it handles target-specific relocation tasks. + // Here "rel" is the current relocation being examined, "sym" is the symbol + // containing the chunk of data to which the relocation applies, and "off" + // is the contents of the to-be-relocated data item (from sym.P). Return + // value is the appropriately relocated value (to be written back to the + // same spot in sym.P), number of external _host_ relocations needed (i.e. + // ELF/Mach-O/etc. relocations, not Go relocations, this must match Elfreloc1, + // etc.), and a boolean indicating success/failure (a failing value indicates + // a fatal error). + Archreloc func(*Target, *loader.Loader, *ArchSyms, loader.Reloc, loader.Sym, + int64) (relocatedOffset int64, nExtReloc int, ok bool) + // Archrelocvariant is a second arch-specific hook used for + // relocation processing; it handles relocations where r.Type is + // insufficient to describe the relocation (r.Variant != + // sym.RV_NONE). Here "rel" is the relocation being applied, "sym" + // is the symbol containing the chunk of data to which the + // relocation applies, and "off" is the contents of the + // to-be-relocated data item (from sym.P). Return is an updated + // offset value. + Archrelocvariant func(target *Target, ldr *loader.Loader, rel loader.Reloc, + rv sym.RelocVariant, sym loader.Sym, offset int64) (relocatedOffset int64) + + // Generate a trampoline for a call from s to rs if necessary. ri is + // index of the relocation. + Trampoline func(ctxt *Link, ldr *loader.Loader, ri int, rs, s loader.Sym) + + // Assembling the binary breaks into two phases, writing the code/data/ + // dwarf information (which is rather generic), and some more architecture + // specific work like setting up the elf headers/dynamic relocations, etc. + // The phases are called "Asmb" and "Asmb2". Asmb2 needs to be defined for + // every architecture, but only if architecture has an Asmb function will + // it be used for assembly. Otherwise a generic assembly Asmb function is + // used. + Asmb func(*Link, *loader.Loader) + Asmb2 func(*Link, *loader.Loader) + + // Extreloc is an arch-specific hook that converts a Go relocation to an + // external relocation. Return the external relocation and whether it is + // needed. + Extreloc func(*Target, *loader.Loader, loader.Reloc, loader.Sym) (loader.ExtReloc, bool) + + Elfreloc1 func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int, int64) bool + ElfrelocSize uint32 // size of an ELF relocation record, must match Elfreloc1. + Elfsetupplt func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) + Gentext func(*Link, *loader.Loader) // Generate text before addressing has been performed. + Machoreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool + MachorelocSize uint32 // size of an Mach-O relocation record, must match Machoreloc1. + PEreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool + Xcoffreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool + + // Generate additional symbols for the native symbol table just prior to + // code generation. + GenSymsLate func(*Link, *loader.Loader) + + // TLSIEtoLE converts a TLS Initial Executable relocation to + // a TLS Local Executable relocation. + // + // This is possible when a TLS IE relocation refers to a local + // symbol in an executable, which is typical when internally + // linking PIE binaries. + TLSIEtoLE func(P []byte, off, size int) + + // optional override for assignAddress + AssignAddress func(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) +} + +var ( + thearch Arch + lcSize int32 + rpath Rpath + spSize int32 + symSize int32 +) + +const ( + MINFUNC = 16 // minimum size for a function +) + +// DynlinkingGo reports whether we are producing Go code that can live +// in separate shared libraries linked together at runtime. +func (ctxt *Link) DynlinkingGo() bool { + if !ctxt.Loaded { + panic("DynlinkingGo called before all symbols loaded") + } + return ctxt.BuildMode == BuildModeShared || ctxt.linkShared || ctxt.BuildMode == BuildModePlugin || ctxt.canUsePlugins +} + +// CanUsePlugins reports whether a plugins can be used +func (ctxt *Link) CanUsePlugins() bool { + if !ctxt.Loaded { + panic("CanUsePlugins called before all symbols loaded") + } + return ctxt.canUsePlugins +} + +// NeedCodeSign reports whether we need to code-sign the output binary. +func (ctxt *Link) NeedCodeSign() bool { + return ctxt.IsDarwin() && ctxt.IsARM64() +} + +var ( + dynlib []string + ldflag []string + havedynamic int + Funcalign int + iscgo bool + elfglobalsymndx int + interpreter string + + debug_s bool // backup old value of debug['s'] + HEADR int32 + + nerrors int + liveness int64 + + // See -strictdups command line flag. + checkStrictDups int // 0=off 1=warning 2=error + strictDupMsgCount int +) + +var ( + Segtext sym.Segment + Segrodata sym.Segment + Segrelrodata sym.Segment + Segdata sym.Segment + Segdwarf sym.Segment + + Segments = []*sym.Segment{&Segtext, &Segrodata, &Segrelrodata, &Segdata, &Segdwarf} +) + +const pkgdef = "__.PKGDEF" + +var ( + // Set if we see an object compiled by the host compiler that is not + // from a package that is known to support internal linking mode. + externalobj = false + theline string +) + +func Lflag(ctxt *Link, arg string) { + ctxt.Libdir = append(ctxt.Libdir, arg) +} + +/* + * Unix doesn't like it when we write to a running (or, sometimes, + * recently run) binary, so remove the output file before writing it. + * On Windows 7, remove() can force a subsequent create() to fail. + * S_ISREG() does not exist on Plan 9. + */ +func mayberemoveoutfile() { + if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() { + return + } + os.Remove(*flagOutfile) +} + +func libinit(ctxt *Link) { + Funcalign = thearch.Funcalign + + // add goroot to the end of the libdir list. + suffix := "" + + suffixsep := "" + if *flagInstallSuffix != "" { + suffixsep = "_" + suffix = *flagInstallSuffix + } else if *flagRace { + suffixsep = "_" + suffix = "race" + } else if *flagMsan { + suffixsep = "_" + suffix = "msan" + } + + Lflag(ctxt, filepath.Join(objabi.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", objabi.GOOS, objabi.GOARCH, suffixsep, suffix))) + + mayberemoveoutfile() + + if err := ctxt.Out.Open(*flagOutfile); err != nil { + Exitf("cannot create %s: %v", *flagOutfile, err) + } + + if *flagEntrySymbol == "" { + switch ctxt.BuildMode { + case BuildModeCShared, BuildModeCArchive: + *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", objabi.GOARCH, objabi.GOOS) + case BuildModeExe, BuildModePIE: + *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", objabi.GOARCH, objabi.GOOS) + case BuildModeShared, BuildModePlugin: + // No *flagEntrySymbol for -buildmode=shared and plugin + default: + Errorf(nil, "unknown *flagEntrySymbol for buildmode %v", ctxt.BuildMode) + } + } +} + +func exitIfErrors() { + if nerrors != 0 || checkStrictDups > 1 && strictDupMsgCount > 0 { + mayberemoveoutfile() + Exit(2) + } + +} + +func errorexit() { + exitIfErrors() + Exit(0) +} + +func loadinternal(ctxt *Link, name string) *sym.Library { + zerofp := goobj.FingerprintType{} + if ctxt.linkShared && ctxt.PackageShlib != nil { + if shlib := ctxt.PackageShlib[name]; shlib != "" { + return addlibpath(ctxt, "internal", "internal", "", name, shlib, zerofp) + } + } + if ctxt.PackageFile != nil { + if pname := ctxt.PackageFile[name]; pname != "" { + return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp) + } + ctxt.Logf("loadinternal: cannot find %s\n", name) + return nil + } + + for _, libdir := range ctxt.Libdir { + if ctxt.linkShared { + shlibname := filepath.Join(libdir, name+".shlibname") + if ctxt.Debugvlog != 0 { + ctxt.Logf("searching for %s.a in %s\n", name, shlibname) + } + if _, err := os.Stat(shlibname); err == nil { + return addlibpath(ctxt, "internal", "internal", "", name, shlibname, zerofp) + } + } + pname := filepath.Join(libdir, name+".a") + if ctxt.Debugvlog != 0 { + ctxt.Logf("searching for %s.a in %s\n", name, pname) + } + if _, err := os.Stat(pname); err == nil { + return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp) + } + } + + ctxt.Logf("warning: unable to find %s.a\n", name) + return nil +} + +// extld returns the current external linker. +func (ctxt *Link) extld() string { + if *flagExtld == "" { + *flagExtld = "gcc" + } + return *flagExtld +} + +// findLibPathCmd uses cmd command to find gcc library libname. +// It returns library full path if found, or "none" if not found. +func (ctxt *Link) findLibPathCmd(cmd, libname string) string { + extld := ctxt.extld() + args := hostlinkArchArgs(ctxt.Arch) + args = append(args, cmd) + if ctxt.Debugvlog != 0 { + ctxt.Logf("%s %v\n", extld, args) + } + out, err := exec.Command(extld, args...).Output() + if err != nil { + if ctxt.Debugvlog != 0 { + ctxt.Logf("not using a %s file because compiler failed\n%v\n%s\n", libname, err, out) + } + return "none" + } + return strings.TrimSpace(string(out)) +} + +// findLibPath searches for library libname. +// It returns library full path if found, or "none" if not found. +func (ctxt *Link) findLibPath(libname string) string { + return ctxt.findLibPathCmd("--print-file-name="+libname, libname) +} + +func (ctxt *Link) loadlib() { + var flags uint32 + switch *FlagStrictDups { + case 0: + // nothing to do + case 1, 2: + flags = loader.FlagStrictDups + default: + log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups) + } + elfsetstring1 := func(str string, off int) { elfsetstring(ctxt, 0, str, off) } + ctxt.loader = loader.NewLoader(flags, elfsetstring1, &ctxt.ErrorReporter.ErrorReporter) + ctxt.ErrorReporter.SymName = func(s loader.Sym) string { + return ctxt.loader.SymName(s) + } + + ctxt.cgo_export_static = make(map[string]bool) + ctxt.cgo_export_dynamic = make(map[string]bool) + + // ctxt.Library grows during the loop, so not a range loop. + i := 0 + for ; i < len(ctxt.Library); i++ { + lib := ctxt.Library[i] + if lib.Shlib == "" { + if ctxt.Debugvlog > 1 { + ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref) + } + loadobjfile(ctxt, lib) + } + } + + // load internal packages, if not already + if *flagRace { + loadinternal(ctxt, "runtime/race") + } + if *flagMsan { + loadinternal(ctxt, "runtime/msan") + } + loadinternal(ctxt, "runtime") + for ; i < len(ctxt.Library); i++ { + lib := ctxt.Library[i] + if lib.Shlib == "" { + loadobjfile(ctxt, lib) + } + } + // At this point, the Go objects are "preloaded". Not all the symbols are + // added to the symbol table (only defined package symbols are). Looking + // up symbol by name may not get expected result. + + iscgo = ctxt.LibraryByPkg["runtime/cgo"] != nil + + // Plugins a require cgo support to function. Similarly, plugins may require additional + // internal linker support on some platforms which may not be implemented. + ctxt.canUsePlugins = ctxt.LibraryByPkg["plugin"] != nil && iscgo + + // We now have enough information to determine the link mode. + determineLinkMode(ctxt) + + if ctxt.LinkMode == LinkExternal && !iscgo && !(objabi.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && ctxt.Arch.Family == sys.AMD64) { + // This indicates a user requested -linkmode=external. + // The startup code uses an import of runtime/cgo to decide + // whether to initialize the TLS. So give it one. This could + // be handled differently but it's an unusual case. + if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil && lib.Shlib == "" { + if ctxt.BuildMode == BuildModeShared || ctxt.linkShared { + Exitf("cannot implicitly include runtime/cgo in a shared library") + } + loadobjfile(ctxt, lib) + } + } + + // Add non-package symbols and references of externally defined symbols. + ctxt.loader.LoadSyms(ctxt.Arch) + + // Load symbols from shared libraries, after all Go object symbols are loaded. + for _, lib := range ctxt.Library { + if lib.Shlib != "" { + if ctxt.Debugvlog > 1 { + ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref) + } + ldshlibsyms(ctxt, lib.Shlib) + } + } + + // Process cgo directives (has to be done before host object loading). + ctxt.loadcgodirectives() + + // Conditionally load host objects, or setup for external linking. + hostobjs(ctxt) + hostlinksetup(ctxt) + + if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 { + // If we have any undefined symbols in external + // objects, try to read them from the libgcc file. + any := false + undefs := ctxt.loader.UndefinedRelocTargets(1) + if len(undefs) > 0 { + any = true + } + if any { + if *flagLibGCC == "" { + *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc") + } + if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" { + // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a". + // In this case we fail to load libgcc.a and can encounter link + // errors - see if we can find libcompiler_rt.a instead. + *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt") + } + if *flagLibGCC != "none" { + hostArchive(ctxt, *flagLibGCC) + } + if ctxt.HeadType == objabi.Hwindows { + if p := ctxt.findLibPath("libmingwex.a"); p != "none" { + hostArchive(ctxt, p) + } + if p := ctxt.findLibPath("libmingw32.a"); p != "none" { + hostArchive(ctxt, p) + } + // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol + // (see https://golang.org/issue/23649 for details). + if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" { + hostArchive(ctxt, p) + } + // TODO: maybe do something similar to peimporteddlls to collect all lib names + // and try link them all to final exe just like libmingwex.a and libmingw32.a: + /* + for: + #cgo windows LDFLAGS: -lmsvcrt -lm + import: + libmsvcrt.a libm.a + */ + } + } + } + + // We've loaded all the code now. + ctxt.Loaded = true + + importcycles() + + strictDupMsgCount = ctxt.loader.NStrictDupMsgs() +} + +// setupdynexp constructs ctxt.dynexp, a list of loader.Sym. +func setupdynexp(ctxt *Link) { + dynexpMap := ctxt.cgo_export_dynamic + if ctxt.LinkMode == LinkExternal { + dynexpMap = ctxt.cgo_export_static + } + d := make([]loader.Sym, 0, len(dynexpMap)) + for exp := range dynexpMap { + s := ctxt.loader.LookupOrCreateSym(exp, 0) + d = append(d, s) + // sanity check + if !ctxt.loader.AttrReachable(s) { + panic("dynexp entry not reachable") + } + } + sort.Slice(d, func(i, j int) bool { + return ctxt.loader.SymName(d[i]) < ctxt.loader.SymName(d[j]) + }) + + // Resolve ABI aliases in the list of cgo-exported functions. + // This is necessary because we load the ABI0 symbol for all + // cgo exports. + for i, s := range d { + if ctxt.loader.SymType(s) != sym.SABIALIAS { + continue + } + t := ctxt.loader.ResolveABIAlias(s) + ctxt.loader.CopyAttributes(s, t) + ctxt.loader.SetSymExtname(t, ctxt.loader.SymExtname(s)) + d[i] = t + } + ctxt.dynexp = d + + ctxt.cgo_export_static = nil + ctxt.cgo_export_dynamic = nil +} + +// loadcgodirectives reads the previously discovered cgo directives, creating +// symbols in preparation for host object loading or use later in the link. +func (ctxt *Link) loadcgodirectives() { + l := ctxt.loader + hostObjSyms := make(map[loader.Sym]struct{}) + for _, d := range ctxt.cgodata { + setCgoAttr(ctxt, ctxt.loader.LookupOrCreateSym, d.file, d.pkg, d.directives, hostObjSyms) + } + ctxt.cgodata = nil + + if ctxt.LinkMode == LinkInternal { + // Drop all the cgo_import_static declarations. + // Turns out we won't be needing them. + for symIdx := range hostObjSyms { + if l.SymType(symIdx) == sym.SHOSTOBJ { + // If a symbol was marked both + // cgo_import_static and cgo_import_dynamic, + // then we want to make it cgo_import_dynamic + // now. + su := l.MakeSymbolUpdater(symIdx) + if l.SymExtname(symIdx) != "" && l.SymDynimplib(symIdx) != "" && !(l.AttrCgoExportStatic(symIdx) || l.AttrCgoExportDynamic(symIdx)) { + su.SetType(sym.SDYNIMPORT) + } else { + su.SetType(0) + } + } + } + } +} + +// Set up flags and special symbols depending on the platform build mode. +// This version works with loader.Loader. +func (ctxt *Link) linksetup() { + switch ctxt.BuildMode { + case BuildModeCShared, BuildModePlugin: + symIdx := ctxt.loader.LookupOrCreateSym("runtime.islibrary", 0) + sb := ctxt.loader.MakeSymbolUpdater(symIdx) + sb.SetType(sym.SNOPTRDATA) + sb.AddUint8(1) + case BuildModeCArchive: + symIdx := ctxt.loader.LookupOrCreateSym("runtime.isarchive", 0) + sb := ctxt.loader.MakeSymbolUpdater(symIdx) + sb.SetType(sym.SNOPTRDATA) + sb.AddUint8(1) + } + + // Recalculate pe parameters now that we have ctxt.LinkMode set. + if ctxt.HeadType == objabi.Hwindows { + Peinit(ctxt) + } + + if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal { + *FlagTextAddr = 0 + } + + // If there are no dynamic libraries needed, gcc disables dynamic linking. + // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) + // assumes that a dynamic binary always refers to at least one dynamic library. + // Rather than be a source of test cases for glibc, disable dynamic linking + // the same way that gcc would. + // + // Exception: on OS X, programs such as Shark only work with dynamic + // binaries, so leave it enabled on OS X (Mach-O) binaries. + // Also leave it enabled on Solaris which doesn't support + // statically linked binaries. + if ctxt.BuildMode == BuildModeExe { + if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris { + *FlagD = true + } + } + + if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" { + toc := ctxt.loader.LookupOrCreateSym(".TOC.", 0) + sb := ctxt.loader.MakeSymbolUpdater(toc) + sb.SetType(sym.SDYNIMPORT) + } + + // The Android Q linker started to complain about underalignment of the our TLS + // section. We don't actually use the section on android, so don't + // generate it. + if objabi.GOOS != "android" { + tlsg := ctxt.loader.LookupOrCreateSym("runtime.tlsg", 0) + sb := ctxt.loader.MakeSymbolUpdater(tlsg) + + // runtime.tlsg is used for external linking on platforms that do not define + // a variable to hold g in assembly (currently only intel). + if sb.Type() == 0 { + sb.SetType(sym.STLSBSS) + sb.SetSize(int64(ctxt.Arch.PtrSize)) + } else if sb.Type() != sym.SDYNIMPORT { + Errorf(nil, "runtime declared tlsg variable %v", sb.Type()) + } + ctxt.loader.SetAttrReachable(tlsg, true) + ctxt.Tlsg = tlsg + } + + var moduledata loader.Sym + var mdsb *loader.SymbolBuilder + if ctxt.BuildMode == BuildModePlugin { + moduledata = ctxt.loader.LookupOrCreateSym("local.pluginmoduledata", 0) + mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) + ctxt.loader.SetAttrLocal(moduledata, true) + } else { + moduledata = ctxt.loader.LookupOrCreateSym("runtime.firstmoduledata", 0) + mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) + } + if mdsb.Type() != 0 && mdsb.Type() != sym.SDYNIMPORT { + // If the module (toolchain-speak for "executable or shared + // library") we are linking contains the runtime package, it + // will define the runtime.firstmoduledata symbol and we + // truncate it back to 0 bytes so we can define its entire + // contents in symtab.go:symtab(). + mdsb.SetSize(0) + + // In addition, on ARM, the runtime depends on the linker + // recording the value of GOARM. + if ctxt.Arch.Family == sys.ARM { + goarm := ctxt.loader.LookupOrCreateSym("runtime.goarm", 0) + sb := ctxt.loader.MakeSymbolUpdater(goarm) + sb.SetType(sym.SDATA) + sb.SetSize(0) + sb.AddUint8(uint8(objabi.GOARM)) + } + } else { + // If OTOH the module does not contain the runtime package, + // create a local symbol for the moduledata. + moduledata = ctxt.loader.LookupOrCreateSym("local.moduledata", 0) + mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) + ctxt.loader.SetAttrLocal(moduledata, true) + } + // In all cases way we mark the moduledata as noptrdata to hide it from + // the GC. + mdsb.SetType(sym.SNOPTRDATA) + ctxt.loader.SetAttrReachable(moduledata, true) + ctxt.Moduledata = moduledata + + if ctxt.Arch == sys.Arch386 && ctxt.HeadType != objabi.Hwindows { + if (ctxt.BuildMode == BuildModeCArchive && ctxt.IsELF) || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE || ctxt.DynlinkingGo() { + got := ctxt.loader.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0) + sb := ctxt.loader.MakeSymbolUpdater(got) + sb.SetType(sym.SDYNIMPORT) + ctxt.loader.SetAttrReachable(got, true) + } + } + + // DWARF-gen and other phases require that the unit Textp slices + // be populated, so that it can walk the functions in each unit. + // Call into the loader to do this (requires that we collect the + // set of internal libraries first). NB: might be simpler if we + // moved isRuntimeDepPkg to cmd/internal and then did the test in + // loader.AssignTextSymbolOrder. + ctxt.Library = postorder(ctxt.Library) + intlibs := []bool{} + for _, lib := range ctxt.Library { + intlibs = append(intlibs, isRuntimeDepPkg(lib.Pkg)) + } + ctxt.Textp = ctxt.loader.AssignTextSymbolOrder(ctxt.Library, intlibs, ctxt.Textp) +} + +// mangleTypeSym shortens the names of symbols that represent Go types +// if they are visible in the symbol table. +// +// As the names of these symbols are derived from the string of +// the type, they can run to many kilobytes long. So we shorten +// them using a SHA-1 when the name appears in the final binary. +// This also removes characters that upset external linkers. +// +// These are the symbols that begin with the prefix 'type.' and +// contain run-time type information used by the runtime and reflect +// packages. All Go binaries contain these symbols, but only +// those programs loaded dynamically in multiple parts need these +// symbols to have entries in the symbol table. +func (ctxt *Link) mangleTypeSym() { + if ctxt.BuildMode != BuildModeShared && !ctxt.linkShared && ctxt.BuildMode != BuildModePlugin && !ctxt.CanUsePlugins() { + return + } + + ldr := ctxt.loader + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) && !ctxt.linkShared { + // If -linkshared, the GCProg generation code may need to reach + // out to the shared library for the type descriptor's data, even + // the type descriptor itself is not actually needed at run time + // (therefore not reachable). We still need to mangle its name, + // so it is consistent with the one stored in the shared library. + continue + } + name := ldr.SymName(s) + newName := typeSymbolMangle(name) + if newName != name { + ldr.SetSymExtname(s, newName) + + // When linking against a shared library, the Go object file may + // have reference to the original symbol name whereas the shared + // library provides a symbol with the mangled name. We need to + // copy the payload of mangled to original. + // XXX maybe there is a better way to do this. + dup := ldr.Lookup(newName, ldr.SymVersion(s)) + if dup != 0 { + st := ldr.SymType(s) + dt := ldr.SymType(dup) + if st == sym.Sxxx && dt != sym.Sxxx { + ldr.CopySym(dup, s) + } + } + } + } +} + +// typeSymbolMangle mangles the given symbol name into something shorter. +// +// Keep the type.. prefix, which parts of the linker (like the +// DWARF generator) know means the symbol is not decodable. +// Leave type.runtime. symbols alone, because other parts of +// the linker manipulates them. +func typeSymbolMangle(name string) string { + if !strings.HasPrefix(name, "type.") { + return name + } + if strings.HasPrefix(name, "type.runtime.") { + return name + } + if len(name) <= 14 && !strings.Contains(name, "@") { // Issue 19529 + return name + } + hash := sha1.Sum([]byte(name)) + prefix := "type." + if name[5] == '.' { + prefix = "type.." + } + return prefix + base64.StdEncoding.EncodeToString(hash[:6]) +} + +/* + * look for the next file in an archive. + * adapted from libmach. + */ +func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 { + if off&1 != 0 { + off++ + } + bp.MustSeek(off, 0) + var buf [SAR_HDR]byte + if n, err := io.ReadFull(bp, buf[:]); err != nil { + if n == 0 && err != io.EOF { + return -1 + } + return 0 + } + + a.name = artrim(buf[0:16]) + a.date = artrim(buf[16:28]) + a.uid = artrim(buf[28:34]) + a.gid = artrim(buf[34:40]) + a.mode = artrim(buf[40:48]) + a.size = artrim(buf[48:58]) + a.fmag = artrim(buf[58:60]) + + arsize := atolwhex(a.size) + if arsize&1 != 0 { + arsize++ + } + return arsize + SAR_HDR +} + +func loadobjfile(ctxt *Link, lib *sym.Library) { + pkg := objabi.PathToPrefix(lib.Pkg) + + if ctxt.Debugvlog > 1 { + ctxt.Logf("ldobj: %s (%s)\n", lib.File, pkg) + } + f, err := bio.Open(lib.File) + if err != nil { + Exitf("cannot open file %s: %v", lib.File, err) + } + defer f.Close() + defer func() { + if pkg == "main" && !lib.Main { + Exitf("%s: not package main", lib.File) + } + }() + + for i := 0; i < len(ARMAG); i++ { + if c, err := f.ReadByte(); err == nil && c == ARMAG[i] { + continue + } + + /* load it as a regular file */ + l := f.MustSeek(0, 2) + f.MustSeek(0, 0) + ldobj(ctxt, f, lib, l, lib.File, lib.File) + return + } + + /* + * load all the object files from the archive now. + * this gives us sequential file access and keeps us + * from needing to come back later to pick up more + * objects. it breaks the usual C archive model, but + * this is Go, not C. the common case in Go is that + * we need to load all the objects, and then we throw away + * the individual symbols that are unused. + * + * loading every object will also make it possible to + * load foreign objects not referenced by __.PKGDEF. + */ + var arhdr ArHdr + off := f.Offset() + for { + l := nextar(f, off, &arhdr) + if l == 0 { + break + } + if l < 0 { + Exitf("%s: malformed archive", lib.File) + } + off += l + + // __.PKGDEF isn't a real Go object file, and it's + // absent in -linkobj builds anyway. Skipping it + // ensures consistency between -linkobj and normal + // build modes. + if arhdr.name == pkgdef { + continue + } + + // Skip other special (non-object-file) sections that + // build tools may have added. Such sections must have + // short names so that the suffix is not truncated. + if len(arhdr.name) < 16 { + if ext := filepath.Ext(arhdr.name); ext != ".o" && ext != ".syso" { + continue + } + } + + pname := fmt.Sprintf("%s(%s)", lib.File, arhdr.name) + l = atolwhex(arhdr.size) + ldobj(ctxt, f, lib, l, pname, lib.File) + } +} + +type Hostobj struct { + ld func(*Link, *bio.Reader, string, int64, string) + pkg string + pn string + file string + off int64 + length int64 +} + +var hostobj []Hostobj + +// These packages can use internal linking mode. +// Others trigger external mode. +var internalpkg = []string{ + "crypto/x509", + "net", + "os/user", + "runtime/cgo", + "runtime/race", + "runtime/msan", +} + +func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType objabi.HeadType, f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj { + isinternal := false + for _, intpkg := range internalpkg { + if pkg == intpkg { + isinternal = true + break + } + } + + // DragonFly declares errno with __thread, which results in a symbol + // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not + // currently know how to handle TLS relocations, hence we have to + // force external linking for any libraries that link in code that + // uses errno. This can be removed if the Go linker ever supports + // these relocation types. + if headType == objabi.Hdragonfly { + if pkg == "net" || pkg == "os/user" { + isinternal = false + } + } + + if !isinternal { + externalobj = true + } + + hostobj = append(hostobj, Hostobj{}) + h := &hostobj[len(hostobj)-1] + h.ld = ld + h.pkg = pkg + h.pn = pn + h.file = file + h.off = f.Offset() + h.length = length + return h +} + +func hostobjs(ctxt *Link) { + if ctxt.LinkMode != LinkInternal { + return + } + var h *Hostobj + + for i := 0; i < len(hostobj); i++ { + h = &hostobj[i] + f, err := bio.Open(h.file) + if err != nil { + Exitf("cannot reopen %s: %v", h.pn, err) + } + + f.MustSeek(h.off, 0) + h.ld(ctxt, f, h.pkg, h.length, h.pn) + f.Close() + } +} + +func hostlinksetup(ctxt *Link) { + if ctxt.LinkMode != LinkExternal { + return + } + + // For external link, record that we need to tell the external linker -s, + // and turn off -s internally: the external linker needs the symbol + // information for its final link. + debug_s = *FlagS + *FlagS = false + + // create temporary directory and arrange cleanup + if *flagTmpdir == "" { + dir, err := ioutil.TempDir("", "go-link-") + if err != nil { + log.Fatal(err) + } + *flagTmpdir = dir + ownTmpDir = true + AtExit(func() { + ctxt.Out.Close() + os.RemoveAll(*flagTmpdir) + }) + } + + // change our output to temporary object file + if err := ctxt.Out.Close(); err != nil { + Exitf("error closing output file") + } + mayberemoveoutfile() + + p := filepath.Join(*flagTmpdir, "go.o") + if err := ctxt.Out.Open(p); err != nil { + Exitf("cannot create %s: %v", p, err) + } +} + +// hostobjCopy creates a copy of the object files in hostobj in a +// temporary directory. +func hostobjCopy() (paths []string) { + var wg sync.WaitGroup + sema := make(chan struct{}, runtime.NumCPU()) // limit open file descriptors + for i, h := range hostobj { + h := h + dst := filepath.Join(*flagTmpdir, fmt.Sprintf("%06d.o", i)) + paths = append(paths, dst) + + wg.Add(1) + go func() { + sema <- struct{}{} + defer func() { + <-sema + wg.Done() + }() + f, err := os.Open(h.file) + if err != nil { + Exitf("cannot reopen %s: %v", h.pn, err) + } + defer f.Close() + if _, err := f.Seek(h.off, 0); err != nil { + Exitf("cannot seek %s: %v", h.pn, err) + } + + w, err := os.Create(dst) + if err != nil { + Exitf("cannot create %s: %v", dst, err) + } + if _, err := io.CopyN(w, f, h.length); err != nil { + Exitf("cannot write %s: %v", dst, err) + } + if err := w.Close(); err != nil { + Exitf("cannot close %s: %v", dst, err) + } + }() + } + wg.Wait() + return paths +} + +// writeGDBLinkerScript creates gcc linker script file in temp +// directory. writeGDBLinkerScript returns created file path. +// The script is used to work around gcc bug +// (see https://golang.org/issue/20183 for details). +func writeGDBLinkerScript() string { + name := "fix_debug_gdb_scripts.ld" + path := filepath.Join(*flagTmpdir, name) + src := `SECTIONS +{ + .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) : + { + *(.debug_gdb_scripts) + } +} +INSERT AFTER .debug_types; +` + err := ioutil.WriteFile(path, []byte(src), 0666) + if err != nil { + Errorf(nil, "WriteFile %s failed: %v", name, err) + } + return path +} + +// archive builds a .a archive from the hostobj object files. +func (ctxt *Link) archive() { + if ctxt.BuildMode != BuildModeCArchive { + return + } + + exitIfErrors() + + if *flagExtar == "" { + *flagExtar = "ar" + } + + mayberemoveoutfile() + + // Force the buffer to flush here so that external + // tools will see a complete file. + if err := ctxt.Out.Close(); err != nil { + Exitf("error closing %v", *flagOutfile) + } + + argv := []string{*flagExtar, "-q", "-c", "-s"} + if ctxt.HeadType == objabi.Haix { + argv = append(argv, "-X64") + } + argv = append(argv, *flagOutfile) + argv = append(argv, filepath.Join(*flagTmpdir, "go.o")) + argv = append(argv, hostobjCopy()...) + + if ctxt.Debugvlog != 0 { + ctxt.Logf("archive: %s\n", strings.Join(argv, " ")) + } + + // If supported, use syscall.Exec() to invoke the archive command, + // which should be the final remaining step needed for the link. + // This will reduce peak RSS for the link (and speed up linking of + // large applications), since when the archive command runs we + // won't be holding onto all of the linker's live memory. + if syscallExecSupported && !ownTmpDir { + runAtExitFuncs() + ctxt.execArchive(argv) + panic("should not get here") + } + + // Otherwise invoke 'ar' in the usual way (fork + exec). + if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { + Exitf("running %s failed: %v\n%s", argv[0], err, out) + } +} + +func (ctxt *Link) hostlink() { + if ctxt.LinkMode != LinkExternal || nerrors > 0 { + return + } + if ctxt.BuildMode == BuildModeCArchive { + return + } + + var argv []string + argv = append(argv, ctxt.extld()) + argv = append(argv, hostlinkArchArgs(ctxt.Arch)...) + + if *FlagS || debug_s { + if ctxt.HeadType == objabi.Hdarwin { + // Recent versions of macOS print + // ld: warning: option -s is obsolete and being ignored + // so do not pass any arguments. + } else { + argv = append(argv, "-s") + } + } + + // On darwin, whether to combine DWARF into executable. + // Only macOS supports unmapped segments such as our __DWARF segment. + combineDwarf := ctxt.IsDarwin() && !*FlagS && !*FlagW && !debug_s && machoPlatform == PLATFORM_MACOS + + switch ctxt.HeadType { + case objabi.Hdarwin: + if combineDwarf { + // Leave room for DWARF combining. + // -headerpad is incompatible with -fembed-bitcode. + argv = append(argv, "-Wl,-headerpad,1144") + } + 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 { + argv = append(argv, "-Wl,-S") // suppress STAB (symbolic debugging) symbols + } + case objabi.Hopenbsd: + argv = append(argv, "-Wl,-nopie") + argv = append(argv, "-pthread") + case objabi.Hwindows: + if windowsgui { + argv = append(argv, "-mwindows") + } else { + argv = append(argv, "-mconsole") + } + // Mark as having awareness of terminal services, to avoid + // ancient compatibility hacks. + argv = append(argv, "-Wl,--tsaware") + + // Enable DEP + argv = append(argv, "-Wl,--nxcompat") + + argv = append(argv, fmt.Sprintf("-Wl,--major-os-version=%d", PeMinimumTargetMajorVersion)) + argv = append(argv, fmt.Sprintf("-Wl,--minor-os-version=%d", PeMinimumTargetMinorVersion)) + argv = append(argv, fmt.Sprintf("-Wl,--major-subsystem-version=%d", PeMinimumTargetMajorVersion)) + argv = append(argv, fmt.Sprintf("-Wl,--minor-subsystem-version=%d", PeMinimumTargetMinorVersion)) + case objabi.Haix: + argv = append(argv, "-pthread") + // prevent ld to reorder .text functions to keep the same + // first/last functions for moduledata. + argv = append(argv, "-Wl,-bnoobjreorder") + // mcmodel=large is needed for every gcc generated files, but + // ld still need -bbigtoc in order to allow larger TOC. + argv = append(argv, "-mcmodel=large") + argv = append(argv, "-Wl,-bbigtoc") + } + + // Enable 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 { + if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() { + argv = append(argv, "-Wl,-no_pie") + argv = append(argv, "-Wl,-pagezero_size,4000000") + } + } + case BuildModePIE: + switch ctxt.HeadType { + case objabi.Hdarwin, objabi.Haix: + case objabi.Hwindows: + argv = addASLRargs(argv) + default: + // ELF. + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-pie") + } + case BuildModeCShared: + if ctxt.HeadType == objabi.Hdarwin { + argv = append(argv, "-dynamiclib") + } else { + // ELF. + argv = append(argv, "-Wl,-Bsymbolic") + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-shared") + 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") + } + } + case BuildModeShared: + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-shared") + case BuildModePlugin: + if ctxt.HeadType == objabi.Hdarwin { + argv = append(argv, "-dynamiclib") + } else { + if ctxt.UseRelro() { + argv = append(argv, "-Wl,-z,relro") + } + argv = append(argv, "-shared") + } + } + + var altLinker string + if ctxt.IsELF && ctxt.DynlinkingGo() { + // We force all symbol resolution to be done at program startup + // because lazy PLT resolution can use large amounts of stack at + // times we cannot allow it to do so. + argv = append(argv, "-Wl,-znow") + + // Do not let the host linker generate COPY relocations. These + // can move symbols out of sections that rely on stable offsets + // from the beginning of the section (like sym.STYPE). + argv = append(argv, "-Wl,-znocopyreloc") + + if objabi.GOOS == "android" { + // Use lld to avoid errors from default linker (issue #38838) + altLinker = "lld" + } + + if ctxt.Arch.InFamily(sys.ARM, sys.ARM64) && objabi.GOOS == "linux" { + // On ARM, the GNU linker will generate COPY relocations + // even with -znocopyreloc set. + // https://sourceware.org/bugzilla/show_bug.cgi?id=19962 + // + // On ARM64, the GNU linker will fail instead of + // generating COPY relocations. + // + // In both cases, switch to gold. + altLinker = "gold" + + // If gold is not installed, gcc will silently switch + // back to ld.bfd. So we parse the version information + // and provide a useful error if gold is missing. + cmd := exec.Command(*flagExtld, "-fuse-ld=gold", "-Wl,--version") + if out, err := cmd.CombinedOutput(); err == nil { + if !bytes.Contains(out, []byte("GNU gold")) { + log.Fatalf("ARM external linker must be gold (issue #15696), but is not: %s", out) + } + } + } + } + if ctxt.Arch.Family == sys.ARM64 && objabi.GOOS == "freebsd" { + // Switch to ld.bfd on freebsd/arm64. + altLinker = "bfd" + + // Provide a useful error if ld.bfd is missing. + cmd := exec.Command(*flagExtld, "-fuse-ld=bfd", "-Wl,--version") + if out, err := cmd.CombinedOutput(); err == nil { + if !bytes.Contains(out, []byte("GNU ld")) { + log.Fatalf("ARM64 external linker must be ld.bfd (issue #35197), please install devel/binutils") + } + } + } + if altLinker != "" { + argv = append(argv, "-fuse-ld="+altLinker) + } + + if ctxt.IsELF && len(buildinfo) > 0 { + argv = append(argv, fmt.Sprintf("-Wl,--build-id=0x%x", buildinfo)) + } + + // On Windows, given -o foo, GCC will append ".exe" to produce + // "foo.exe". We have decided that we want to honor the -o + // option. To make this work, we append a '.' so that GCC + // will decide that the file already has an extension. We + // only want to do this when producing a Windows output file + // on a Windows host. + outopt := *flagOutfile + if objabi.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" { + outopt += "." + } + argv = append(argv, "-o") + argv = append(argv, outopt) + + if rpath.val != "" { + argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val)) + } + + // Force global symbols to be exported for dlopen, etc. + if ctxt.IsELF { + argv = append(argv, "-rdynamic") + } + if ctxt.HeadType == objabi.Haix { + fileName := xcoffCreateExportFile(ctxt) + argv = append(argv, "-Wl,-bE:"+fileName) + } + + if strings.Contains(argv[0], "clang") { + argv = append(argv, "-Qunused-arguments") + } + + const compressDWARF = "-Wl,--compress-debug-sections=zlib-gnu" + if ctxt.compressDWARF && linkerFlagSupported(ctxt.Arch, argv[0], altLinker, compressDWARF) { + argv = append(argv, compressDWARF) + } + + argv = append(argv, filepath.Join(*flagTmpdir, "go.o")) + argv = append(argv, hostobjCopy()...) + if ctxt.HeadType == objabi.Haix { + // We want to have C files after Go files to remove + // trampolines csects made by ld. + argv = append(argv, "-nostartfiles") + argv = append(argv, "/lib/crt0_64.o") + + extld := ctxt.extld() + // Get starting files. + getPathFile := func(file string) string { + args := []string{"-maix64", "--print-file-name=" + file} + out, err := exec.Command(extld, args...).CombinedOutput() + if err != nil { + log.Fatalf("running %s failed: %v\n%s", extld, err, out) + } + return strings.Trim(string(out), "\n") + } + argv = append(argv, getPathFile("crtcxa.o")) + argv = append(argv, getPathFile("crtdbase.o")) + } + + if ctxt.linkShared { + seenDirs := make(map[string]bool) + seenLibs := make(map[string]bool) + addshlib := func(path string) { + dir, base := filepath.Split(path) + if !seenDirs[dir] { + argv = append(argv, "-L"+dir) + if !rpath.set { + argv = append(argv, "-Wl,-rpath="+dir) + } + seenDirs[dir] = true + } + base = strings.TrimSuffix(base, ".so") + base = strings.TrimPrefix(base, "lib") + if !seenLibs[base] { + argv = append(argv, "-l"+base) + seenLibs[base] = true + } + } + for _, shlib := range ctxt.Shlibs { + addshlib(shlib.Path) + for _, dep := range shlib.Deps { + if dep == "" { + continue + } + libpath := findshlib(ctxt, dep) + if libpath != "" { + addshlib(libpath) + } + } + } + } + + // clang, unlike GCC, passes -rdynamic to the linker + // even when linking with -static, causing a linker + // error when using GNU ld. So take out -rdynamic if + // we added it. We do it in this order, rather than + // only adding -rdynamic later, so that -*extldflags + // can override -rdynamic without using -static. + checkStatic := func(arg string) { + if ctxt.IsELF && arg == "-static" { + for i := range argv { + if argv[i] == "-rdynamic" { + argv[i] = "-static" + } + } + } + } + + for _, p := range ldflag { + argv = append(argv, p) + checkStatic(p) + } + + // When building a program with the default -buildmode=exe the + // gc compiler generates code requires DT_TEXTREL in a + // position independent executable (PIE). On systems where the + // toolchain creates PIEs by default, and where DT_TEXTREL + // does not work, the resulting programs will not run. See + // issue #17847. To avoid this problem pass -no-pie to the + // toolchain if it is supported. + if ctxt.BuildMode == BuildModeExe && !ctxt.linkShared && !(ctxt.IsDarwin() && ctxt.IsARM64()) { + // GCC uses -no-pie, clang uses -nopie. + for _, nopie := range []string{"-no-pie", "-nopie"} { + if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, nopie) { + argv = append(argv, nopie) + break + } + } + } + + for _, p := range strings.Fields(*flagExtldflags) { + argv = append(argv, p) + checkStatic(p) + } + if ctxt.HeadType == objabi.Hwindows { + // Determine which linker we're using. Add in the extldflags in + // case used has specified "-fuse-ld=...". + cmd := exec.Command(*flagExtld, *flagExtldflags, "-Wl,--version") + usingLLD := false + if out, err := cmd.CombinedOutput(); err == nil { + if bytes.Contains(out, []byte("LLD ")) { + usingLLD = true + } + } + + // use gcc linker script to work around gcc bug + // (see https://golang.org/issue/20183 for details). + if !usingLLD { + p := writeGDBLinkerScript() + argv = append(argv, "-Wl,-T,"+p) + } + // libmingw32 and libmingwex have some inter-dependencies, + // so must use linker groups. + argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group") + argv = append(argv, peimporteddlls()...) + } + + if ctxt.Debugvlog != 0 { + ctxt.Logf("host link:") + for _, v := range argv { + ctxt.Logf(" %q", v) + } + ctxt.Logf("\n") + } + + out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput() + if err != nil { + Exitf("running %s failed: %v\n%s", argv[0], err, out) + } + + // Filter out useless linker warnings caused by bugs outside Go. + // See also cmd/go/internal/work/exec.go's gccld method. + var save [][]byte + var skipLines int + for _, line := range bytes.SplitAfter(out, []byte("\n")) { + // golang.org/issue/26073 - Apple Xcode bug + if bytes.Contains(line, []byte("ld: warning: text-based stub file")) { + continue + } + + if skipLines > 0 { + skipLines-- + continue + } + + // Remove TOC overflow warning on AIX. + if bytes.Contains(line, []byte("ld: 0711-783")) { + skipLines = 2 + continue + } + + save = append(save, line) + } + out = bytes.Join(save, nil) + + if len(out) > 0 { + // always print external output even if the command is successful, so that we don't + // swallow linker warnings (see https://golang.org/issue/17935). + ctxt.Logf("%s", out) + } + + if combineDwarf { + dsym := filepath.Join(*flagTmpdir, "go.dwarf") + 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("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. + if _, err := os.Stat(dsym); os.IsNotExist(err) { + return + } + // For os.Rename to work reliably, must be in same directory as outfile. + combinedOutput := *flagOutfile + "~" + exef, err := os.Open(*flagOutfile) + if err != nil { + Exitf("%s: combining dwarf failed: %v", os.Args[0], err) + } + defer exef.Close() + exem, err := macho.NewFile(exef) + if err != nil { + Exitf("%s: parsing Mach-O header failed: %v", os.Args[0], err) + } + if err := machoCombineDwarf(ctxt, exef, exem, dsym, combinedOutput); err != nil { + Exitf("%s: combining dwarf failed: %v", os.Args[0], err) + } + os.Remove(*flagOutfile) + if err := os.Rename(combinedOutput, *flagOutfile); err != nil { + Exitf("%s: %v", os.Args[0], err) + } + } + if ctxt.NeedCodeSign() { + err := machoCodeSign(ctxt, *flagOutfile) + if err != nil { + Exitf("%s: code signing failed: %v", os.Args[0], err) + } + } +} + +var createTrivialCOnce sync.Once + +func linkerFlagSupported(arch *sys.Arch, linker, altLinker, flag string) bool { + createTrivialCOnce.Do(func() { + src := filepath.Join(*flagTmpdir, "trivial.c") + if err := ioutil.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil { + Errorf(nil, "WriteFile trivial.c failed: %v", err) + } + }) + + flagsWithNextArgSkip := []string{ + "-F", + "-l", + "-L", + "-framework", + "-Wl,-framework", + "-Wl,-rpath", + "-Wl,-undefined", + } + flagsWithNextArgKeep := []string{ + "-arch", + "-isysroot", + "--sysroot", + "-target", + } + prefixesToKeep := []string{ + "-f", + "-m", + "-p", + "-Wl,", + "-arch", + "-isysroot", + "--sysroot", + "-target", + } + + flags := hostlinkArchArgs(arch) + keep := false + skip := false + extldflags := strings.Fields(*flagExtldflags) + for _, f := range append(extldflags, ldflag...) { + if keep { + flags = append(flags, f) + keep = false + } else if skip { + skip = false + } else if f == "" || f[0] != '-' { + } else if contains(flagsWithNextArgSkip, f) { + skip = true + } else if contains(flagsWithNextArgKeep, f) { + flags = append(flags, f) + keep = true + } else { + for _, p := range prefixesToKeep { + if strings.HasPrefix(f, p) { + flags = append(flags, f) + break + } + } + } + } + + if altLinker != "" { + flags = append(flags, "-fuse-ld="+altLinker) + } + flags = append(flags, flag, "trivial.c") + + cmd := exec.Command(linker, flags...) + cmd.Dir = *flagTmpdir + cmd.Env = append([]string{"LC_ALL=C"}, os.Environ()...) + out, err := cmd.CombinedOutput() + // GCC says "unrecognized command line option ‘-no-pie’" + // clang says "unknown argument: '-no-pie'" + return err == nil && !bytes.Contains(out, []byte("unrecognized")) && !bytes.Contains(out, []byte("unknown")) +} + +// hostlinkArchArgs returns arguments to pass to the external linker +// based on the architecture. +func hostlinkArchArgs(arch *sys.Arch) []string { + switch arch.Family { + case sys.I386: + return []string{"-m32"} + case sys.AMD64: + if objabi.GOOS == "darwin" { + return []string{"-arch", "x86_64", "-m64"} + } + return []string{"-m64"} + case sys.S390X: + return []string{"-m64"} + case sys.ARM: + return []string{"-marm"} + case sys.ARM64: + if objabi.GOOS == "darwin" { + return []string{"-arch", "arm64"} + } + case sys.MIPS64: + return []string{"-mabi=64"} + case sys.MIPS: + return []string{"-mabi=32"} + case sys.PPC64: + if objabi.GOOS == "aix" { + return []string{"-maix64"} + } else { + return []string{"-m64"} + } + + } + return nil +} + +// ldobj loads an input object. If it is a host object (an object +// compiled by a non-Go compiler) it returns the Hostobj pointer. If +// it is a Go object, it returns nil. +func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, file string) *Hostobj { + pkg := objabi.PathToPrefix(lib.Pkg) + + eof := f.Offset() + length + start := f.Offset() + c1 := bgetc(f) + c2 := bgetc(f) + c3 := bgetc(f) + c4 := bgetc(f) + f.MustSeek(start, 0) + + unit := &sym.CompilationUnit{Lib: lib} + lib.Units = append(lib.Units, unit) + + magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) + if magic == 0x7f454c46 { // \x7F E L F + ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn, ehdr.Flags) + if err != nil { + Errorf(nil, "%v", err) + return + } + ehdr.Flags = flags + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file) + } + + if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { + ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file) + } + + if /* x86 */ c1 == 0x4c && c2 == 0x01 || /* x86_64 */ c1 == 0x64 && c2 == 0x86 || /* armv7 */ c1 == 0xc4 && c2 == 0x01 { + ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + if len(rsrc) != 0 { + setpersrc(ctxt, rsrc) + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file) + } + + if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) { + ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn) + if err != nil { + Errorf(nil, "%v", err) + return + } + ctxt.Textp = append(ctxt.Textp, textp...) + } + return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) + } + + /* check the header */ + line, err := f.ReadString('\n') + if err != nil { + Errorf(nil, "truncated object file: %s: %v", pn, err) + return nil + } + + if !strings.HasPrefix(line, "go object ") { + if strings.HasSuffix(pn, ".go") { + Exitf("%s: uncompiled .go source file", pn) + return nil + } + + if line == ctxt.Arch.Name { + // old header format: just $GOOS + Errorf(nil, "%s: stale object file", pn) + return nil + } + + Errorf(nil, "%s: not an object file", pn) + return nil + } + + // First, check that the basic GOOS, GOARCH, and Version match. + t := fmt.Sprintf("%s %s %s ", objabi.GOOS, objabi.GOARCH, objabi.Version) + + line = strings.TrimRight(line, "\n") + if !strings.HasPrefix(line[10:]+" ", t) && !*flagF { + Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], t) + return nil + } + + // Second, check that longer lines match each other exactly, + // so that the Go compiler and write additional information + // that must be the same from run to run. + if len(line) >= len(t)+10 { + if theline == "" { + theline = line[10:] + } else if theline != line[10:] { + Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], theline) + return nil + } + } + + // Skip over exports and other info -- ends with \n!\n. + // + // Note: It's possible for "\n!\n" to appear within the binary + // package export data format. To avoid truncating the package + // definition prematurely (issue 21703), we keep track of + // how many "$$" delimiters we've seen. + + import0 := f.Offset() + + c1 = '\n' // the last line ended in \n + c2 = bgetc(f) + c3 = bgetc(f) + markers := 0 + for { + if c1 == '\n' { + if markers%2 == 0 && c2 == '!' && c3 == '\n' { + break + } + if c2 == '$' && c3 == '$' { + markers++ + } + } + + c1 = c2 + c2 = c3 + c3 = bgetc(f) + if c3 == -1 { + Errorf(nil, "truncated object file: %s", pn) + return nil + } + } + + import1 := f.Offset() + + f.MustSeek(import0, 0) + ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n + f.MustSeek(import1, 0) + + fingerprint := ctxt.loader.Preload(ctxt.IncVersion(), f, lib, unit, eof-f.Offset()) + if !fingerprint.IsZero() { // Assembly objects don't have fingerprints. Ignore them. + // Check fingerprint, to ensure the importing and imported packages + // have consistent view of symbol indices. + // Normally the go command should ensure this. But in case something + // goes wrong, it could lead to obscure bugs like run-time crash. + // Check it here to be sure. + if lib.Fingerprint.IsZero() { // Not yet imported. Update its fingerprint. + lib.Fingerprint = fingerprint + } + checkFingerprint(lib, fingerprint, lib.Srcref, lib.Fingerprint) + } + + addImports(ctxt, lib, pn) + return nil +} + +func checkFingerprint(lib *sym.Library, libfp goobj.FingerprintType, src string, srcfp goobj.FingerprintType) { + if libfp != srcfp { + Exitf("fingerprint mismatch: %s has %x, import from %s expecting %x", lib, libfp, src, srcfp) + } +} + +func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte { + data := make([]byte, sym.Size) + sect := f.Sections[sym.Section] + if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE { + Errorf(nil, "reading %s from non-data section", sym.Name) + } + n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr)) + if uint64(n) != sym.Size { + Errorf(nil, "reading contents of %s: %v", sym.Name, err) + } + return data +} + +func readwithpad(r io.Reader, sz int32) ([]byte, error) { + data := make([]byte, Rnd(int64(sz), 4)) + _, err := io.ReadFull(r, data) + if err != nil { + return nil, err + } + data = data[:sz] + return data, nil +} + +func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) { + for _, sect := range f.Sections { + if sect.Type != elf.SHT_NOTE { + continue + } + r := sect.Open() + for { + var namesize, descsize, noteType int32 + err := binary.Read(r, f.ByteOrder, &namesize) + if err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("read namesize failed: %v", err) + } + err = binary.Read(r, f.ByteOrder, &descsize) + if err != nil { + return nil, fmt.Errorf("read descsize failed: %v", err) + } + err = binary.Read(r, f.ByteOrder, ¬eType) + if err != nil { + return nil, fmt.Errorf("read type failed: %v", err) + } + noteName, err := readwithpad(r, namesize) + if err != nil { + return nil, fmt.Errorf("read name failed: %v", err) + } + desc, err := readwithpad(r, descsize) + if err != nil { + return nil, fmt.Errorf("read desc failed: %v", err) + } + if string(name) == string(noteName) && typ == noteType { + return desc, nil + } + } + } + return nil, nil +} + +func findshlib(ctxt *Link, shlib string) string { + if filepath.IsAbs(shlib) { + return shlib + } + for _, libdir := range ctxt.Libdir { + libpath := filepath.Join(libdir, shlib) + if _, err := os.Stat(libpath); err == nil { + return libpath + } + } + Errorf(nil, "cannot find shared library: %s", shlib) + return "" +} + +func ldshlibsyms(ctxt *Link, shlib string) { + var libpath string + if filepath.IsAbs(shlib) { + libpath = shlib + shlib = filepath.Base(shlib) + } else { + libpath = findshlib(ctxt, shlib) + if libpath == "" { + return + } + } + for _, processedlib := range ctxt.Shlibs { + if processedlib.Path == libpath { + return + } + } + if ctxt.Debugvlog > 1 { + ctxt.Logf("ldshlibsyms: found library with name %s at %s\n", shlib, libpath) + } + + f, err := elf.Open(libpath) + if err != nil { + Errorf(nil, "cannot open shared library: %s", libpath) + return + } + // Keep the file open as decodetypeGcprog needs to read from it. + // TODO: fix. Maybe mmap the file. + //defer f.Close() + + hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG) + if err != nil { + Errorf(nil, "cannot read ABI hash from shared library %s: %v", libpath, err) + return + } + + depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG) + if err != nil { + Errorf(nil, "cannot read dep list from shared library %s: %v", libpath, err) + return + } + var deps []string + for _, dep := range strings.Split(string(depsbytes), "\n") { + if dep == "" { + continue + } + if !filepath.IsAbs(dep) { + // If the dep can be interpreted as a path relative to the shlib + // in which it was found, do that. Otherwise, we will leave it + // to be resolved by libdir lookup. + abs := filepath.Join(filepath.Dir(libpath), dep) + if _, err := os.Stat(abs); err == nil { + dep = abs + } + } + deps = append(deps, dep) + } + + syms, err := f.DynamicSymbols() + if err != nil { + Errorf(nil, "cannot read symbols from shared library: %s", libpath) + return + } + for _, elfsym := range syms { + if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION { + continue + } + + // Symbols whose names start with "type." are compiler + // generated, so make functions with that prefix internal. + ver := 0 + if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type.") { + ver = sym.SymVerABIInternal + } + + l := ctxt.loader + s := l.LookupOrCreateSym(elfsym.Name, ver) + + // Because loadlib above loads all .a files before loading + // any shared libraries, any non-dynimport symbols we find + // that duplicate symbols already loaded should be ignored + // (the symbols from the .a files "win"). + if l.SymType(s) != 0 && l.SymType(s) != sym.SDYNIMPORT { + continue + } + su := l.MakeSymbolUpdater(s) + su.SetType(sym.SDYNIMPORT) + l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info)) + su.SetSize(int64(elfsym.Size)) + if elfsym.Section != elf.SHN_UNDEF { + // Set .File for the library that actually defines the symbol. + l.SetSymPkg(s, libpath) + + // The decodetype_* functions in decodetype.go need access to + // the type data. + sname := l.SymName(s) + if strings.HasPrefix(sname, "type.") && !strings.HasPrefix(sname, "type..") { + su.SetData(readelfsymboldata(ctxt, f, &elfsym)) + } + } + + // For function symbols, we don't know what ABI is + // available, so alias it under both ABIs. + // + // TODO(austin): This is almost certainly wrong once + // the ABIs are actually different. We might have to + // mangle Go function names in the .so to include the + // ABI. + if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 { + alias := ctxt.loader.LookupOrCreateSym(elfsym.Name, sym.SymVerABIInternal) + if l.SymType(alias) != 0 { + continue + } + su := l.MakeSymbolUpdater(alias) + su.SetType(sym.SABIALIAS) + r, _ := su.AddRel(0) // type doesn't matter + r.SetSym(s) + } + } + ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f}) +} + +func addsection(ldr *loader.Loader, arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section { + sect := ldr.NewSection() + sect.Rwx = uint8(rwx) + sect.Name = name + sect.Seg = seg + sect.Align = int32(arch.PtrSize) // everything is at least pointer-aligned + seg.Sections = append(seg.Sections, sect) + return sect +} + +type chain struct { + sym loader.Sym + up *chain + limit int // limit on entry to sym +} + +func haslinkregister(ctxt *Link) bool { + return ctxt.FixedFrameSize() != 0 +} + +func callsize(ctxt *Link) int { + if haslinkregister(ctxt) { + return 0 + } + return ctxt.Arch.RegSize +} + +type stkChk struct { + ldr *loader.Loader + ctxt *Link + morestack loader.Sym + done loader.Bitmap +} + +// Walk the call tree and check that there is always enough stack space +// for the call frames, especially for a chain of nosplit functions. +func (ctxt *Link) dostkcheck() { + ldr := ctxt.loader + sc := stkChk{ + ldr: ldr, + ctxt: ctxt, + morestack: ldr.Lookup("runtime.morestack", 0), + done: loader.MakeBitmap(ldr.NSym()), + } + + // Every splitting function ensures that there are at least StackLimit + // bytes available below SP when the splitting prologue finishes. + // If the splitting function calls F, then F begins execution with + // at least StackLimit - callsize() bytes available. + // Check that every function behaves correctly with this amount + // of stack, following direct calls in order to piece together chains + // of non-splitting functions. + var ch chain + ch.limit = objabi.StackLimit - callsize(ctxt) + if objabi.GOARCH == "arm64" { + // need extra 8 bytes below SP to save FP + ch.limit -= 8 + } + + // Check every function, but do the nosplit functions in a first pass, + // to make the printed failure chains as short as possible. + for _, s := range ctxt.Textp { + if ldr.IsNoSplit(s) { + ch.sym = s + sc.check(&ch, 0) + } + } + + for _, s := range ctxt.Textp { + if !ldr.IsNoSplit(s) { + ch.sym = s + sc.check(&ch, 0) + } + } +} + +func (sc *stkChk) check(up *chain, depth int) int { + limit := up.limit + s := up.sym + ldr := sc.ldr + ctxt := sc.ctxt + + // Don't duplicate work: only need to consider each + // function at top of safe zone once. + top := limit == objabi.StackLimit-callsize(ctxt) + if top { + if sc.done.Has(s) { + return 0 + } + sc.done.Set(s) + } + + if depth > 500 { + sc.ctxt.Errorf(s, "nosplit stack check too deep") + sc.broke(up, 0) + return -1 + } + + if ldr.AttrExternal(s) { + // external function. + // should never be called directly. + // onlyctxt.Diagnose the direct caller. + // TODO(mwhudson): actually think about this. + // TODO(khr): disabled for now. Calls to external functions can only happen on the g0 stack. + // See the trampolines in src/runtime/sys_darwin_$ARCH.go. + //if depth == 1 && ldr.SymType(s) != sym.SXREF && !ctxt.DynlinkingGo() && + // ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin { + // Errorf(s, "call to external function") + //} + return -1 + } + info := ldr.FuncInfo(s) + if !info.Valid() { // external function. see above. + return -1 + } + + if limit < 0 { + sc.broke(up, limit) + return -1 + } + + // morestack looks like it calls functions, + // but it switches the stack pointer first. + if s == sc.morestack { + return 0 + } + + var ch chain + ch.up = up + + if !ldr.IsNoSplit(s) { + // Ensure we have enough stack to call morestack. + ch.limit = limit - callsize(ctxt) + ch.sym = sc.morestack + if sc.check(&ch, depth+1) < 0 { + return -1 + } + if !top { + return 0 + } + // Raise limit to allow frame. + locals := info.Locals() + limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize()) + } + + // Walk through sp adjustments in function, consuming relocs. + relocs := ldr.Relocs(s) + var ch1 chain + pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) + ri := 0 + 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. + if int32(limit)-pcsp.Value < 0 { + sc.broke(up, int(int32(limit)-pcsp.Value)) + return -1 + } + + // Process calls in this span. + for ; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if uint32(r.Off()) >= pcsp.NextPC { + break + } + t := r.Type() + switch { + case t.IsDirectCall(): + ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt))) + ch.sym = r.Sym() + if sc.check(&ch, depth+1) < 0 { + return -1 + } + + // Indirect call. Assume it is a call to a splitting function, + // so we have to make sure it can call morestack. + // Arrange the data structures to report both calls, so that + // if there is an error, stkprint shows all the steps involved. + case t == objabi.R_CALLIND: + ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt))) + ch.sym = 0 + ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue + ch1.up = &ch + ch1.sym = sc.morestack + if sc.check(&ch1, depth+2) < 0 { + return -1 + } + } + } + } + + return 0 +} + +func (sc *stkChk) broke(ch *chain, limit int) { + sc.ctxt.Errorf(ch.sym, "nosplit stack overflow") + sc.print(ch, limit) +} + +func (sc *stkChk) print(ch *chain, limit int) { + ldr := sc.ldr + ctxt := sc.ctxt + var name string + if ch.sym != 0 { + name = ldr.SymName(ch.sym) + if ldr.IsNoSplit(ch.sym) { + name += " (nosplit)" + } + } else { + name = "function pointer" + } + + if ch.up == nil { + // top of chain. ch.sym != 0. + if ldr.IsNoSplit(ch.sym) { + fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name) + } else { + fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name) + } + } else { + sc.print(ch.up, ch.limit+callsize(ctxt)) + if !haslinkregister(ctxt) { + fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name) + } + } + + if ch.limit != limit { + fmt.Printf("\t%d\tafter %s uses %d\n", limit, name, ch.limit-limit) + } +} + +func usage() { + fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n") + objabi.Flagprint(os.Stderr) + Exit(2) +} + +type SymbolType int8 // TODO: after genasmsym is gone, maybe rename to plan9typeChar or something + +const ( + // see also https://9p.io/magic/man2html/1/nm + TextSym SymbolType = 'T' + DataSym SymbolType = 'D' + BSSSym SymbolType = 'B' + UndefinedSym SymbolType = 'U' + TLSSym SymbolType = 't' + FrameSym SymbolType = 'm' + ParamSym SymbolType = 'p' + AutoSym SymbolType = 'a' + + // Deleted auto (not a real sym, just placeholder for type) + DeletedAutoSym = 'x' +) + +// defineInternal defines a symbol used internally by the go runtime. +func (ctxt *Link) defineInternal(p string, t sym.SymKind) loader.Sym { + s := ctxt.loader.CreateSymForUpdate(p, 0) + s.SetType(t) + s.SetSpecial(true) + s.SetLocal(true) + return s.Sym() +} + +func (ctxt *Link) xdefine(p string, t sym.SymKind, v int64) loader.Sym { + s := ctxt.defineInternal(p, t) + ctxt.loader.SetSymValue(s, v) + return s +} + +func datoff(ldr *loader.Loader, s loader.Sym, addr int64) int64 { + if uint64(addr) >= Segdata.Vaddr { + return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff) + } + if uint64(addr) >= Segtext.Vaddr { + return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff) + } + ldr.Errorf(s, "invalid datoff %#x", addr) + return 0 +} + +func Entryvalue(ctxt *Link) int64 { + a := *flagEntrySymbol + if a[0] >= '0' && a[0] <= '9' { + return atolwhex(a) + } + ldr := ctxt.loader + s := ldr.Lookup(a, 0) + st := ldr.SymType(s) + if st == 0 { + return *FlagTextAddr + } + if !ctxt.IsAIX() && st != sym.STEXT { + ldr.Errorf(s, "entry not text") + } + return ldr.SymValue(s) +} + +func (ctxt *Link) callgraph() { + if !*FlagC { + return + } + + ldr := ctxt.loader + for _, s := range ctxt.Textp { + relocs := ldr.Relocs(s) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At(i) + rs := r.Sym() + if rs == 0 { + continue + } + if r.Type().IsDirectCall() && (ldr.SymType(rs) == sym.STEXT || ldr.SymType(rs) == sym.SABIALIAS) { + ctxt.Logf("%s calls %s\n", ldr.SymName(s), ldr.SymName(rs)) + } + } + } +} + +func Rnd(v int64, r int64) int64 { + if r <= 0 { + return v + } + v += r - 1 + c := v % r + if c < 0 { + c += r + } + v -= c + return v +} + +func bgetc(r *bio.Reader) int { + c, err := r.ReadByte() + if err != nil { + if err != io.EOF { + log.Fatalf("reading input: %v", err) + } + return -1 + } + return int(c) +} + +type markKind uint8 // for postorder traversal +const ( + _ markKind = iota + visiting + visited +) + +func postorder(libs []*sym.Library) []*sym.Library { + order := make([]*sym.Library, 0, len(libs)) // hold the result + mark := make(map[*sym.Library]markKind, len(libs)) + for _, lib := range libs { + dfs(lib, mark, &order) + } + return order +} + +func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library) { + if mark[lib] == visited { + return + } + if mark[lib] == visiting { + panic("found import cycle while visiting " + lib.Pkg) + } + mark[lib] = visiting + for _, i := range lib.Imports { + dfs(i, mark, order) + } + mark[lib] = visited + *order = append(*order, lib) +} + +func ElfSymForReloc(ctxt *Link, s loader.Sym) int32 { + // If putelfsym created a local version of this symbol, use that in all + // relocations. + les := ctxt.loader.SymLocalElfSym(s) + if les != 0 { + return les + } else { + return ctxt.loader.SymElfSym(s) + } +} + +func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, elfRelocTyp uint32) { + if ldr.SymGot(s) >= 0 { + return + } + + Adddynsym(ldr, target, syms, s) + got := ldr.MakeSymbolUpdater(syms.GOT) + ldr.SetGot(s, int32(got.Size())) + got.AddUint(target.Arch, 0) + + if target.IsElf() { + if target.Arch.PtrSize == 8 { + rela := ldr.MakeSymbolUpdater(syms.Rela) + rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + rela.AddUint64(target.Arch, elf.R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp)) + rela.AddUint64(target.Arch, 0) + } else { + rel := ldr.MakeSymbolUpdater(syms.Rel) + rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) + rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), elfRelocTyp)) + } + } else if target.IsDarwin() { + leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT) + leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s))) + if target.IsPIE() && target.IsInternal() { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation. + // Here we record what are needed and encode them later. + MachoAddBind(int64(ldr.SymGot(s)), s) + } + } else { + ldr.Errorf(s, "addgotsym: unsupported binary format") + } +} diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go new file mode 100644 index 0000000..f26d051 --- /dev/null +++ b/src/cmd/link/internal/ld/link.go @@ -0,0 +1,176 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bufio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "fmt" +) + +type Shlib struct { + Path string + Hash []byte + Deps []string + File *elf.File +} + +// Link holds the context for writing object code from a compiler +// or for reading that input into the linker. +type Link struct { + Target + ErrorReporter + ArchSyms + + outSem chan int // limits the number of output writers + Out *OutBuf + + version int // current version number for static/file-local symbols + + Debugvlog int + Bso *bufio.Writer + + Loaded bool // set after all inputs have been loaded as symbols + + compressDWARF bool + + Libdir []string + Library []*sym.Library + LibraryByPkg map[string]*sym.Library + Shlibs []Shlib + Textp []loader.Sym + Moduledata loader.Sym + + PackageFile map[string]string + PackageShlib map[string]string + + tramps []loader.Sym // trampolines + + compUnits []*sym.CompilationUnit // DWARF compilation units + runtimeCU *sym.CompilationUnit // One of the runtime CUs, the last one seen. + + loader *loader.Loader + cgodata []cgodata // cgo directives to load, three strings are args for loadcgo + + cgo_export_static map[string]bool + cgo_export_dynamic map[string]bool + + datap []loader.Sym + dynexp []loader.Sym + + // Elf symtab variables. + numelfsym int // starts at 0, 1 is reserved + + // These are symbols that created and written by the linker. + // Rather than creating a symbol, and writing all its data into the heap, + // you can create a symbol, and just a generation function will be called + // after the symbol's been created in the output mmap. + generatorSyms map[loader.Sym]generatorFunc +} + +type cgodata struct { + file string + pkg string + directives [][]string +} + +// The smallest possible offset from the hardware stack pointer to a local +// variable on the stack. Architectures that use a link register save its value +// on the stack in the function prologue and so always have a pointer between +// the hardware stack pointer and the local variable area. +func (ctxt *Link) FixedFrameSize() int64 { + switch ctxt.Arch.Family { + case sys.AMD64, sys.I386: + return 0 + case sys.PPC64: + // PIC code on ppc64le requires 32 bytes of stack, and it's easier to + // just use that much stack always on ppc64x. + return int64(4 * ctxt.Arch.PtrSize) + default: + return int64(ctxt.Arch.PtrSize) + } +} + +func (ctxt *Link) Logf(format string, args ...interface{}) { + fmt.Fprintf(ctxt.Bso, format, args...) + ctxt.Bso.Flush() +} + +func addImports(ctxt *Link, l *sym.Library, pn string) { + pkg := objabi.PathToPrefix(l.Pkg) + for _, imp := range l.Autolib { + lib := addlib(ctxt, pkg, pn, imp.Pkg, imp.Fingerprint) + if lib != nil { + l.Imports = append(l.Imports, lib) + } + } + l.Autolib = nil +} + +// Allocate a new version (i.e. symbol namespace). +func (ctxt *Link) IncVersion() int { + ctxt.version++ + return ctxt.version - 1 +} + +// returns the maximum version number +func (ctxt *Link) MaxVersion() int { + return ctxt.version +} + +// generatorFunc is a convenience type. +// Linker created symbols that are large, and shouldn't really live in the +// heap can define a generator function, and their bytes can be generated +// directly in the output mmap. +// +// Generator symbols shouldn't grow the symbol size, and might be called in +// parallel in the future. +// +// Generator Symbols have their Data set to the mmapped area when the +// generator is called. +type generatorFunc func(*Link, loader.Sym) + +// createGeneratorSymbol is a convenience method for creating a generator +// symbol. +func (ctxt *Link) createGeneratorSymbol(name string, version int, t sym.SymKind, size int64, gen generatorFunc) loader.Sym { + ldr := ctxt.loader + s := ldr.LookupOrCreateSym(name, version) + ldr.SetIsGeneratedSym(s, true) + sb := ldr.MakeSymbolUpdater(s) + sb.SetType(t) + sb.SetSize(size) + ctxt.generatorSyms[s] = gen + return s +} diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go new file mode 100644 index 0000000..a9f4d87 --- /dev/null +++ b/src/cmd/link/internal/ld/macho.go @@ -0,0 +1,1528 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "cmd/internal/codesign" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/macho" + "encoding/binary" + "fmt" + "io" + "os" + "sort" + "strings" + "unsafe" +) + +type MachoHdr struct { + cpu uint32 + subcpu uint32 +} + +type MachoSect struct { + name string + segname string + addr uint64 + size uint64 + off uint32 + align uint32 + reloc uint32 + nreloc uint32 + flag uint32 + res1 uint32 + res2 uint32 +} + +type MachoSeg struct { + name string + vsize uint64 + vaddr uint64 + fileoffset uint64 + filesize uint64 + prot1 uint32 + prot2 uint32 + nsect uint32 + msect uint32 + sect []MachoSect + flag uint32 +} + +// MachoPlatformLoad represents a LC_VERSION_MIN_* or +// LC_BUILD_VERSION load command. +type MachoPlatformLoad struct { + platform MachoPlatform // One of PLATFORM_* constants. + cmd MachoLoad +} + +type MachoLoad struct { + type_ uint32 + data []uint32 +} + +type MachoPlatform int + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, and SHeaders. + * May waste some. + */ +const ( + INITIAL_MACHO_HEADR = 4 * 1024 +) + +const ( + MACHO_CPU_AMD64 = 1<<24 | 7 + MACHO_CPU_386 = 7 + MACHO_SUBCPU_X86 = 3 + MACHO_CPU_ARM = 12 + MACHO_SUBCPU_ARM = 0 + MACHO_SUBCPU_ARMV7 = 9 + MACHO_CPU_ARM64 = 1<<24 | 12 + MACHO_SUBCPU_ARM64_ALL = 0 + MACHO32SYMSIZE = 12 + MACHO64SYMSIZE = 16 + MACHO_X86_64_RELOC_UNSIGNED = 0 + MACHO_X86_64_RELOC_SIGNED = 1 + MACHO_X86_64_RELOC_BRANCH = 2 + MACHO_X86_64_RELOC_GOT_LOAD = 3 + MACHO_X86_64_RELOC_GOT = 4 + MACHO_X86_64_RELOC_SUBTRACTOR = 5 + MACHO_X86_64_RELOC_SIGNED_1 = 6 + MACHO_X86_64_RELOC_SIGNED_2 = 7 + MACHO_X86_64_RELOC_SIGNED_4 = 8 + MACHO_ARM_RELOC_VANILLA = 0 + MACHO_ARM_RELOC_PAIR = 1 + MACHO_ARM_RELOC_SECTDIFF = 2 + MACHO_ARM_RELOC_BR24 = 5 + MACHO_ARM64_RELOC_UNSIGNED = 0 + MACHO_ARM64_RELOC_BRANCH26 = 2 + MACHO_ARM64_RELOC_PAGE21 = 3 + MACHO_ARM64_RELOC_PAGEOFF12 = 4 + MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5 + MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6 + MACHO_ARM64_RELOC_ADDEND = 10 + MACHO_GENERIC_RELOC_VANILLA = 0 + MACHO_FAKE_GOTPCREL = 100 +) + +const ( + MH_MAGIC = 0xfeedface + MH_MAGIC_64 = 0xfeedfacf + + MH_OBJECT = 0x1 + MH_EXECUTE = 0x2 + + MH_NOUNDEFS = 0x1 + MH_DYLDLINK = 0x4 + MH_PIE = 0x200000 +) + +const ( + LC_SEGMENT = 0x1 + LC_SYMTAB = 0x2 + LC_SYMSEG = 0x3 + LC_THREAD = 0x4 + LC_UNIXTHREAD = 0x5 + LC_LOADFVMLIB = 0x6 + LC_IDFVMLIB = 0x7 + LC_IDENT = 0x8 + LC_FVMFILE = 0x9 + LC_PREPAGE = 0xa + LC_DYSYMTAB = 0xb + LC_LOAD_DYLIB = 0xc + LC_ID_DYLIB = 0xd + LC_LOAD_DYLINKER = 0xe + LC_ID_DYLINKER = 0xf + LC_PREBOUND_DYLIB = 0x10 + LC_ROUTINES = 0x11 + LC_SUB_FRAMEWORK = 0x12 + LC_SUB_UMBRELLA = 0x13 + LC_SUB_CLIENT = 0x14 + LC_SUB_LIBRARY = 0x15 + LC_TWOLEVEL_HINTS = 0x16 + LC_PREBIND_CKSUM = 0x17 + LC_LOAD_WEAK_DYLIB = 0x80000018 + LC_SEGMENT_64 = 0x19 + LC_ROUTINES_64 = 0x1a + LC_UUID = 0x1b + LC_RPATH = 0x8000001c + LC_CODE_SIGNATURE = 0x1d + LC_SEGMENT_SPLIT_INFO = 0x1e + LC_REEXPORT_DYLIB = 0x8000001f + LC_LAZY_LOAD_DYLIB = 0x20 + LC_ENCRYPTION_INFO = 0x21 + LC_DYLD_INFO = 0x22 + LC_DYLD_INFO_ONLY = 0x80000022 + LC_LOAD_UPWARD_DYLIB = 0x80000023 + LC_VERSION_MIN_MACOSX = 0x24 + LC_VERSION_MIN_IPHONEOS = 0x25 + LC_FUNCTION_STARTS = 0x26 + LC_DYLD_ENVIRONMENT = 0x27 + LC_MAIN = 0x80000028 + LC_DATA_IN_CODE = 0x29 + LC_SOURCE_VERSION = 0x2A + LC_DYLIB_CODE_SIGN_DRS = 0x2B + LC_ENCRYPTION_INFO_64 = 0x2C + LC_LINKER_OPTION = 0x2D + LC_LINKER_OPTIMIZATION_HINT = 0x2E + LC_VERSION_MIN_TVOS = 0x2F + LC_VERSION_MIN_WATCHOS = 0x30 + LC_VERSION_NOTE = 0x31 + LC_BUILD_VERSION = 0x32 +) + +const ( + S_REGULAR = 0x0 + S_ZEROFILL = 0x1 + S_NON_LAZY_SYMBOL_POINTERS = 0x6 + S_SYMBOL_STUBS = 0x8 + S_MOD_INIT_FUNC_POINTERS = 0x9 + S_ATTR_PURE_INSTRUCTIONS = 0x80000000 + S_ATTR_DEBUG = 0x02000000 + S_ATTR_SOME_INSTRUCTIONS = 0x00000400 +) + +const ( + PLATFORM_MACOS MachoPlatform = 1 + PLATFORM_IOS MachoPlatform = 2 + PLATFORM_TVOS MachoPlatform = 3 + PLATFORM_WATCHOS MachoPlatform = 4 + PLATFORM_BRIDGEOS MachoPlatform = 5 +) + +// rebase table opcode +const ( + REBASE_TYPE_POINTER = 1 + REBASE_TYPE_TEXT_ABSOLUTE32 = 2 + REBASE_TYPE_TEXT_PCREL32 = 3 + + REBASE_OPCODE_MASK = 0xF0 + REBASE_IMMEDIATE_MASK = 0x0F + REBASE_OPCODE_DONE = 0x00 + REBASE_OPCODE_SET_TYPE_IMM = 0x10 + REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20 + REBASE_OPCODE_ADD_ADDR_ULEB = 0x30 + REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40 + REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50 + REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60 + REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70 + REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80 +) + +// bind table opcode +const ( + BIND_TYPE_POINTER = 1 + BIND_TYPE_TEXT_ABSOLUTE32 = 2 + BIND_TYPE_TEXT_PCREL32 = 3 + + BIND_SPECIAL_DYLIB_SELF = 0 + BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1 + BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2 + BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3 + + BIND_OPCODE_MASK = 0xF0 + BIND_IMMEDIATE_MASK = 0x0F + BIND_OPCODE_DONE = 0x00 + BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10 + BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20 + BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30 + BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40 + BIND_OPCODE_SET_TYPE_IMM = 0x50 + BIND_OPCODE_SET_ADDEND_SLEB = 0x60 + BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70 + BIND_OPCODE_ADD_ADDR_ULEB = 0x80 + BIND_OPCODE_DO_BIND = 0x90 + BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0 + BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0 + BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0 + BIND_OPCODE_THREADED = 0xD0 + BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00 + BIND_SUBOPCODE_THREADED_APPLY = 0x01 +) + +const machoHeaderSize64 = 8 * 4 // size of 64-bit Mach-O header + +// Mach-O file writing +// https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html + +var machohdr MachoHdr + +var load []MachoLoad + +var machoPlatform MachoPlatform + +var seg [16]MachoSeg + +var nseg int + +var ndebug int + +var nsect int + +const ( + SymKindLocal = 0 + iota + SymKindExtdef + SymKindUndef + NumSymKind +) + +var nkind [NumSymKind]int + +var sortsym []loader.Sym + +var nsortsym int + +// Amount of space left for adding load commands +// that refer to dynamic libraries. Because these have +// to go in the Mach-O header, we can't just pick a +// "big enough" header size. The initial header is +// one page, the non-dynamic library stuff takes +// up about 1300 bytes; we overestimate that as 2k. +var loadBudget = INITIAL_MACHO_HEADR - 2*1024 + +func getMachoHdr() *MachoHdr { + return &machohdr +} + +func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad { + if arch.PtrSize == 8 && (ndata&1 != 0) { + ndata++ + } + + load = append(load, MachoLoad{}) + l := &load[len(load)-1] + l.type_ = type_ + l.data = make([]uint32, ndata) + return l +} + +func newMachoSeg(name string, msect int) *MachoSeg { + if nseg >= len(seg) { + Exitf("too many segs") + } + + s := &seg[nseg] + nseg++ + s.name = name + s.msect = uint32(msect) + s.sect = make([]MachoSect, msect) + return s +} + +func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect { + if seg.nsect >= seg.msect { + Exitf("too many sects in segment %s", seg.name) + } + + s := &seg.sect[seg.nsect] + seg.nsect++ + s.name = name + s.segname = segname + nsect++ + return s +} + +// Generic linking code. + +var dylib []string + +var linkoff int64 + +func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int { + o1 := out.Offset() + + loadsize := 4 * 4 * ndebug + for i := range load { + loadsize += 4 * (len(load[i].data) + 2) + } + if arch.PtrSize == 8 { + loadsize += 18 * 4 * nseg + loadsize += 20 * 4 * nsect + } else { + loadsize += 14 * 4 * nseg + loadsize += 17 * 4 * nsect + } + + if arch.PtrSize == 8 { + out.Write32(MH_MAGIC_64) + } else { + out.Write32(MH_MAGIC) + } + out.Write32(machohdr.cpu) + out.Write32(machohdr.subcpu) + if linkmode == LinkExternal { + out.Write32(MH_OBJECT) /* file type - mach object */ + } else { + out.Write32(MH_EXECUTE) /* file type - mach executable */ + } + out.Write32(uint32(len(load)) + uint32(nseg) + uint32(ndebug)) + out.Write32(uint32(loadsize)) + flags := uint32(0) + if nkind[SymKindUndef] == 0 { + flags |= MH_NOUNDEFS + } + if ctxt.IsPIE() && linkmode == LinkInternal { + flags |= MH_PIE | MH_DYLDLINK + } + out.Write32(flags) /* flags */ + if arch.PtrSize == 8 { + out.Write32(0) /* reserved */ + } + + for i := 0; i < nseg; i++ { + s := &seg[i] + if arch.PtrSize == 8 { + out.Write32(LC_SEGMENT_64) + out.Write32(72 + 80*s.nsect) + out.WriteStringN(s.name, 16) + out.Write64(s.vaddr) + out.Write64(s.vsize) + out.Write64(s.fileoffset) + out.Write64(s.filesize) + out.Write32(s.prot1) + out.Write32(s.prot2) + out.Write32(s.nsect) + out.Write32(s.flag) + } else { + out.Write32(LC_SEGMENT) + out.Write32(56 + 68*s.nsect) + out.WriteStringN(s.name, 16) + out.Write32(uint32(s.vaddr)) + out.Write32(uint32(s.vsize)) + out.Write32(uint32(s.fileoffset)) + out.Write32(uint32(s.filesize)) + out.Write32(s.prot1) + out.Write32(s.prot2) + out.Write32(s.nsect) + out.Write32(s.flag) + } + + for j := uint32(0); j < s.nsect; j++ { + t := &s.sect[j] + if arch.PtrSize == 8 { + out.WriteStringN(t.name, 16) + out.WriteStringN(t.segname, 16) + out.Write64(t.addr) + out.Write64(t.size) + out.Write32(t.off) + out.Write32(t.align) + out.Write32(t.reloc) + out.Write32(t.nreloc) + out.Write32(t.flag) + out.Write32(t.res1) /* reserved */ + out.Write32(t.res2) /* reserved */ + out.Write32(0) /* reserved */ + } else { + out.WriteStringN(t.name, 16) + out.WriteStringN(t.segname, 16) + out.Write32(uint32(t.addr)) + out.Write32(uint32(t.size)) + out.Write32(t.off) + out.Write32(t.align) + out.Write32(t.reloc) + out.Write32(t.nreloc) + out.Write32(t.flag) + out.Write32(t.res1) /* reserved */ + out.Write32(t.res2) /* reserved */ + } + } + } + + for i := range load { + l := &load[i] + out.Write32(l.type_) + out.Write32(4 * (uint32(len(l.data)) + 2)) + for j := 0; j < len(l.data); j++ { + out.Write32(l.data[j]) + } + } + + return int(out.Offset() - o1) +} + +func (ctxt *Link) domacho() { + if *FlagD { + return + } + + // Copy platform load command. + for _, h := range hostobj { + load, err := hostobjMachoPlatform(&h) + if err != nil { + Exitf("%v", err) + } + if load != nil { + machoPlatform = load.platform + ml := newMachoLoad(ctxt.Arch, load.cmd.type_, uint32(len(load.cmd.data))) + copy(ml.data, load.cmd.data) + break + } + } + if machoPlatform == 0 { + switch ctxt.Arch.Family { + default: + machoPlatform = PLATFORM_MACOS + if ctxt.LinkMode == LinkInternal { + // For lldb, must say LC_VERSION_MIN_MACOSX or else + // it won't know that this Mach-O binary is from OS X + // (could be iOS or WatchOS instead). + // Go on iOS uses linkmode=external, and linkmode=external + // adds this itself. So we only need this code for linkmode=internal + // and we can assume OS X. + // + // See golang.org/issues/12941. + // + // The version must be at least 10.9; see golang.org/issues/30488. + ml := newMachoLoad(ctxt.Arch, LC_VERSION_MIN_MACOSX, 2) + ml.data[0] = 10<<16 | 9<<8 | 0<<0 // OS X version 10.9.0 + ml.data[1] = 10<<16 | 9<<8 | 0<<0 // SDK 10.9.0 + } + case sys.ARM, sys.ARM64: + machoPlatform = PLATFORM_IOS + } + } + + // empirically, string table must begin with " \x00". + s := ctxt.loader.LookupOrCreateSym(".machosymstr", 0) + sb := ctxt.loader.MakeSymbolUpdater(s) + + sb.SetType(sym.SMACHOSYMSTR) + sb.SetReachable(true) + sb.AddUint8(' ') + sb.AddUint8('\x00') + + s = ctxt.loader.LookupOrCreateSym(".machosymtab", 0) + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOSYMTAB) + sb.SetReachable(true) + + if ctxt.IsInternal() { + s = ctxt.loader.LookupOrCreateSym(".plt", 0) // will be __symbol_stub + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOPLT) + sb.SetReachable(true) + + s = ctxt.loader.LookupOrCreateSym(".got", 0) // will be __nl_symbol_ptr + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOGOT) + sb.SetReachable(true) + sb.SetAlign(4) + + s = ctxt.loader.LookupOrCreateSym(".linkedit.plt", 0) // indirect table for .plt + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOINDIRECTPLT) + sb.SetReachable(true) + + s = ctxt.loader.LookupOrCreateSym(".linkedit.got", 0) // indirect table for .got + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHOINDIRECTGOT) + sb.SetReachable(true) + } + + // Add a dummy symbol that will become the __asm marker section. + if ctxt.IsExternal() { + s = ctxt.loader.LookupOrCreateSym(".llvmasm", 0) + sb = ctxt.loader.MakeSymbolUpdater(s) + sb.SetType(sym.SMACHO) + sb.SetReachable(true) + sb.AddUint8(0) + } + + // 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) { + if seenlib[lib] || linkmode == LinkExternal { + return + } + seenlib[lib] = true + + // Will need to store the library name rounded up + // and 24 bytes of header metadata. If not enough + // space, grab another page of initial space at the + // beginning of the output file. + loadBudget -= (len(lib)+7)/8*8 + 24 + + if loadBudget < 0 { + HEADR += 4096 + *FlagTextAddr += 4096 + loadBudget += 4096 + } + + dylib = append(dylib, lib) +} + +func machoshbits(ctxt *Link, mseg *MachoSeg, sect *sym.Section, segname string) { + buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1) + + msect := newMachoSect(mseg, buf, segname) + + if sect.Rellen > 0 { + msect.reloc = uint32(sect.Reloff) + msect.nreloc = uint32(sect.Rellen / 8) + } + + for 1<<msect.align < sect.Align { + msect.align++ + } + msect.addr = sect.Vaddr + msect.size = sect.Length + + if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { + // data in file + if sect.Length > sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr { + Errorf(nil, "macho cannot represent section %s crossing data and bss", sect.Name) + } + msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr) + } else { + msect.off = 0 + msect.flag |= S_ZEROFILL + } + + if sect.Rwx&1 != 0 { + msect.flag |= S_ATTR_SOME_INSTRUCTIONS + } + + if sect.Name == ".text" { + msect.flag |= S_ATTR_PURE_INSTRUCTIONS + } + + if sect.Name == ".plt" { + msect.name = "__symbol_stub1" + msect.flag = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS | S_SYMBOL_STUBS + msect.res1 = 0 //nkind[SymKindLocal]; + msect.res2 = 6 + } + + if sect.Name == ".got" { + msect.name = "__nl_symbol_ptr" + msect.flag = S_NON_LAZY_SYMBOL_POINTERS + msect.res1 = uint32(ctxt.loader.SymSize(ctxt.ArchSyms.LinkEditPLT) / 4) /* offset into indirect symbol table */ + } + + if sect.Name == ".init_array" { + msect.name = "__mod_init_func" + msect.flag = S_MOD_INIT_FUNC_POINTERS + } + + // Some platforms such as watchOS and tvOS require binaries with + // bitcode enabled. The Go toolchain can't output bitcode, so use + // a marker section in the __LLVM segment, "__asm", to tell the Apple + // toolchain that the Go text came from assembler and thus has no + // bitcode. This is not true, but Kotlin/Native, Rust and Flutter + // are also using this trick. + if sect.Name == ".llvmasm" { + msect.name = "__asm" + msect.segname = "__LLVM" + } + + if segname == "__DWARF" { + msect.flag |= S_ATTR_DEBUG + } +} + +func asmbMacho(ctxt *Link) { + machlink := doMachoLink(ctxt) + if !*FlagS && ctxt.IsExternal() { + symo := int64(Segdwarf.Fileoff + uint64(Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))) + uint64(machlink)) + ctxt.Out.SeekSet(symo) + machoEmitReloc(ctxt) + } + ctxt.Out.SeekSet(0) + + ldr := ctxt.loader + + /* apple MACH */ + va := *FlagTextAddr - int64(HEADR) + + mh := getMachoHdr() + switch ctxt.Arch.Family { + default: + Exitf("unknown macho architecture: %v", ctxt.Arch.Family) + + case sys.AMD64: + mh.cpu = MACHO_CPU_AMD64 + mh.subcpu = MACHO_SUBCPU_X86 + + case sys.ARM64: + mh.cpu = MACHO_CPU_ARM64 + mh.subcpu = MACHO_SUBCPU_ARM64_ALL + } + + var ms *MachoSeg + if ctxt.LinkMode == LinkExternal { + /* segment for entire file */ + ms = newMachoSeg("", 40) + + ms.fileoffset = Segtext.Fileoff + ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff + ms.vsize = Segdwarf.Vaddr + Segdwarf.Length - Segtext.Vaddr + } + + /* segment for zero page */ + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__PAGEZERO", 0) + ms.vsize = uint64(va) + } + + /* text */ + v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__TEXT", 20) + ms.vaddr = uint64(va) + ms.vsize = uint64(v) + ms.fileoffset = 0 + ms.filesize = uint64(v) + ms.prot1 = 7 + ms.prot2 = 5 + } + + for _, sect := range Segtext.Sections { + machoshbits(ctxt, ms, sect, "__TEXT") + } + + /* rodata */ + if ctxt.LinkMode != LinkExternal && Segrelrodata.Length > 0 { + ms = newMachoSeg("__DATA_CONST", 20) + ms.vaddr = Segrelrodata.Vaddr + ms.vsize = Segrelrodata.Length + ms.fileoffset = Segrelrodata.Fileoff + ms.filesize = Segrelrodata.Filelen + ms.prot1 = 3 + ms.prot2 = 3 + ms.flag = 0x10 // SG_READ_ONLY + } + + for _, sect := range Segrelrodata.Sections { + machoshbits(ctxt, ms, sect, "__DATA_CONST") + } + + /* data */ + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__DATA", 20) + ms.vaddr = Segdata.Vaddr + ms.vsize = Segdata.Length + ms.fileoffset = Segdata.Fileoff + ms.filesize = Segdata.Filelen + ms.prot1 = 3 + ms.prot2 = 3 + } + + for _, sect := range Segdata.Sections { + machoshbits(ctxt, ms, sect, "__DATA") + } + + /* dwarf */ + if !*FlagW { + if ctxt.LinkMode != LinkExternal { + ms = newMachoSeg("__DWARF", 20) + ms.vaddr = Segdwarf.Vaddr + ms.vsize = 0 + ms.fileoffset = Segdwarf.Fileoff + ms.filesize = Segdwarf.Filelen + } + for _, sect := range Segdwarf.Sections { + machoshbits(ctxt, ms, sect, "__DWARF") + } + } + + if ctxt.LinkMode != LinkExternal { + switch ctxt.Arch.Family { + default: + Exitf("unknown macho architecture: %v", ctxt.Arch.Family) + + case sys.AMD64: + ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 42+2) + ml.data[0] = 4 /* thread type */ + ml.data[1] = 42 /* word count */ + ml.data[2+32] = uint32(Entryvalue(ctxt)) /* start pc */ + ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32) + + case sys.ARM64: + ml := newMachoLoad(ctxt.Arch, LC_MAIN, 4) + ml.data[0] = uint32(uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) + ml.data[1] = uint32((uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) >> 32) + } + } + + var codesigOff int64 + if !*FlagD { + // must match doMachoLink below + s1 := ldr.SymSize(ldr.Lookup(".machorebase", 0)) + s2 := ldr.SymSize(ldr.Lookup(".machobind", 0)) + s3 := ldr.SymSize(ldr.Lookup(".machosymtab", 0)) + s4 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT) + s5 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT) + s6 := ldr.SymSize(ldr.Lookup(".machosymstr", 0)) + s7 := ldr.SymSize(ldr.Lookup(".machocodesig", 0)) + + if ctxt.LinkMode != LinkExternal { + ms := newMachoSeg("__LINKEDIT", 0) + ms.vaddr = uint64(Rnd(int64(Segdata.Vaddr+Segdata.Length), int64(*FlagRound))) + ms.vsize = uint64(s1 + s2 + s3 + s4 + s5 + s6 + s7) + ms.fileoffset = uint64(linkoff) + ms.filesize = ms.vsize + ms.prot1 = 1 + ms.prot2 = 1 + + codesigOff = linkoff + s1 + s2 + s3 + s4 + s5 + s6 + } + + if ctxt.LinkMode != LinkExternal && ctxt.IsPIE() { + ml := newMachoLoad(ctxt.Arch, LC_DYLD_INFO_ONLY, 10) + ml.data[0] = uint32(linkoff) // rebase off + ml.data[1] = uint32(s1) // rebase size + ml.data[2] = uint32(linkoff + s1) // bind off + ml.data[3] = uint32(s2) // bind size + ml.data[4] = 0 // weak bind off + ml.data[5] = 0 // weak bind size + ml.data[6] = 0 // lazy bind off + ml.data[7] = 0 // lazy bind size + ml.data[8] = 0 // export + ml.data[9] = 0 // export size + } + + ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4) + ml.data[0] = uint32(linkoff + s1 + s2) /* symoff */ + ml.data[1] = uint32(nsortsym) /* nsyms */ + ml.data[2] = uint32(linkoff + s1 + s2 + s3 + s4 + s5) /* stroff */ + ml.data[3] = uint32(s6) /* strsize */ + + machodysymtab(ctxt, linkoff+s1+s2) + + if ctxt.LinkMode != LinkExternal { + ml := newMachoLoad(ctxt.Arch, LC_LOAD_DYLINKER, 6) + ml.data[0] = 12 /* offset to string */ + stringtouint32(ml.data[1:], "/usr/lib/dyld") + + for _, lib := range dylib { + ml = newMachoLoad(ctxt.Arch, LC_LOAD_DYLIB, 4+(uint32(len(lib))+1+7)/8*2) + ml.data[0] = 24 /* offset of string from beginning of load */ + ml.data[1] = 0 /* time stamp */ + ml.data[2] = 0 /* version */ + ml.data[3] = 0 /* compatibility version */ + stringtouint32(ml.data[4:], lib) + } + } + + if ctxt.IsInternal() && ctxt.NeedCodeSign() { + ml := newMachoLoad(ctxt.Arch, LC_CODE_SIGNATURE, 2) + ml.data[0] = uint32(codesigOff) + ml.data[1] = uint32(s7) + } + } + + a := machowrite(ctxt, ctxt.Arch, ctxt.Out, ctxt.LinkMode) + if int32(a) > HEADR { + Exitf("HEADR too small: %d > %d", a, HEADR) + } + + // Now we have written everything. Compute the code signature (which + // is a hash of the file content, so it must be done at last.) + if ctxt.IsInternal() && ctxt.NeedCodeSign() { + cs := ldr.Lookup(".machocodesig", 0) + data := ctxt.Out.Data() + if int64(len(data)) != codesigOff { + panic("wrong size") + } + codesign.Sign(ldr.Data(cs), bytes.NewReader(data), "a.out", codesigOff, int64(Segtext.Fileoff), int64(Segtext.Filelen), ctxt.IsExe() || ctxt.IsPIE()) + ctxt.Out.SeekSet(codesigOff) + ctxt.Out.Write(ldr.Data(cs)) + } +} + +func symkind(ldr *loader.Loader, s loader.Sym) int { + if ldr.SymType(s) == sym.SDYNIMPORT { + return SymKindUndef + } + if ldr.AttrCgoExport(s) { + return SymKindExtdef + } + return SymKindLocal +} + +func collectmachosyms(ctxt *Link) { + ldr := ctxt.loader + + addsym := func(s loader.Sym) { + sortsym = append(sortsym, s) + nkind[symkind(ldr, s)]++ + } + + // Add special runtime.text and runtime.etext symbols. + // We've already included this symbol in Textp on darwin if ctxt.DynlinkingGo(). + // See data.go:/textaddress + if !ctxt.DynlinkingGo() { + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + } + + // Add text symbols. + for _, s := range ctxt.Textp { + addsym(s) + } + + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + name := ldr.RawSymName(s) // TODO: try not to read the name + if name == "" || name[0] == '.' { + return false + } + return true + } + + // Add data symbols and external references. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + t := ldr.SymType(s) + if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata + if t == sym.STLSBSS { + // TLSBSS is not used on darwin. See data.go:allocateDataSections + continue + } + if !shouldBeInSymbolTable(s) { + continue + } + addsym(s) + } + + switch t { + case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT: + addsym(s) + } + + // Some 64-bit functions have a "$INODE64" or "$INODE64$UNIX2003" suffix. + if t == sym.SDYNIMPORT && ldr.SymDynimplib(s) == "/usr/lib/libSystem.B.dylib" { + // But only on macOS. + if machoPlatform == PLATFORM_MACOS { + switch n := ldr.SymExtname(s); n { + case "fdopendir": + switch objabi.GOARCH { + case "amd64": + ldr.SetSymExtname(s, n+"$INODE64") + } + case "readdir_r", "getfsstat": + switch objabi.GOARCH { + case "amd64": + ldr.SetSymExtname(s, n+"$INODE64") + } + } + } + } + } + + nsortsym = len(sortsym) +} + +func machosymorder(ctxt *Link) { + ldr := ctxt.loader + + // On Mac OS X Mountain Lion, we must sort exported symbols + // So we sort them here and pre-allocate dynid for them + // See https://golang.org/issue/4029 + for _, s := range ctxt.dynexp { + if !ldr.AttrReachable(s) { + panic("dynexp symbol is not reachable") + } + } + collectmachosyms(ctxt) + sort.Slice(sortsym[:nsortsym], func(i, j int) bool { + s1 := sortsym[i] + s2 := sortsym[j] + k1 := symkind(ldr, s1) + k2 := symkind(ldr, s2) + if k1 != k2 { + return k1 < k2 + } + return ldr.SymExtname(s1) < ldr.SymExtname(s2) // Note: unnamed symbols are not added in collectmachosyms + }) + for i, s := range sortsym { + ldr.SetSymDynid(s, int32(i)) + } +} + +// AddMachoSym adds s to Mach-O symbol table, used in GenSymLate. +// Currently only used on ARM64 when external linking. +func AddMachoSym(ldr *loader.Loader, s loader.Sym) { + ldr.SetSymDynid(s, int32(nsortsym)) + sortsym = append(sortsym, s) + nsortsym++ + nkind[symkind(ldr, s)]++ +} + +// machoShouldExport reports whether a symbol needs to be exported. +// +// When dynamically linking, all non-local variables and plugin-exported +// symbols need to be exported. +func machoShouldExport(ctxt *Link, ldr *loader.Loader, s loader.Sym) bool { + if !ctxt.DynlinkingGo() || ldr.AttrLocal(s) { + return false + } + if ctxt.BuildMode == BuildModePlugin && strings.HasPrefix(ldr.SymExtname(s), objabi.PathToPrefix(*flagPluginPath)) { + return true + } + name := ldr.RawSymName(s) + if strings.HasPrefix(name, "go.itab.") { + return true + } + if strings.HasPrefix(name, "type.") && !strings.HasPrefix(name, "type..") { + // reduce runtime typemap pressure, but do not + // export alg functions (type..*), as these + // appear in pclntable. + return true + } + if strings.HasPrefix(name, "go.link.pkghash") { + return true + } + return ldr.SymType(s) >= sym.SFirstWritable // only writable sections +} + +func machosymtab(ctxt *Link) { + ldr := ctxt.loader + symtab := ldr.CreateSymForUpdate(".machosymtab", 0) + symstr := ldr.CreateSymForUpdate(".machosymstr", 0) + + for _, s := range sortsym[:nsortsym] { + symtab.AddUint32(ctxt.Arch, uint32(symstr.Size())) + + export := machoShouldExport(ctxt, ldr, s) + + // Prefix symbol names with "_" to match the system toolchain. + // (We used to only prefix C symbols, which is all required for the build. + // But some tools don't recognize Go symbols as symbols, so we prefix them + // as well.) + symstr.AddUint8('_') + + // replace "·" as ".", because DTrace cannot handle it. + symstr.Addstring(strings.Replace(ldr.SymExtname(s), "·", ".", -1)) + + if t := ldr.SymType(s); t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT { + symtab.AddUint8(0x01) // type N_EXT, external symbol + symtab.AddUint8(0) // no section + symtab.AddUint16(ctxt.Arch, 0) // desc + symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value + } else { + if export || ldr.AttrCgoExportDynamic(s) { + symtab.AddUint8(0x0f) // N_SECT | N_EXT + } else if ldr.AttrCgoExportStatic(s) { + // Only export statically, not dynamically. (N_PEXT is like hidden visibility) + symtab.AddUint8(0x1f) // N_SECT | N_EXT | N_PEXT + } else { + symtab.AddUint8(0x0e) // N_SECT + } + o := s + if outer := ldr.OuterSym(o); outer != 0 { + o = outer + } + if ldr.SymSect(o) == nil { + ldr.Errorf(s, "missing section for symbol") + symtab.AddUint8(0) + } else { + symtab.AddUint8(uint8(ldr.SymSect(o).Extnum)) + } + symtab.AddUint16(ctxt.Arch, 0) // desc + symtab.AddUintXX(ctxt.Arch, uint64(ldr.SymAddr(s)), ctxt.Arch.PtrSize) + } + } +} + +func machodysymtab(ctxt *Link, base int64) { + ml := newMachoLoad(ctxt.Arch, LC_DYSYMTAB, 18) + + n := 0 + ml.data[0] = uint32(n) /* ilocalsym */ + ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */ + n += nkind[SymKindLocal] + + ml.data[2] = uint32(n) /* iextdefsym */ + ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */ + n += nkind[SymKindExtdef] + + ml.data[4] = uint32(n) /* iundefsym */ + ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */ + + ml.data[6] = 0 /* tocoffset */ + ml.data[7] = 0 /* ntoc */ + ml.data[8] = 0 /* modtaboff */ + ml.data[9] = 0 /* nmodtab */ + ml.data[10] = 0 /* extrefsymoff */ + ml.data[11] = 0 /* nextrefsyms */ + + ldr := ctxt.loader + + // must match domacholink below + s1 := ldr.SymSize(ldr.Lookup(".machosymtab", 0)) + s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT) + s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT) + ml.data[12] = uint32(base + s1) /* indirectsymoff */ + ml.data[13] = uint32((s2 + s3) / 4) /* nindirectsyms */ + + ml.data[14] = 0 /* extreloff */ + ml.data[15] = 0 /* nextrel */ + ml.data[16] = 0 /* locreloff */ + ml.data[17] = 0 /* nlocrel */ +} + +func doMachoLink(ctxt *Link) int64 { + machosymtab(ctxt) + machoDyldInfo(ctxt) + + ldr := ctxt.loader + + // write data that will be linkedit section + s1 := ldr.Lookup(".machorebase", 0) + s2 := ldr.Lookup(".machobind", 0) + s3 := ldr.Lookup(".machosymtab", 0) + s4 := ctxt.ArchSyms.LinkEditPLT + s5 := ctxt.ArchSyms.LinkEditGOT + s6 := ldr.Lookup(".machosymstr", 0) + + size := ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4) + ldr.SymSize(s5) + ldr.SymSize(s6) + + // Force the linkedit section to end on a 16-byte + // boundary. This allows pure (non-cgo) Go binaries + // to be code signed correctly. + // + // Apple's codesign_allocate (a helper utility for + // the codesign utility) can do this fine itself if + // it is run on a dynamic Mach-O binary. However, + // when it is run on a pure (non-cgo) Go binary, where + // the linkedit section is mostly empty, it fails to + // account for the extra padding that it itself adds + // when adding the LC_CODE_SIGNATURE load command + // (which must be aligned on a 16-byte boundary). + // + // By forcing the linkedit section to end on a 16-byte + // boundary, codesign_allocate will not need to apply + // any alignment padding itself, working around the + // issue. + if size%16 != 0 { + n := 16 - size%16 + s6b := ldr.MakeSymbolUpdater(s6) + s6b.Grow(s6b.Size() + n) + s6b.SetSize(s6b.Size() + n) + size += n + } + + if size > 0 { + linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segrelrodata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound)) + ctxt.Out.SeekSet(linkoff) + + ctxt.Out.Write(ldr.Data(s1)) + ctxt.Out.Write(ldr.Data(s2)) + ctxt.Out.Write(ldr.Data(s3)) + ctxt.Out.Write(ldr.Data(s4)) + ctxt.Out.Write(ldr.Data(s5)) + ctxt.Out.Write(ldr.Data(s6)) + + // Add code signature if necessary. This must be the last. + s7 := machoCodeSigSym(ctxt, linkoff+size) + size += ldr.SymSize(s7) + } + + return Rnd(size, int64(*FlagRound)) +} + +func machorelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) { + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + ldr := ctxt.loader + + for i, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + + eaddr := sect.Vaddr + sect.Length + for _, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymValue(s) >= int64(eaddr) { + break + } + + // Compute external relocations on the go, and pass to Machoreloc1 + // to stream out. + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + rr, ok := extreloc(ctxt, ldr, s, r) + if !ok { + continue + } + if rr.Xsym == 0 { + ldr.Errorf(s, "missing xsym in relocation") + continue + } + if !ldr.AttrReachable(rr.Xsym) { + ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym)) + } + if !thearch.Machoreloc1(ctxt.Arch, out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) { + ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym())) + } + } + } + + // sanity check + if uint64(out.Offset()) != sect.Reloff+sect.Rellen { + panic("machorelocsect: size mismatch") + } +} + +func machoEmitReloc(ctxt *Link) { + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + sizeExtRelocs(ctxt, thearch.MachorelocSize) + relocSect, wg := relocSectFn(ctxt, machorelocsect) + + relocSect(ctxt, Segtext.Sections[0], ctxt.Textp) + for _, sect := range Segtext.Sections[1:] { + relocSect(ctxt, sect, ctxt.datap) + } + for _, sect := range Segrelrodata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for _, sect := range Segdata.Sections { + relocSect(ctxt, sect, ctxt.datap) + } + for i := 0; i < len(Segdwarf.Sections); i++ { + sect := Segdwarf.Sections[i] + si := dwarfp[i] + if si.secSym() != loader.Sym(sect.Sym) || + ctxt.loader.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + relocSect(ctxt, sect, si.syms) + } + wg.Wait() +} + +// hostobjMachoPlatform returns the first platform load command found +// in the host object, if any. +func hostobjMachoPlatform(h *Hostobj) (*MachoPlatformLoad, error) { + f, err := os.Open(h.file) + if err != nil { + return nil, fmt.Errorf("%s: failed to open host object: %v\n", h.file, err) + } + defer f.Close() + sr := io.NewSectionReader(f, h.off, h.length) + m, err := macho.NewFile(sr) + if err != nil { + // Not a valid Mach-O file. + return nil, nil + } + return peekMachoPlatform(m) +} + +// peekMachoPlatform returns the first LC_VERSION_MIN_* or LC_BUILD_VERSION +// load command found in the Mach-O file, if any. +func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) { + for _, cmd := range m.Loads { + raw := cmd.Raw() + ml := MachoLoad{ + type_: m.ByteOrder.Uint32(raw), + } + // Skip the type and command length. + data := raw[8:] + var p MachoPlatform + switch ml.type_ { + case LC_VERSION_MIN_IPHONEOS: + p = PLATFORM_IOS + case LC_VERSION_MIN_MACOSX: + p = PLATFORM_MACOS + case LC_VERSION_MIN_WATCHOS: + p = PLATFORM_WATCHOS + case LC_VERSION_MIN_TVOS: + p = PLATFORM_TVOS + case LC_BUILD_VERSION: + p = MachoPlatform(m.ByteOrder.Uint32(data)) + default: + continue + } + ml.data = make([]uint32, len(data)/4) + r := bytes.NewReader(data) + if err := binary.Read(r, m.ByteOrder, &ml.data); err != nil { + return nil, err + } + return &MachoPlatformLoad{ + platform: p, + cmd: ml, + }, nil + } + return nil, nil +} + +// A rebase entry tells the dynamic linker the data at sym+off needs to be +// relocated when the in-memory image moves. (This is somewhat like, say, +// ELF R_X86_64_RELATIVE). +// For now, the only kind of entry we support is that the data is an absolute +// address. That seems all we need. +// In the binary it uses a compact stateful bytecode encoding. So we record +// entries as we go and build the table at the end. +type machoRebaseRecord struct { + sym loader.Sym + off int64 +} + +var machorebase []machoRebaseRecord + +func MachoAddRebase(s loader.Sym, off int64) { + machorebase = append(machorebase, machoRebaseRecord{s, off}) +} + +// A bind entry tells the dynamic linker the data at GOT+off should be bound +// to the address of the target symbol, which is a dynamic import. +// For now, the only kind of entry we support is that the data is an absolute +// address, and the source symbol is always the GOT. That seems all we need. +// In the binary it uses a compact stateful bytecode encoding. So we record +// entries as we go and build the table at the end. +type machoBindRecord struct { + off int64 + targ loader.Sym +} + +var machobind []machoBindRecord + +func MachoAddBind(off int64, targ loader.Sym) { + machobind = append(machobind, machoBindRecord{off, targ}) +} + +// Generate data for the dynamic linker, used in LC_DYLD_INFO_ONLY load command. +// See mach-o/loader.h, struct dyld_info_command, for the encoding. +// e.g. https://opensource.apple.com/source/xnu/xnu-6153.81.5/EXTERNAL_HEADERS/mach-o/loader.h +func machoDyldInfo(ctxt *Link) { + ldr := ctxt.loader + rebase := ldr.CreateSymForUpdate(".machorebase", 0) + bind := ldr.CreateSymForUpdate(".machobind", 0) + + if !(ctxt.IsPIE() && ctxt.IsInternal()) { + return + } + + segId := func(seg *sym.Segment) uint8 { + switch seg { + case &Segtext: + return 1 + case &Segrelrodata: + return 2 + case &Segdata: + if Segrelrodata.Length > 0 { + return 3 + } + return 2 + } + panic("unknown segment") + } + + dylibId := func(s loader.Sym) int { + slib := ldr.SymDynimplib(s) + for i, lib := range dylib { + if lib == slib { + return i + 1 + } + } + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP // don't know where it is from + } + + // Rebase table. + // TODO: use more compact encoding. The encoding is stateful, and + // we can use delta encoding. + rebase.AddUint8(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER) + for _, r := range machorebase { + seg := ldr.SymSect(r.sym).Seg + off := uint64(ldr.SymValue(r.sym)+r.off) - seg.Vaddr + rebase.AddUint8(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg)) + rebase.AddUleb(off) + + rebase.AddUint8(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1) + } + rebase.AddUint8(REBASE_OPCODE_DONE) + sz := Rnd(rebase.Size(), 8) + rebase.Grow(sz) + rebase.SetSize(sz) + + // Bind table. + // TODO: compact encoding, as above. + // TODO: lazy binding? + got := ctxt.GOT + seg := ldr.SymSect(got).Seg + gotAddr := ldr.SymValue(got) + bind.AddUint8(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER) + for _, r := range machobind { + off := uint64(gotAddr+r.off) - seg.Vaddr + bind.AddUint8(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg)) + bind.AddUleb(off) + + d := dylibId(r.targ) + if d > 0 && d < 128 { + bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | uint8(d)&0xf) + } else if d >= 128 { + bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) + bind.AddUleb(uint64(d)) + } else { // d <= 0 + bind.AddUint8(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | uint8(d)&0xf) + } + + bind.AddUint8(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM) + // target symbol name as a C string, with _ prefix + bind.AddUint8('_') + bind.Addstring(ldr.SymExtname(r.targ)) + + bind.AddUint8(BIND_OPCODE_DO_BIND) + } + bind.AddUint8(BIND_OPCODE_DONE) + sz = Rnd(bind.Size(), 16) // make it 16-byte aligned, see the comment in doMachoLink + bind.Grow(sz) + bind.SetSize(sz) + + // TODO: export table. + // The symbols names are encoded as a trie. I'm really too lazy to do that + // for now. + // Without it, the symbols are not dynamically exported, so they cannot be + // e.g. dlsym'd. But internal linking is not the default in that case, so + // it is fine. +} + +// machoCodeSigSym creates and returns a symbol for code signature. +// The symbol context is left as zeros, which will be generated at the end +// (as it depends on the rest of the file). +func machoCodeSigSym(ctxt *Link, codeSize int64) loader.Sym { + ldr := ctxt.loader + cs := ldr.CreateSymForUpdate(".machocodesig", 0) + if !ctxt.NeedCodeSign() || ctxt.IsExternal() { + return cs.Sym() + } + sz := codesign.Size(codeSize, "a.out") + cs.Grow(sz) + cs.SetSize(sz) + return cs.Sym() +} + +// machoCodeSign code-signs Mach-O file fname with an ad-hoc signature. +// This is used for updating an external linker generated binary. +func machoCodeSign(ctxt *Link, fname string) error { + f, err := os.OpenFile(fname, os.O_RDWR, 0) + if err != nil { + return err + } + defer f.Close() + + mf, err := macho.NewFile(f) + if err != nil { + return err + } + if mf.Magic != macho.Magic64 { + Exitf("not 64-bit Mach-O file: %s", fname) + } + + // Find existing LC_CODE_SIGNATURE and __LINKEDIT segment + var sigOff, sigSz, csCmdOff, linkeditOff int64 + var linkeditSeg, textSeg *macho.Segment + loadOff := int64(machoHeaderSize64) + get32 := mf.ByteOrder.Uint32 + for _, l := range mf.Loads { + data := l.Raw() + cmd, sz := get32(data), get32(data[4:]) + if cmd == LC_CODE_SIGNATURE { + sigOff = int64(get32(data[8:])) + sigSz = int64(get32(data[12:])) + csCmdOff = loadOff + } + if seg, ok := l.(*macho.Segment); ok { + switch seg.Name { + case "__LINKEDIT": + linkeditSeg = seg + linkeditOff = loadOff + case "__TEXT": + textSeg = seg + } + } + loadOff += int64(sz) + } + + if sigOff == 0 { + // The C linker doesn't generate a signed binary, for some reason. + // Skip. + return nil + } + + fi, err := f.Stat() + if err != nil { + return err + } + if sigOff+sigSz != fi.Size() { + // We don't expect anything after the signature (this will invalidate + // the signature anyway.) + return fmt.Errorf("unexpected content after code signature") + } + + sz := codesign.Size(sigOff, "a.out") + if sz != sigSz { + // Update the load command, + var tmp [8]byte + mf.ByteOrder.PutUint32(tmp[:4], uint32(sz)) + _, err = f.WriteAt(tmp[:4], csCmdOff+12) + if err != nil { + return err + } + + // Uodate the __LINKEDIT segment. + segSz := sigOff + sz - int64(linkeditSeg.Offset) + mf.ByteOrder.PutUint64(tmp[:8], uint64(segSz)) + _, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Memsz))) + if err != nil { + return err + } + _, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Filesz))) + if err != nil { + return err + } + } + + cs := make([]byte, sz) + codesign.Sign(cs, f, "a.out", sigOff, int64(textSeg.Offset), int64(textSeg.Filesz), ctxt.IsExe() || ctxt.IsPIE()) + _, err = f.WriteAt(cs, sigOff) + if err != nil { + return err + } + err = f.Truncate(sigOff + sz) + return err +} diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go new file mode 100644 index 0000000..77ee8a4 --- /dev/null +++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go @@ -0,0 +1,430 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "compress/zlib" + "debug/macho" + "encoding/binary" + "fmt" + "io" + "os" + "reflect" + "unsafe" +) + +type loadCmd struct { + Cmd macho.LoadCmd + Len uint32 +} + +type dyldInfoCmd struct { + Cmd macho.LoadCmd + Len uint32 + RebaseOff, RebaseLen uint32 + BindOff, BindLen uint32 + WeakBindOff, WeakBindLen uint32 + LazyBindOff, LazyBindLen uint32 + ExportOff, ExportLen uint32 +} + +type linkEditDataCmd struct { + Cmd macho.LoadCmd + Len uint32 + DataOff, DataLen uint32 +} + +type encryptionInfoCmd struct { + Cmd macho.LoadCmd + Len uint32 + CryptOff, CryptLen uint32 + CryptId uint32 +} + +type loadCmdReader struct { + offset, next int64 + f *os.File + order binary.ByteOrder +} + +func (r *loadCmdReader) Next() (loadCmd, error) { + var cmd loadCmd + + r.offset = r.next + if _, err := r.f.Seek(r.offset, 0); err != nil { + return cmd, err + } + if err := binary.Read(r.f, r.order, &cmd); err != nil { + return cmd, err + } + r.next = r.offset + int64(cmd.Len) + return cmd, nil +} + +func (r loadCmdReader) ReadAt(offset int64, data interface{}) error { + if _, err := r.f.Seek(r.offset+offset, 0); err != nil { + return err + } + return binary.Read(r.f, r.order, data) +} + +func (r loadCmdReader) WriteAt(offset int64, data interface{}) error { + if _, err := r.f.Seek(r.offset+offset, 0); err != nil { + return err + } + return binary.Write(r.f, r.order, data) +} + +// machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable. +// +// With internal linking, DWARF is embedded into the executable, this lets us do the +// same for external linking. +// exef is the file of the executable with no DWARF. It must have enough room in the macho +// header to add the DWARF sections. (Use ld's -headerpad option) +// exem is the macho representation of exef. +// dsym is the path to the macho file containing DWARF from dsymutil. +// outexe is the path where the combined executable should be saved. +func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe string) error { + dwarff, err := os.Open(dsym) + if err != nil { + return err + } + defer dwarff.Close() + outf, err := os.OpenFile(outexe, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + return err + } + defer outf.Close() + dwarfm, err := macho.NewFile(dwarff) + if err != nil { + return err + } + defer dwarfm.Close() + + // The string table needs to be the last thing in the file + // for code signing to work. So we'll need to move the + // linkedit section, but all the others can be copied directly. + linkseg := exem.Segment("__LINKEDIT") + if linkseg == nil { + return fmt.Errorf("missing __LINKEDIT segment") + } + + if _, err := exef.Seek(0, 0); err != nil { + return err + } + if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil { + return err + } + + realdwarf := dwarfm.Segment("__DWARF") + if realdwarf == nil { + return fmt.Errorf("missing __DWARF segment") + } + + // Try to compress the DWARF sections. This includes some Apple + // proprietary sections like __apple_types. + compressedSects, compressedBytes, err := machoCompressSections(ctxt, dwarfm) + if err != nil { + return err + } + + // Now copy the dwarf data into the output. + // Kernel requires all loaded segments to be page-aligned in the file, + // even though we mark this one as being 0 bytes of virtual address space. + dwarfstart := Rnd(int64(linkseg.Offset), int64(*FlagRound)) + if _, err := outf.Seek(dwarfstart, 0); err != nil { + return err + } + + if _, err := dwarff.Seek(int64(realdwarf.Offset), 0); err != nil { + return err + } + + // Write out the compressed sections, or the originals if we gave up + // on compressing them. + var dwarfsize uint64 + if compressedBytes != nil { + dwarfsize = uint64(len(compressedBytes)) + if _, err := outf.Write(compressedBytes); err != nil { + return err + } + } else { + if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil { + return err + } + dwarfsize = realdwarf.Filesz + } + + // And finally the linkedit section. + if _, err := exef.Seek(int64(linkseg.Offset), 0); err != nil { + return err + } + linkstart := Rnd(dwarfstart+int64(dwarfsize), int64(*FlagRound)) + if _, err := outf.Seek(linkstart, 0); err != nil { + return err + } + if _, err := io.Copy(outf, exef); err != nil { + return err + } + + // Now we need to update the headers. + textsect := exem.Section("__text") + if textsect == nil { + return fmt.Errorf("missing __text section") + } + + cmdOffset := unsafe.Sizeof(exem.FileHeader) + if is64bit := exem.Magic == macho.Magic64; is64bit { + // mach_header_64 has one extra uint32. + cmdOffset += unsafe.Sizeof(exem.Magic) + } + dwarfCmdOffset := uint32(cmdOffset) + exem.FileHeader.Cmdsz + availablePadding := textsect.Offset - dwarfCmdOffset + if availablePadding < realdwarf.Len { + return fmt.Errorf("no room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding) + } + // First, copy the dwarf load command into the header. It will be + // updated later with new offsets and lengths as necessary. + if _, err := outf.Seek(int64(dwarfCmdOffset), 0); err != nil { + return err + } + if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil { + return err + } + if _, err := outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil { + return err + } + if err := binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil { + return err + } + if err := binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil { + return err + } + + reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder} + for i := uint32(0); i < exem.Ncmd; i++ { + cmd, err := reader.Next() + if err != nil { + return err + } + linkoffset := uint64(linkstart) - linkseg.Offset + switch cmd.Cmd { + case macho.LoadCmdSegment64: + err = machoUpdateSegment(reader, linkseg, linkoffset) + case macho.LoadCmdSegment: + panic("unexpected 32-bit segment") + case LC_DYLD_INFO, LC_DYLD_INFO_ONLY: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff") + case macho.LoadCmdSymtab: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.SymtabCmd{}, "Symoff", "Stroff") + case macho.LoadCmdDysymtab: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff") + case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &linkEditDataCmd{}, "DataOff") + case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64: + err = machoUpdateLoadCommand(reader, linkseg, linkoffset, &encryptionInfoCmd{}, "CryptOff") + case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH, LC_ID_DYLIB, LC_SYMSEG, LC_LOADFVMLIB, LC_IDFVMLIB, LC_IDENT, LC_FVMFILE, LC_PREPAGE, LC_ID_DYLINKER, LC_ROUTINES, LC_SUB_FRAMEWORK, LC_SUB_UMBRELLA, LC_SUB_CLIENT, LC_SUB_LIBRARY, LC_TWOLEVEL_HINTS, LC_PREBIND_CKSUM, LC_ROUTINES_64, LC_LAZY_LOAD_DYLIB, LC_LOAD_UPWARD_DYLIB, LC_DYLD_ENVIRONMENT, LC_LINKER_OPTION, LC_LINKER_OPTIMIZATION_HINT, LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS, LC_VERSION_NOTE, LC_BUILD_VERSION: + // Nothing to update + default: + err = fmt.Errorf("unknown load command 0x%x (%s)", int(cmd.Cmd), cmd.Cmd) + } + if err != nil { + return err + } + } + // Do the final update of the DWARF segment's load command. + return machoUpdateDwarfHeader(&reader, compressedSects, dwarfsize, dwarfstart, realdwarf) +} + +// machoCompressSections tries to compress the DWARF segments in dwarfm, +// returning the updated sections and segment contents, nils if the sections +// weren't compressed, or an error if there was a problem reading dwarfm. +func machoCompressSections(ctxt *Link, dwarfm *macho.File) ([]*macho.Section, []byte, error) { + if !ctxt.compressDWARF { + return nil, nil, nil + } + + dwarfseg := dwarfm.Segment("__DWARF") + var sects []*macho.Section + var buf bytes.Buffer + + for _, sect := range dwarfm.Sections { + if sect.Seg != "__DWARF" { + continue + } + + // As of writing, there are no relocations in dsymutil's output + // so there's no point in worrying about them. Bail out if that + // changes. + if sect.Nreloc != 0 { + return nil, nil, nil + } + + data, err := sect.Data() + if err != nil { + return nil, nil, err + } + + compressed, contents, err := machoCompressSection(data) + if err != nil { + return nil, nil, err + } + + newSec := *sect + newSec.Offset = uint32(dwarfseg.Offset) + uint32(buf.Len()) + newSec.Addr = dwarfseg.Addr + uint64(buf.Len()) + if compressed { + newSec.Name = "__z" + sect.Name[2:] + newSec.Size = uint64(len(contents)) + } + sects = append(sects, &newSec) + buf.Write(contents) + } + return sects, buf.Bytes(), nil +} + +// machoCompressSection compresses secBytes if it results in less data. +func machoCompressSection(sectBytes []byte) (compressed bool, contents []byte, err error) { + var buf bytes.Buffer + buf.WriteString("ZLIB") + var sizeBytes [8]byte + binary.BigEndian.PutUint64(sizeBytes[:], uint64(len(sectBytes))) + buf.Write(sizeBytes[:]) + + z := zlib.NewWriter(&buf) + if _, err := z.Write(sectBytes); err != nil { + return false, nil, err + } + if err := z.Close(); err != nil { + return false, nil, err + } + if buf.Len() >= len(sectBytes) { + return false, sectBytes, nil + } + return true, buf.Bytes(), nil +} + +// machoUpdateSegment updates the load command for a moved segment. +// Only the linkedit segment should move, and it should have 0 sections. +func machoUpdateSegment(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64) error { + var seg macho.Segment64 + if err := r.ReadAt(0, &seg); err != nil { + return err + } + + // Only the linkedit segment moved, anything before that is fine. + if seg.Offset < linkseg.Offset { + return nil + } + seg.Offset += linkoffset + if err := r.WriteAt(0, &seg); err != nil { + return err + } + // There shouldn't be any sections, but just to make sure... + return machoUpdateSections(r, &seg, linkoffset, nil) +} + +func machoUpdateSections(r loadCmdReader, seg *macho.Segment64, deltaOffset uint64, compressedSects []*macho.Section) error { + nsect := seg.Nsect + if nsect == 0 { + return nil + } + sectOffset := int64(unsafe.Sizeof(*seg)) + + var sect macho.Section64 + sectSize := int64(unsafe.Sizeof(sect)) + for i := uint32(0); i < nsect; i++ { + if err := r.ReadAt(sectOffset, §); err != nil { + return err + } + if compressedSects != nil { + cSect := compressedSects[i] + copy(sect.Name[:], cSect.Name) + sect.Size = cSect.Size + if cSect.Offset != 0 { + sect.Offset = cSect.Offset + uint32(deltaOffset) + } + if cSect.Addr != 0 { + sect.Addr = cSect.Addr + } + } else { + if sect.Offset != 0 { + sect.Offset += uint32(deltaOffset) + } + if sect.Reloff != 0 { + sect.Reloff += uint32(deltaOffset) + } + } + if err := r.WriteAt(sectOffset, §); err != nil { + return err + } + sectOffset += sectSize + } + return nil +} + +// machoUpdateDwarfHeader updates the DWARF segment load command. +func machoUpdateDwarfHeader(r *loadCmdReader, compressedSects []*macho.Section, dwarfsize uint64, dwarfstart int64, realdwarf *macho.Segment) error { + cmd, err := r.Next() + if err != nil { + return err + } + if cmd.Cmd != macho.LoadCmdSegment64 { + panic("not a Segment64") + } + var seg macho.Segment64 + if err := r.ReadAt(0, &seg); err != nil { + return err + } + seg.Offset = uint64(dwarfstart) + + if compressedSects != nil { + var segSize uint64 + for _, newSect := range compressedSects { + segSize += newSect.Size + } + seg.Filesz = segSize + } else { + seg.Filesz = dwarfsize + } + + // We want the DWARF segment to be considered non-loadable, so + // force vmaddr and vmsize to zero. In addition, set the initial + // protection to zero so as to make the dynamic loader happy, + // since otherwise it may complain that that the vm size and file + // size don't match for the segment. See issues 21647 and 32673 + // for more context. Also useful to refer to the Apple dynamic + // loader source, specifically ImageLoaderMachO::sniffLoadCommands + // in ImageLoaderMachO.cpp (various versions can be found online, see + // https://opensource.apple.com/source/dyld/dyld-519.2.2/src/ImageLoaderMachO.cpp.auto.html + // as one example). + seg.Addr = 0 + seg.Memsz = 0 + seg.Prot = 0 + + if err := r.WriteAt(0, &seg); err != nil { + return err + } + return machoUpdateSections(*r, &seg, uint64(dwarfstart)-realdwarf.Offset, compressedSects) +} + +func machoUpdateLoadCommand(r loadCmdReader, linkseg *macho.Segment, linkoffset uint64, cmd interface{}, fields ...string) error { + if err := r.ReadAt(0, cmd); err != nil { + return err + } + value := reflect.Indirect(reflect.ValueOf(cmd)) + + for _, name := range fields { + field := value.FieldByName(name) + if fieldval := field.Uint(); fieldval >= linkseg.Offset { + field.SetUint(fieldval + linkoffset) + } + } + if err := r.WriteAt(0, cmd); err != nil { + return err + } + return nil +} diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go new file mode 100644 index 0000000..5a096f1 --- /dev/null +++ b/src/cmd/link/internal/ld/main.go @@ -0,0 +1,417 @@ +// Inferno utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bufio" + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/benchmark" + "flag" + "log" + "os" + "runtime" + "runtime/pprof" + "strings" +) + +var ( + pkglistfornote []byte + windowsgui bool // writes a "GUI binary" instead of a "console binary" + ownTmpDir bool // set to true if tmp dir created by linker (e.g. no -tmpdir) +) + +func init() { + flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...") +} + +// Flags used by the linker. The exported flags are used by the architecture-specific packages. +var ( + flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id") + + flagOutfile = flag.String("o", "", "write output to `file`") + flagPluginPath = flag.String("pluginpath", "", "full path name for plugin") + + flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`") + flagDumpDep = flag.Bool("dumpdep", false, "dump symbol dependency graph") + flagRace = flag.Bool("race", false, "enable race detector") + flagMsan = flag.Bool("msan", false, "enable MSan interface") + flagAslr = flag.Bool("aslr", true, "enable ASLR for buildmode=c-shared on windows") + + flagFieldTrack = flag.String("k", "", "set field tracking `symbol`") + flagLibGCC = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable") + flagTmpdir = flag.String("tmpdir", "", "use `directory` for temporary files") + + flagExtld = flag.String("extld", "", "use `linker` when linking in external mode") + flagExtldflags = flag.String("extldflags", "", "pass `flags` to external linker") + flagExtar = flag.String("extar", "", "archive program for buildmode=c-archive") + + flagA = flag.Bool("a", false, "no-op (deprecated)") + FlagC = flag.Bool("c", false, "dump call graph") + FlagD = flag.Bool("d", false, "disable dynamic executable") + flagF = flag.Bool("f", false, "ignore version mismatch") + flagG = flag.Bool("g", false, "disable go package data checks") + flagH = flag.Bool("h", false, "halt on error") + flagN = flag.Bool("n", false, "dump symbol table") + FlagS = flag.Bool("s", false, "disable symbol table") + FlagW = flag.Bool("w", false, "disable DWARF generation") + flag8 bool // use 64-bit addresses in symbol table + flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker") + FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines") + FlagDebugTextSize = flag.Int("debugppc64textsize", 0, "debug PPC64 text section max") + FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).") + FlagRound = flag.Int("R", -1, "set address rounding `quantum`") + FlagTextAddr = flag.Int64("T", -1, "set text segment `address`") + flagEntrySymbol = flag.String("E", "", "set `entry` symbol name") + + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") + memprofile = flag.String("memprofile", "", "write memory profile to `file`") + memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`") + + benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking") + benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof") +) + +// Main is the main entry point for the linker code. +func Main(arch *sys.Arch, theArch Arch) { + thearch = theArch + ctxt := linknew(arch) + ctxt.Bso = bufio.NewWriter(os.Stdout) + + // For testing behavior of go command when tools crash silently. + // Undocumented, not in standard flag parser to avoid + // exposing in usage message. + for _, arg := range os.Args { + if arg == "-crash_for_testing" { + os.Exit(2) + } + } + + final := gorootFinal() + addstrdata1(ctxt, "runtime/internal/sys.DefaultGoroot="+final) + addstrdata1(ctxt, "cmd/internal/objabi.defaultGOROOT="+final) + + // TODO(matloob): define these above and then check flag values here + if ctxt.Arch.Family == sys.AMD64 && objabi.GOOS == "plan9" { + flag.BoolVar(&flag8, "8", false, "use 64-bit addresses in symbol table") + } + flagHeadType := flag.String("H", "", "set header `type`") + flag.BoolVar(&ctxt.linkShared, "linkshared", false, "link against installed Go shared libraries") + flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`") + flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`") + flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible") + objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) + objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) }) + objabi.AddVersionFlag() // -V + objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) }) + objabi.Flagcount("v", "print link trace", &ctxt.Debugvlog) + objabi.Flagfn1("importcfg", "read import configuration from `file`", ctxt.readImportCfg) + + objabi.Flagparse(usage) + + if ctxt.Debugvlog > 0 { + // dump symbol info on crash + defer func() { ctxt.loader.Dump() }() + } + + switch *flagHeadType { + case "": + case "windowsgui": + ctxt.HeadType = objabi.Hwindows + windowsgui = true + default: + if err := ctxt.HeadType.Set(*flagHeadType); err != nil { + Errorf(nil, "%v", err) + usage() + } + } + if ctxt.HeadType == objabi.Hunknown { + ctxt.HeadType.Set(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.Set("exe") + } + + if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 { + usage() + } + + if *flagOutfile == "" { + *flagOutfile = "a.out" + if ctxt.HeadType == objabi.Hwindows { + *flagOutfile += ".exe" + } + } + + interpreter = *flagInterpreter + + if *flagBuildid == "" && ctxt.Target.IsOpenbsd() { + // TODO(jsing): Remove once direct syscalls are no longer in use. + // OpenBSD 6.7 onwards will not permit direct syscalls from a + // dynamically linked binary unless it identifies the binary + // contains a .note.go.buildid ELF note. See issue #36435. + *flagBuildid = "go-openbsd" + } + + // enable benchmarking + var bench *benchmark.Metrics + if len(*benchmarkFlag) != 0 { + if *benchmarkFlag == "mem" { + bench = benchmark.New(benchmark.GC, *benchmarkFileFlag) + } else if *benchmarkFlag == "cpu" { + bench = benchmark.New(benchmark.NoGC, *benchmarkFileFlag) + } else { + Errorf(nil, "unknown benchmark flag: %q", *benchmarkFlag) + usage() + } + } + + bench.Start("libinit") + libinit(ctxt) // creates outfile + bench.Start("computeTLSOffset") + ctxt.computeTLSOffset() + bench.Start("Archinit") + thearch.Archinit(ctxt) + + if ctxt.linkShared && !ctxt.IsELF { + Exitf("-linkshared can only be used on elf systems") + } + + if ctxt.Debugvlog != 0 { + ctxt.Logf("HEADER = -H%d -T0x%x -R0x%x\n", ctxt.HeadType, uint64(*FlagTextAddr), uint32(*FlagRound)) + } + + zerofp := goobj.FingerprintType{} + switch ctxt.BuildMode { + case BuildModeShared: + for i := 0; i < flag.NArg(); i++ { + arg := flag.Arg(i) + parts := strings.SplitN(arg, "=", 2) + var pkgpath, file string + if len(parts) == 1 { + pkgpath, file = "main", arg + } else { + pkgpath, file = parts[0], parts[1] + } + pkglistfornote = append(pkglistfornote, pkgpath...) + pkglistfornote = append(pkglistfornote, '\n') + addlibpath(ctxt, "command line", "command line", file, pkgpath, "", zerofp) + } + case BuildModePlugin: + addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "", zerofp) + default: + addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "", zerofp) + } + bench.Start("loadlib") + ctxt.loadlib() + + bench.Start("deadcode") + deadcode(ctxt) + + bench.Start("linksetup") + ctxt.linksetup() + + bench.Start("dostrdata") + ctxt.dostrdata() + if objabi.Fieldtrack_enabled != 0 { + bench.Start("fieldtrack") + fieldtrack(ctxt.Arch, ctxt.loader) + } + + bench.Start("dwarfGenerateDebugInfo") + dwarfGenerateDebugInfo(ctxt) + + bench.Start("callgraph") + ctxt.callgraph() + + bench.Start("dostkcheck") + ctxt.dostkcheck() + + bench.Start("mangleTypeSym") + ctxt.mangleTypeSym() + + if ctxt.IsELF { + bench.Start("doelf") + ctxt.doelf() + } + if ctxt.IsDarwin() { + bench.Start("domacho") + ctxt.domacho() + } + if ctxt.IsWindows() { + bench.Start("dope") + ctxt.dope() + bench.Start("windynrelocsyms") + ctxt.windynrelocsyms() + } + if ctxt.IsAIX() { + bench.Start("doxcoff") + ctxt.doxcoff() + } + + bench.Start("textbuildid") + ctxt.textbuildid() + bench.Start("addexport") + setupdynexp(ctxt) + ctxt.setArchSyms() + ctxt.addexport() + bench.Start("Gentext") + thearch.Gentext(ctxt, ctxt.loader) // trampolines, call stubs, etc. + + bench.Start("textaddress") + ctxt.textaddress() + bench.Start("typelink") + ctxt.typelink() + bench.Start("buildinfo") + ctxt.buildinfo() + bench.Start("pclntab") + containers := ctxt.findContainerSyms() + pclnState := ctxt.pclntab(containers) + bench.Start("findfunctab") + ctxt.findfunctab(pclnState, containers) + bench.Start("dwarfGenerateDebugSyms") + dwarfGenerateDebugSyms(ctxt) + bench.Start("symtab") + symGroupType := ctxt.symtab(pclnState) + bench.Start("dodata") + ctxt.dodata(symGroupType) + bench.Start("address") + order := ctxt.address() + bench.Start("dwarfcompress") + dwarfcompress(ctxt) + bench.Start("layout") + filesize := ctxt.layout(order) + + // Write out the output file. + // It is split into two parts (Asmb and Asmb2). The first + // part writes most of the content (sections and segments), + // for which we have computed the size and offset, in a + // mmap'd region. The second part writes more content, for + // which we don't know the size. + if ctxt.Arch.Family != sys.Wasm { + // Don't mmap if we're building for Wasm. Wasm file + // layout is very different so filesize is meaningless. + if err := ctxt.Out.Mmap(filesize); err != nil { + panic(err) + } + } + // asmb will redirect symbols to the output file mmap, and relocations + // will be applied directly there. + bench.Start("Asmb") + asmb(ctxt) + + exitIfErrors() + + // Generate additional symbols for the native symbol table just prior + // to code generation. + bench.Start("GenSymsLate") + if thearch.GenSymsLate != nil { + thearch.GenSymsLate(ctxt, ctxt.loader) + } + + bench.Start("Asmb2") + asmb2(ctxt) + + bench.Start("Munmap") + ctxt.Out.Close() // Close handles Munmapping if necessary. + + bench.Start("hostlink") + ctxt.hostlink() + if ctxt.Debugvlog != 0 { + ctxt.Logf("%s", ctxt.loader.Stat()) + ctxt.Logf("%d liveness data\n", liveness) + } + bench.Start("Flush") + ctxt.Bso.Flush() + bench.Start("archive") + ctxt.archive() + bench.Report(os.Stdout) + + errorexit() +} + +type Rpath struct { + set bool + val string +} + +func (r *Rpath) Set(val string) error { + r.set = true + r.val = val + return nil +} + +func (r *Rpath) String() string { + return r.val +} + +func startProfile() { + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatalf("%v", err) + } + if err := pprof.StartCPUProfile(f); err != nil { + log.Fatalf("%v", err) + } + AtExit(pprof.StopCPUProfile) + } + if *memprofile != "" { + if *memprofilerate != 0 { + runtime.MemProfileRate = int(*memprofilerate) + } + f, err := os.Create(*memprofile) + if err != nil { + log.Fatalf("%v", err) + } + AtExit(func() { + // Profile all outstanding allocations. + runtime.GC() + // compilebench parses the memory profile to extract memstats, + // which are only written in the legacy pprof format. + // See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap. + const writeLegacyFormat = 1 + if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil { + log.Fatalf("%v", err) + } + }) + } +} diff --git a/src/cmd/link/internal/ld/nooptcgolink_test.go b/src/cmd/link/internal/ld/nooptcgolink_test.go new file mode 100644 index 0000000..4d2ff1a --- /dev/null +++ b/src/cmd/link/internal/ld/nooptcgolink_test.go @@ -0,0 +1,37 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "testing" +) + +func TestNooptCgoBuild(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + t.Parallel() + + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out")) + cmd.Dir = filepath.Join(runtime.GOROOT(), "src", "runtime", "testdata", "testprogcgo") + out, err := cmd.CombinedOutput() + if err != nil { + t.Logf("go build output: %s", out) + t.Fatal(err) + } +} diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go new file mode 100644 index 0000000..530836e --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf.go @@ -0,0 +1,311 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/sys" + "cmd/link/internal/loader" + "encoding/binary" + "errors" + "log" + "os" +) + +// If fallocate is not supported on this platform, return this error. The error +// is ignored where needed, and OutBuf writes to heap memory. +var errNoFallocate = errors.New("operation not supported") + +const outbufMode = 0775 + +// OutBuf is a buffered file writer. +// +// It is simlar to the Writer in cmd/internal/bio with a few small differences. +// +// First, it tracks the output architecture and uses it to provide +// endian helpers. +// +// Second, it provides a very cheap offset counter that doesn't require +// any system calls to read the value. +// +// Third, it also mmaps the output file (if available). The intended usage is: +// - Mmap the output file +// - Write the content +// - possibly apply any edits in the output buffer +// - possibly write more content to the file. These writes take place in a heap +// backed buffer that will get synced to disk. +// - Munmap the output file +// +// And finally, it provides a mechanism by which you can multithread the +// writing of output files. This mechanism is accomplished by copying a OutBuf, +// and using it in the thread/goroutine. +// +// Parallel OutBuf is intended to be used like: +// +// func write(out *OutBuf) { +// var wg sync.WaitGroup +// for i := 0; i < 10; i++ { +// wg.Add(1) +// view, err := out.View(start[i]) +// if err != nil { +// // handle output +// continue +// } +// go func(out *OutBuf, i int) { +// // do output +// wg.Done() +// }(view, i) +// } +// wg.Wait() +// } +type OutBuf struct { + arch *sys.Arch + off int64 + + buf []byte // backing store of mmap'd output file + heap []byte // backing store for non-mmapped data + + name string + f *os.File + encbuf [8]byte // temp buffer used by WriteN methods + isView bool // true if created from View() +} + +func (out *OutBuf) Open(name string) error { + if out.f != nil { + return errors.New("cannot open more than one file") + } + f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, outbufMode) + if err != nil { + return err + } + out.off = 0 + out.name = name + out.f = f + return nil +} + +func NewOutBuf(arch *sys.Arch) *OutBuf { + return &OutBuf{ + arch: arch, + } +} + +var viewError = errors.New("output not mmapped") + +func (out *OutBuf) View(start uint64) (*OutBuf, error) { + return &OutBuf{ + arch: out.arch, + name: out.name, + buf: out.buf, + heap: out.heap, + off: int64(start), + isView: true, + }, nil +} + +var viewCloseError = errors.New("cannot Close OutBuf from View") + +func (out *OutBuf) Close() error { + if out.isView { + return viewCloseError + } + if out.isMmapped() { + out.copyHeap() + out.purgeSignatureCache() + out.munmap() + } + if out.f == nil { + return nil + } + if len(out.heap) != 0 { + if _, err := out.f.Write(out.heap); err != nil { + return err + } + } + if err := out.f.Close(); err != nil { + return err + } + out.f = nil + return nil +} + +// isMmapped returns true if the OutBuf is mmaped. +func (out *OutBuf) isMmapped() bool { + return len(out.buf) != 0 +} + +// Data returns the whole written OutBuf as a byte slice. +func (out *OutBuf) Data() []byte { + if out.isMmapped() { + out.copyHeap() + return out.buf + } + return out.heap +} + +// copyHeap copies the heap to the mmapped section of memory, returning true if +// a copy takes place. +func (out *OutBuf) copyHeap() bool { + if !out.isMmapped() { // only valuable for mmapped OutBufs. + return false + } + if out.isView { + panic("can't copyHeap a view") + } + + bufLen := len(out.buf) + heapLen := len(out.heap) + total := uint64(bufLen + heapLen) + if heapLen != 0 { + if err := out.Mmap(total); err != nil { // Mmap will copy out.heap over to out.buf + panic(err) + } + } + return true +} + +// maxOutBufHeapLen limits the growth of the heap area. +const maxOutBufHeapLen = 10 << 20 + +// writeLoc determines the write location if a buffer is mmaped. +// We maintain two write buffers, an mmapped section, and a heap section for +// writing. When the mmapped section is full, we switch over the heap memory +// for writing. +func (out *OutBuf) writeLoc(lenToWrite int64) (int64, []byte) { + // See if we have enough space in the mmaped area. + bufLen := int64(len(out.buf)) + if out.off+lenToWrite <= bufLen { + return out.off, out.buf + } + + // Not enough space in the mmaped area, write to heap area instead. + heapPos := out.off - bufLen + heapLen := int64(len(out.heap)) + lenNeeded := heapPos + lenToWrite + if lenNeeded > heapLen { // do we need to grow the heap storage? + // The heap variables aren't protected by a mutex. For now, just bomb if you + // try to use OutBuf in parallel. (Note this probably could be fixed.) + if out.isView { + panic("cannot write to heap in parallel") + } + // See if our heap would grow to be too large, and if so, copy it to the end + // of the mmapped area. + if heapLen > maxOutBufHeapLen && out.copyHeap() { + heapPos -= heapLen + lenNeeded = heapPos + lenToWrite + heapLen = 0 + } + out.heap = append(out.heap, make([]byte, lenNeeded-heapLen)...) + } + return heapPos, out.heap +} + +func (out *OutBuf) SeekSet(p int64) { + out.off = p +} + +func (out *OutBuf) Offset() int64 { + return out.off +} + +// Write writes the contents of v to the buffer. +func (out *OutBuf) Write(v []byte) (int, error) { + n := len(v) + pos, buf := out.writeLoc(int64(n)) + copy(buf[pos:], v) + out.off += int64(n) + return n, nil +} + +func (out *OutBuf) Write8(v uint8) { + pos, buf := out.writeLoc(1) + buf[pos] = v + out.off++ +} + +// WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface. +func (out *OutBuf) WriteByte(v byte) error { + out.Write8(v) + return nil +} + +func (out *OutBuf) Write16(v uint16) { + out.arch.ByteOrder.PutUint16(out.encbuf[:], v) + out.Write(out.encbuf[:2]) +} + +func (out *OutBuf) Write32(v uint32) { + out.arch.ByteOrder.PutUint32(out.encbuf[:], v) + out.Write(out.encbuf[:4]) +} + +func (out *OutBuf) Write32b(v uint32) { + binary.BigEndian.PutUint32(out.encbuf[:], v) + out.Write(out.encbuf[:4]) +} + +func (out *OutBuf) Write64(v uint64) { + out.arch.ByteOrder.PutUint64(out.encbuf[:], v) + out.Write(out.encbuf[:8]) +} + +func (out *OutBuf) Write64b(v uint64) { + binary.BigEndian.PutUint64(out.encbuf[:], v) + out.Write(out.encbuf[:8]) +} + +func (out *OutBuf) WriteString(s string) { + pos, buf := out.writeLoc(int64(len(s))) + n := copy(buf[pos:], s) + if n != len(s) { + log.Fatalf("WriteString truncated. buffer size: %d, offset: %d, len(s)=%d", len(out.buf), out.off, len(s)) + } + out.off += int64(n) +} + +// WriteStringN writes the first n bytes of s. +// If n is larger than len(s) then it is padded with zero bytes. +func (out *OutBuf) WriteStringN(s string, n int) { + out.WriteStringPad(s, n, zeros[:]) +} + +// WriteStringPad writes the first n bytes of s. +// If n is larger than len(s) then it is padded with the bytes in pad (repeated as needed). +func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) { + if len(s) >= n { + out.WriteString(s[:n]) + } else { + out.WriteString(s) + n -= len(s) + for n > len(pad) { + out.Write(pad) + n -= len(pad) + + } + out.Write(pad[:n]) + } +} + +// WriteSym writes the content of a Symbol, and returns the output buffer +// that we just wrote, so we can apply further edit to the symbol content. +// For generator symbols, it also sets the symbol's Data to the output +// buffer. +func (out *OutBuf) WriteSym(ldr *loader.Loader, s loader.Sym) []byte { + if !ldr.IsGeneratedSym(s) { + P := ldr.Data(s) + n := int64(len(P)) + pos, buf := out.writeLoc(n) + copy(buf[pos:], P) + out.off += n + ldr.FreeData(s) + return buf[pos : pos+n] + } else { + n := ldr.SymSize(s) + pos, buf := out.writeLoc(n) + out.off += n + ldr.MakeSymbolUpdater(s).SetData(buf[pos : pos+n]) + return buf[pos : pos+n] + } +} diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go new file mode 100644 index 0000000..9444b65 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_darwin.go @@ -0,0 +1,47 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "syscall" + "unsafe" +) + +func (out *OutBuf) fallocate(size uint64) error { + stat, err := out.f.Stat() + if err != nil { + return err + } + // F_PEOFPOSMODE allocates from the end of the file, so we want the size difference. + // Apparently, it uses the end of the allocation, instead of the logical end of the + // the file. + cursize := uint64(stat.Sys().(*syscall.Stat_t).Blocks * 512) // allocated size + if size <= cursize { + return nil + } + + store := &syscall.Fstore_t{ + Flags: syscall.F_ALLOCATEALL, + Posmode: syscall.F_PEOFPOSMODE, + Offset: 0, + Length: int64(size - cursize), + } + + _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store))) + if errno != 0 { + return errno + } + + 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_linux.go b/src/cmd/link/internal/ld/outbuf_linux.go new file mode 100644 index 0000000..bd9a0c6 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_linux.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import "syscall" + +func (out *OutBuf) fallocate(size uint64) error { + return syscall.Fallocate(int(out.f.Fd()), 0, 0, int64(size)) +} diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go new file mode 100644 index 0000000..807fe24 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_mmap.go @@ -0,0 +1,59 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build aix darwin dragonfly freebsd linux netbsd openbsd + +package ld + +import ( + "syscall" +) + +// Mmap maps the output file with the given size. It unmaps the old mapping +// if it is already mapped. It also flushes any in-heap data to the new +// mapping. +func (out *OutBuf) Mmap(filesize uint64) (err error) { + oldlen := len(out.buf) + if oldlen != 0 { + out.munmap() + } + + for { + if err = out.fallocate(filesize); err != syscall.EINTR { + break + } + } + if err != nil { + // Some file systems do not support fallocate. We ignore that error as linking + // can still take place, but you might SIGBUS when you write to the mmapped + // area. + if err != syscall.ENOTSUP && err != syscall.EPERM && err != errNoFallocate { + return err + } + } + err = out.f.Truncate(int64(filesize)) + if err != nil { + Exitf("resize output file failed: %v", err) + } + out.buf, err = syscall.Mmap(int(out.f.Fd()), 0, int(filesize), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FILE) + if err != nil { + return err + } + + // copy heap to new mapping + if uint64(oldlen+len(out.heap)) > filesize { + panic("mmap size too small") + } + copy(out.buf[oldlen:], out.heap) + out.heap = out.heap[:0] + return nil +} + +func (out *OutBuf) munmap() { + if out.buf == nil { + return + } + syscall.Munmap(out.buf) + out.buf = nil +} diff --git a/src/cmd/link/internal/ld/outbuf_nofallocate.go b/src/cmd/link/internal/ld/outbuf_nofallocate.go new file mode 100644 index 0000000..6bf96bc --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_nofallocate.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!linux + +package ld + +func (out *OutBuf) fallocate(size uint64) error { + return errNoFallocate +} diff --git a/src/cmd/link/internal/ld/outbuf_nommap.go b/src/cmd/link/internal/ld/outbuf_nommap.go new file mode 100644 index 0000000..6b40253 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_nommap.go @@ -0,0 +1,22 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!windows + +package ld + +// Mmap allocates an in-heap output buffer with the given size. It copies +// any old data (if any) to the new buffer. +func (out *OutBuf) Mmap(filesize uint64) error { + // We need space to put all the symbols before we apply relocations. + oldheap := out.heap + if filesize < uint64(len(oldheap)) { + panic("mmap size too small") + } + out.heap = make([]byte, filesize) + copy(out.heap, oldheap) + return nil +} + +func (out *OutBuf) munmap() { panic("unreachable") } diff --git a/src/cmd/link/internal/ld/outbuf_notdarwin.go b/src/cmd/link/internal/ld/outbuf_notdarwin.go new file mode 100644 index 0000000..8c5666f --- /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 new file mode 100644 index 0000000..e6643da --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_test.go @@ -0,0 +1,99 @@ +// 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 ( + "io/ioutil" + "os" + "path/filepath" + "runtime" + "testing" +) + +// TestMMap ensures that we can actually mmap on every supported platform. +func TestMMap(t *testing.T) { + switch runtime.GOOS { + default: + t.Skip("unsupported OS") + case "aix", "darwin", "ios", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "windows": + } + dir, err := ioutil.TempDir("", "TestMMap") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + filename := filepath.Join(dir, "foo.out") + ob := NewOutBuf(nil) + if err := ob.Open(filename); err != nil { + t.Fatalf("error opening file: %v", err) + } + defer ob.Close() + if err := ob.Mmap(1 << 20); err != nil { + t.Errorf("error mmapping file %v", err) + } + if !ob.isMmapped() { + t.Errorf("should be mmapped") + } +} + +// TestWriteLoc ensures that the math surrounding writeLoc is correct. +func TestWriteLoc(t *testing.T) { + tests := []struct { + bufLen int + off int64 + heapLen int + lenToWrite int64 + expectedHeapLen int + writePos int64 + addressInHeap bool + }{ + {100, 0, 0, 100, 0, 0, false}, + {100, 100, 0, 100, 100, 0, true}, + {10, 10, 0, 100, 100, 0, true}, + {10, 20, 10, 100, 110, 10, true}, + {0, 0, 0, 100, 100, 0, true}, + } + + for i, test := range tests { + ob := &OutBuf{ + buf: make([]byte, test.bufLen), + off: test.off, + heap: make([]byte, test.heapLen), + } + pos, buf := ob.writeLoc(test.lenToWrite) + if pos != test.writePos { + t.Errorf("[%d] position = %d, expected %d", i, pos, test.writePos) + } + message := "mmapped area" + expected := ob.buf + if test.addressInHeap { + message = "heap" + expected = ob.heap + } + if &buf[0] != &expected[0] { + t.Errorf("[%d] expected position to be %q", i, message) + } + if len(ob.heap) != test.expectedHeapLen { + t.Errorf("[%d] expected len(ob.heap) == %d, got %d", i, test.expectedHeapLen, len(ob.heap)) + } + } +} + +func TestIsMmapped(t *testing.T) { + tests := []struct { + length int + expected bool + }{ + {0, false}, + {1, true}, + } + for i, test := range tests { + ob := &OutBuf{buf: make([]byte, test.length)} + if v := ob.isMmapped(); v != test.expected { + + t.Errorf("[%d] isMmapped == %t, expected %t", i, v, test.expected) + } + } +} diff --git a/src/cmd/link/internal/ld/outbuf_windows.go b/src/cmd/link/internal/ld/outbuf_windows.go new file mode 100644 index 0000000..915c72b --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_windows.go @@ -0,0 +1,67 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "reflect" + "syscall" + "unsafe" +) + +// Mmap maps the output file with the given size. It unmaps the old mapping +// if it is already mapped. It also flushes any in-heap data to the new +// mapping. +func (out *OutBuf) Mmap(filesize uint64) error { + oldlen := len(out.buf) + if oldlen != 0 { + out.munmap() + } + + err := out.f.Truncate(int64(filesize)) + if err != nil { + Exitf("resize output file failed: %v", err) + } + + low, high := uint32(filesize), uint32(filesize>>32) + fmap, err := syscall.CreateFileMapping(syscall.Handle(out.f.Fd()), nil, syscall.PAGE_READWRITE, high, low, nil) + if err != nil { + return err + } + defer syscall.CloseHandle(fmap) + + ptr, err := syscall.MapViewOfFile(fmap, syscall.FILE_MAP_READ|syscall.FILE_MAP_WRITE, 0, 0, uintptr(filesize)) + if err != nil { + return err + } + bufHdr := (*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 { + panic("mmap size too small") + } + copy(out.buf[oldlen:], out.heap) + out.heap = out.heap[:0] + return nil +} + +func (out *OutBuf) munmap() { + if out.buf == nil { + return + } + // Apparently unmapping without flush may cause ACCESS_DENIED error + // (see issue 38440). + err := syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])), 0) + if err != nil { + Exitf("FlushViewOfFile failed: %v", err) + } + err = syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&out.buf[0]))) + out.buf = nil + if err != nil { + Exitf("UnmapViewOfFile failed: %v", err) + } +} diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go new file mode 100644 index 0000000..72bf33e --- /dev/null +++ b/src/cmd/link/internal/ld/pcln.go @@ -0,0 +1,996 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "os" + "path/filepath" +) + +// 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 + + // Running total size of pclntab. + size int64 + + // runtime.pclntab's symbols + carrier loader.Sym + pclntab loader.Sym + pcheader loader.Sym + funcnametab loader.Sym + findfunctab loader.Sym + cutab loader.Sym + filetab loader.Sym + pctab loader.Sym + + // The number of functions + number of TEXT sections - 1. This is such an + // unexpected value because platforms that have more than one TEXT section + // get a dummy function inserted between because the external linker can place + // functions in those areas. We mark those areas as not covered by the Go + // runtime. + // + // On most platforms this is the number of reachable functions. + nfunc int32 + + // The number of filenames in runtime.filetab. + nfiles uint32 +} + +// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym. +// It is the caller's responsibilty to save they symbol in state. +func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f generatorFunc) loader.Sym { + size = Rnd(size, int64(ctxt.Arch.PtrSize)) + state.size += size + s := ctxt.createGeneratorSymbol(name, 0, sym.SPCLNTAB, size, f) + ctxt.loader.SetAttrReachable(s, true) + ctxt.loader.SetCarrierSym(s, state.carrier) + ctxt.loader.SetAttrNotInSymbolTable(s, true) + return s +} + +// makePclntab makes a pclntab object, and assembles all the compilation units +// we'll need to write pclntab. Returns the pclntab structure, a slice of the +// CompilationUnits we need, and a slice of the function symbols we need to +// generate pclntab. +func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) { + ldr := ctxt.loader + + state := &pclntab{ + // 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 + } + state.lastFunc = s + ss := ldr.SymSect(s) + if ss != prevSect { + // With multiple text sections, the external linker may + // insert functions between the sections, which are not + // known by Go. This leaves holes in the PC range covered + // by the func table. We need to generate an entry to mark + // the hole. + state.nfunc++ + prevSect = ss + } + + // We need to keep track of all compilation units we see. Some symbols + // (eg, go.buildid, _cgoexp_, etc) won't have a compilation unit. + cu := ldr.SymUnit(s) + if _, ok := seenCUs[cu]; cu != nil && !ok { + seenCUs[cu] = struct{}{} + cu.PclnIndex = len(compUnits) + compUnits = append(compUnits, cu) + } + } + return state, compUnits, funcs +} + +func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool { + // We want to generate func table entries only for the "lowest + // level" symbols, not containers of subsymbols. + return !container.Has(s) +} + +func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 { + ldr := ctxt.loader + target := ctxt.Target + deferreturn := uint32(0) + lastWasmAddr := uint32(0) + + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if target.IsWasm() && r.Type() == objabi.R_ADDR { + // Wasm does not have a live variable set at the deferreturn + // call itself. Instead it has one identified by the + // resumption point immediately preceding the deferreturn. + // The wasm code has a R_ADDR relocation which is used to + // set the resumption point to PC_B. + lastWasmAddr = uint32(r.Add()) + } + if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) { + if target.IsWasm() { + deferreturn = lastWasmAddr - 1 + } else { + // Note: the relocation target is in the call instruction, but + // is not necessarily the whole instruction (for instance, on + // x86 the relocation applies to bytes [1:5] of the 5 byte call + // instruction). + deferreturn = uint32(r.Off()) + switch target.Arch.Family { + case sys.AMD64, sys.I386: + deferreturn-- + case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64: + // no change + case sys.RISCV64: + // TODO(jsing): The JALR instruction is marked with + // R_CALLRISCV, whereas the actual reloc is currently + // one instruction earlier starting with the AUIPC. + deferreturn -= 4 + case sys.S390X: + deferreturn -= 2 + default: + panic(fmt.Sprint("Unhandled architecture:", target.Arch.Family)) + } + } + break // only need one + } + } + return deferreturn +} + +// genInlTreeSym generates the InlTree sym for a function with the +// specified FuncInfo. +func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym { + ldr := ctxt.loader + its := ldr.CreateExtSym("", 0) + inlTreeSym := ldr.MakeSymbolUpdater(its) + // Note: the generated symbol is given a type of sym.SGOFUNC, as a + // signal to the symtab() phase that it needs to be grouped in with + // other similar symbols (gcdata, etc); the dodata() phase will + // eventually switch the type back to SRODATA. + inlTreeSym.SetType(sym.SGOFUNC) + ldr.SetAttrReachable(its, true) + ninl := fi.NumInlTree() + for i := 0; i < int(ninl); i++ { + call := fi.InlTree(i) + val := call.File + nameoff, ok := nameOffsets[call.Func] + if !ok { + panic("couldn't find function name offset") + } + + inlTreeSym.SetUint16(arch, int64(i*20+0), uint16(call.Parent)) + inlFunc := ldr.FuncInfo(call.Func) + + var funcID objabi.FuncID + if inlFunc.Valid() { + funcID = inlFunc.FuncID() + } + inlTreeSym.SetUint8(arch, int64(i*20+2), uint8(funcID)) + + // byte 3 is unused + inlTreeSym.SetUint32(arch, int64(i*20+4), uint32(val)) + inlTreeSym.SetUint32(arch, int64(i*20+8), uint32(call.Line)) + inlTreeSym.SetUint32(arch, int64(i*20+12), uint32(nameoff)) + inlTreeSym.SetUint32(arch, int64(i*20+16), uint32(call.ParentPC)) + } + 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) { + writeHeader := func(ctxt *Link, s loader.Sym) { + ldr := ctxt.loader + header := ctxt.loader.MakeSymbolUpdater(s) + + writeSymOffset := func(off int64, ws loader.Sym) int64 { + diff := ldr.SymValue(ws) - ldr.SymValue(s) + if diff <= 0 { + name := ldr.SymName(ws) + panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before %s(%x)", ldr.SymValue(s), name, ldr.SymValue(ws))) + } + return header.SetUintptr(ctxt.Arch, off, uintptr(diff)) + } + + // Write header. + // Keep in sync with runtime/symtab.go:pcHeader. + header.SetUint32(ctxt.Arch, 0, 0xfffffffa) + 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 + 7*ctxt.Arch.PtrSize) + state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader) +} + +// walkFuncs iterates over the funcs, calling a function for each unique +// function and inlined function. +func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) { + ldr := ctxt.loader + seen := make(map[loader.Sym]struct{}) + for _, s := range funcs { + if _, ok := seen[s]; !ok { + f(s) + seen[s] = struct{}{} + } + + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() + for i, ni := 0, fi.NumInlTree(); i < int(ni); i++ { + call := fi.InlTree(i).Func + if _, ok := seen[call]; !ok { + f(call) + seen[call] = struct{}{} + } + } + } +} + +// generateFuncnametab creates the function name table. Returns a map of +// func symbol to the name offset in runtime.funcnamtab. +func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 { + nameOffsets := make(map[loader.Sym]uint32, state.nfunc) + + // Write the null terminated strings. + writeFuncNameTab := func(ctxt *Link, s loader.Sym) { + symtab := ctxt.loader.MakeSymbolUpdater(s) + 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 + 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 +} + +// walkFilenames walks funcs, calling a function for each filename used in each +// function's line table. +func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) { + ldr := ctxt.loader + + // Loop through all functions, finding the filenames we need. + for _, s := range funcs { + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() + + cu := ldr.SymUnit(s) + for i, nf := 0, int(fi.NumFile()); i < nf; i++ { + f(cu, fi.File(i)) + } + for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ { + call := fi.InlTree(i) + f(cu, call.File) + } + } +} + +// generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice +// of the index at which each CU begins in runtime.cutab. +// +// Function objects keep track of the files they reference to print the stack. +// This function creates a per-CU list of filenames if CU[M] references +// files[1-N], the following is generated: +// +// runtime.cutab: +// CU[M] +// offsetToFilename[0] +// offsetToFilename[1] +// .. +// +// runtime.filetab +// filename[0] +// filename[1] +// +// Looking up a filename then becomes: +// 0) Given a func, and filename index [K] +// 1) Get Func.CUIndex: M := func.cuOffset +// 2) Find filename offset: fileOffset := runtime.cutab[M+K] +// 3) Get the filename: getcstring(runtime.filetab[fileOffset]) +func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 { + // On a per-CU basis, keep track of all the filenames we need. + // + // Note, that we store the filenames in a separate section in the object + // files, and deduplicate based on the actual value. It would be better to + // store the filenames as symbols, using content addressable symbols (and + // then not loading extra filenames), and just use the hash value of the + // symbol name to do this cataloging. + // + // 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) + + // Walk the filenames. + // We store the total filename string length we need to load, and the max + // file index we've seen per CU so we can calculate how large the + // CU->global table needs to be. + var fileSize int64 + walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) { + // Note we use the raw filename for lookup, but use the expanded filename + // when we save the size. + filename := cu.FileTable[i] + if _, ok := fileOffsets[filename]; !ok { + fileOffsets[filename] = uint32(fileSize) + fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate + } + + // Find the maximum file index we've seen. + if cuEntries[cu.PclnIndex] < i+1 { + cuEntries[cu.PclnIndex] = i + 1 // Store max + 1 + } + }) + + // Calculate the size of the runtime.cutab variable. + var totalEntries uint32 + cuOffsets := make([]uint32, len(cuEntries)) + for i, entries := range cuEntries { + // Note, cutab is a slice of uint32, so an offset to a cu's entry is just the + // running total of all cu indices we've needed to store so far, not the + // number of bytes we've stored so far. + cuOffsets[i] = totalEntries + totalEntries += uint32(entries) + } + + // Write cutab. + writeCutab := func(ctxt *Link, s loader.Sym) { + sb := ctxt.loader.MakeSymbolUpdater(s) + + var off int64 + for i, max := range cuEntries { + // Write the per CU LUT. + cu := compUnits[i] + for j := goobj.CUFileIndex(0); j < max; j++ { + fileOffset, ok := fileOffsets[cu.FileTable[j]] + if !ok { + // We're looping through all possible file indices. It's possible a file's + // been deadcode eliminated, and although it's a valid file in the CU, it's + // not needed in this binary. When that happens, use an invalid offset. + fileOffset = ^uint32(0) + } + off = sb.SetUint32(ctxt.Arch, off, fileOffset) + } + } + } + state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab) + + // Write filetab. + writeFiletab := func(ctxt *Link, s loader.Sym) { + sb := ctxt.loader.MakeSymbolUpdater(s) + + // Write the strings. + for filename, loc := range fileOffsets { + sb.AddStringAt(int64(loc), expandFile(filename)) + } + } + state.nfiles = uint32(len(fileOffsets)) + state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab) + + return cuOffsets +} + +// generatePctab creates the runtime.pctab variable, holding all the +// deduplicated pcdata. +func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) { + ldr := ctxt.loader + + // Pctab offsets of 0 are considered invalid in the runtime. We respect + // that by just padding a single byte at the beginning of runtime.pctab, + // that way no real offsets can be zero. + size := int64(1) + + // Walk the functions, finding offset to store each pcdata. + seen := make(map[loader.Sym]struct{}) + saveOffset := func(pcSym loader.Sym) { + if _, ok := seen[pcSym]; !ok { + datSize := ldr.SymSize(pcSym) + if datSize != 0 { + ldr.SetSymValue(pcSym, size) + } else { + // Invalid PC data, record as zero. + ldr.SetSymValue(pcSym, 0) + } + size += datSize + seen[pcSym] = struct{}{} + } + } + 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 + } + } + 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 + +// 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 + } + } 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) + } + + state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln) + + // 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) + } +} + +// 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)) + } + 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) + + // 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() + numFuncData := int(fi.NumFuncdataoff()) + if fi.NumInlTree() > 0 { + if numFuncData < objabi.FUNCDATA_InlTree+1 { + numFuncData = objabi.FUNCDATA_InlTree + 1 + } + } + size += int64(numPCData(fi) * 4) + if numFuncData > 0 { // Func data is aligned. + size = Rnd(size, int64(ctxt.Arch.PtrSize)) + } + size += int64(numFuncData * ctxt.Arch.PtrSize) + } + } + + return size, startLocations +} + +// 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++ + + // Write the entry location. + setAddr(sb, ctxt.Arch, int64(startLocations[i]), s, 0) + } + + // Final entry of table is just end pc. + setAddr(sb, ctxt.Arch, int64(funcIndex)*2*int64(ctxt.Arch.PtrSize), prevFunc, ldr.SymSize(prevFunc)) +} + +// 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() + + // 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{} + + // Write the individual func objects. + for i, s := range funcs { + fi := ldr.FuncInfo(s) + if fi.Valid() { + fi.Preload() + } + + // 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 := nameOffsets[s] + if !ok { + panic("couldn't find function name offset") + } + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(nameoff))) + + // args int32 + // TODO: Move into funcinfo. + args := uint32(0) + if fi.Valid() { + args = uint32(fi.Args()) + } + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), args)) + + // deferreturn + deferreturn := computeDeferReturn(ctxt, deferReturnSym, s) + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), deferreturn)) + + // pcdata + if fi.Valid() { + 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)))) + + // Store the offset to compilation unit's file table. + cuIdx := ^uint32(0) + if cu := ldr.SymUnit(s); cu != nil { + cuIdx = cuOffsets[cu.PclnIndex] + } + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), cuIdx)) + + // funcID uint8 + var funcID objabi.FuncID + if fi.Valid() { + funcID = fi.FuncID() + } + off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(funcID))) + + off += 2 // pad + + // nfuncdata must be the final entry. + funcdata, funcdataoff = funcData(fi, 0, funcdata, funcdataoff) + off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata)))) + + // 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))) + } + if fi.NumInlTree() > 0 { + sb.SetUint32(ctxt.Arch, int64(off+objabi.PCDATA_InlTreeIndex*4), uint32(ldr.SymValue(fi.Pcinline()))) + } + } + } +} + +// pclntab initializes the pclntab symbol with +// runtime function and file name information. + +// pclntab generates the pcln table for the link output. +func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { + // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the + // layout and data has changed since that time. + // + // As of August 2020, here's the layout of pclntab: + // + // .gopclntab/__gopclntab [elf/macho section] + // runtime.pclntab + // Carrier symbol for the entire pclntab section. + // + // runtime.pcheader (see: runtime/symtab.go:pcHeader) + // 8-byte magic + // nfunc [thearch.ptrsize bytes] + // offset to runtime.funcnametab from the beginning of runtime.pcheader + // offset to runtime.pclntab_old from beginning of runtime.pcheader + // + // runtime.funcnametab + // []list of null terminated function names + // + // runtime.cutab + // for i=0..#CUs + // for j=0..#max used file index in CU[i] + // uint32 offset into runtime.filetab for the filename[j] + // + // runtime.filetab + // []null terminated filename strings + // + // runtime.pctab + // []byte of deduplicated pc data. + // + // runtime.functab + // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] + // end PC [thearch.ptrsize bytes] + // func structures, pcdata offsets, func data. + + state, compUnits, funcs := makePclntab(ctxt, container) + + ldr := ctxt.loader + state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0) + ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB) + ldr.SetAttrReachable(state.carrier, true) + setCarrierSym(sym.SPCLNTAB, state.carrier) + + state.generatePCHeader(ctxt) + nameOffsets := state.generateFuncnametab(ctxt, funcs) + cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs) + state.generatePctab(ctxt, funcs) + inlSyms := makeInlSyms(ctxt, funcs, nameOffsets) + state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets) + + return state +} + +func gorootFinal() string { + root := objabi.GOROOT + if final := os.Getenv("GOROOT_FINAL"); final != "" { + root = final + } + return root +} + +func expandGoroot(s string) string { + const n = len("$GOROOT") + if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') { + return filepath.ToSlash(filepath.Join(gorootFinal(), s[n:])) + } + return s +} + +const ( + BUCKETSIZE = 256 * MINFUNC + SUBBUCKETS = 16 + SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS + NOIDX = 0x7fffffff +) + +// findfunctab generates a lookup table to quickly find the containing +// function for a pc. See src/runtime/symtab.go:findfunc for details. +func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) { + ldr := ctxt.loader + + // find min and max address + min := ldr.SymValue(ctxt.Textp[0]) + lastp := ctxt.Textp[len(ctxt.Textp)-1] + max := ldr.SymValue(lastp) + ldr.SymSize(lastp) + + // for each subbucket, compute the minimum of all symbol indexes + // that map to that subbucket. + n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) + + nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) + + size := 4*int64(nbuckets) + int64(n) + + writeFindFuncTab := func(_ *Link, s loader.Sym) { + t := ldr.MakeSymbolUpdater(s) + + indexes := make([]int32, n) + for i := int32(0); i < n; i++ { + indexes[i] = NOIDX + } + idx := int32(0) + for i, s := range ctxt.Textp { + if !emitPcln(ctxt, s, container) { + continue + } + p := ldr.SymValue(s) + var e loader.Sym + i++ + if i < len(ctxt.Textp) { + e = ctxt.Textp[i] + } + for e != 0 && !emitPcln(ctxt, e, container) && i < len(ctxt.Textp) { + e = ctxt.Textp[i] + i++ + } + q := max + if e != 0 { + q = ldr.SymValue(e) + } + + //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); + for ; p < q; p += SUBBUCKETSIZE { + i = int((p - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + } + + i = int((q - 1 - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + idx++ + } + + // fill in table + for i := int32(0); i < nbuckets; i++ { + base := indexes[i*SUBBUCKETS] + if base == NOIDX { + Errorf(nil, "hole in findfunctab") + } + t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base)) + for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { + idx = indexes[i*SUBBUCKETS+j] + if idx == NOIDX { + Errorf(nil, "hole in findfunctab") + } + if idx-base >= 256 { + Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) + } + + t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) + } + } + } + + state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab) + ldr.SetAttrReachable(state.findfunctab, true) + ldr.SetAttrLocal(state.findfunctab, true) +} + +// findContainerSyms returns a bitmap, indexed by symbol number, where there's +// a 1 for every container symbol. +func (ctxt *Link) findContainerSyms() loader.Bitmap { + ldr := ctxt.loader + container := loader.MakeBitmap(ldr.NSym()) + // Find container symbols and mark them as such. + for _, s := range ctxt.Textp { + outer := ldr.OuterSym(s) + if outer != 0 { + container.Set(outer) + } + } + return container +} diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go new file mode 100644 index 0000000..5edaf54 --- /dev/null +++ b/src/cmd/link/internal/ld/pe.go @@ -0,0 +1,1628 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// PE (Portable Executable) file writing +// https://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/pe" + "encoding/binary" + "fmt" + "sort" + "strconv" + "strings" +) + +type IMAGE_IMPORT_DESCRIPTOR struct { + OriginalFirstThunk uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + Name uint32 + FirstThunk uint32 +} + +type IMAGE_EXPORT_DIRECTORY struct { + Characteristics uint32 + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + Name uint32 + Base uint32 + NumberOfFunctions uint32 + NumberOfNames uint32 + AddressOfFunctions uint32 + AddressOfNames uint32 + AddressOfNameOrdinals uint32 +} + +const ( + PEBASE = 0x00400000 +) + +var ( + // SectionAlignment must be greater than or equal to FileAlignment. + // The default is the page size for the architecture. + PESECTALIGN int64 = 0x1000 + + // FileAlignment should be a power of 2 between 512 and 64 K, inclusive. + // The default is 512. If the SectionAlignment is less than + // the architecture's page size, then FileAlignment must match SectionAlignment. + PEFILEALIGN int64 = 2 << 8 +) + +const ( + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 + IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 + IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 + IMAGE_SCN_ALIGN_32BYTES = 0x600000 +) + +// TODO(crawshaw): add these constants to debug/pe. +const ( + // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 and IMAGE_SYM_DTYPE_FUNCTION is 2 + IMAGE_SYM_TYPE_NULL = 0 + IMAGE_SYM_TYPE_STRUCT = 8 + IMAGE_SYM_DTYPE_FUNCTION = 0x20 + IMAGE_SYM_DTYPE_ARRAY = 0x30 + IMAGE_SYM_CLASS_EXTERNAL = 2 + IMAGE_SYM_CLASS_STATIC = 3 + + IMAGE_REL_I386_DIR32 = 0x0006 + IMAGE_REL_I386_SECREL = 0x000B + IMAGE_REL_I386_REL32 = 0x0014 + + IMAGE_REL_AMD64_ADDR64 = 0x0001 + IMAGE_REL_AMD64_ADDR32 = 0x0002 + IMAGE_REL_AMD64_REL32 = 0x0004 + IMAGE_REL_AMD64_SECREL = 0x000B + + IMAGE_REL_ARM_ABSOLUTE = 0x0000 + IMAGE_REL_ARM_ADDR32 = 0x0001 + IMAGE_REL_ARM_ADDR32NB = 0x0002 + IMAGE_REL_ARM_BRANCH24 = 0x0003 + IMAGE_REL_ARM_BRANCH11 = 0x0004 + IMAGE_REL_ARM_SECREL = 0x000F + + IMAGE_REL_BASED_HIGHLOW = 3 + IMAGE_REL_BASED_DIR64 = 10 +) + +const ( + PeMinimumTargetMajorVersion = 6 + PeMinimumTargetMinorVersion = 1 +) + +// DOS stub that prints out +// "This program cannot be run in DOS mode." +var dosstub = []uint8{ + 0x4d, + 0x5a, + 0x90, + 0x00, + 0x03, + 0x00, + 0x04, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xff, + 0xff, + 0x00, + 0x00, + 0x8b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x0e, + 0x1f, + 0xba, + 0x0e, + 0x00, + 0xb4, + 0x09, + 0xcd, + 0x21, + 0xb8, + 0x01, + 0x4c, + 0xcd, + 0x21, + 0x54, + 0x68, + 0x69, + 0x73, + 0x20, + 0x70, + 0x72, + 0x6f, + 0x67, + 0x72, + 0x61, + 0x6d, + 0x20, + 0x63, + 0x61, + 0x6e, + 0x6e, + 0x6f, + 0x74, + 0x20, + 0x62, + 0x65, + 0x20, + 0x72, + 0x75, + 0x6e, + 0x20, + 0x69, + 0x6e, + 0x20, + 0x44, + 0x4f, + 0x53, + 0x20, + 0x6d, + 0x6f, + 0x64, + 0x65, + 0x2e, + 0x0d, + 0x0d, + 0x0a, + 0x24, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +} + +type Imp struct { + s loader.Sym + off uint64 + next *Imp + argsize int +} + +type Dll struct { + name string + nameoff uint64 + thunkoff uint64 + ms *Imp + next *Dll +} + +var ( + rsrcsyms []loader.Sym + PESECTHEADR int32 + PEFILEHEADR int32 + pe64 int + dr *Dll + + dexport = make([]loader.Sym, 0, 1024) +) + +// peStringTable is a COFF string table. +type peStringTable struct { + strings []string + stringsLen int +} + +// size returns size of string table t. +func (t *peStringTable) size() int { + // string table starts with 4-byte length at the beginning + return t.stringsLen + 4 +} + +// add adds string str to string table t. +func (t *peStringTable) add(str string) int { + off := t.size() + t.strings = append(t.strings, str) + t.stringsLen += len(str) + 1 // each string will have 0 appended to it + return off +} + +// write writes string table t into the output file. +func (t *peStringTable) write(out *OutBuf) { + out.Write32(uint32(t.size())) + for _, s := range t.strings { + out.WriteString(s) + out.Write8(0) + } +} + +// peSection represents section from COFF section table. +type peSection struct { + name string + shortName string + index int // one-based index into the Section Table + virtualSize uint32 + virtualAddress uint32 + sizeOfRawData uint32 + pointerToRawData uint32 + pointerToRelocations uint32 + numberOfRelocations uint16 + characteristics uint32 +} + +// checkOffset verifies COFF section sect offset in the file. +func (sect *peSection) checkOffset(off int64) { + if off != int64(sect.pointerToRawData) { + Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(off)) + errorexit() + } +} + +// checkSegment verifies COFF section sect matches address +// and file offset provided in segment seg. +func (sect *peSection) checkSegment(seg *sym.Segment) { + if seg.Vaddr-PEBASE != uint64(sect.virtualAddress) { + Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-PEBASE))) + errorexit() + } + if seg.Fileoff != uint64(sect.pointerToRawData) { + Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(seg.Fileoff))) + errorexit() + } +} + +// pad adds zeros to the section sect. It writes as many bytes +// as necessary to make section sect.SizeOfRawData bytes long. +// It assumes that n bytes are already written to the file. +func (sect *peSection) pad(out *OutBuf, n uint32) { + out.WriteStringN("", int(sect.sizeOfRawData-n)) +} + +// write writes COFF section sect into the output file. +func (sect *peSection) write(out *OutBuf, linkmode LinkMode) error { + h := pe.SectionHeader32{ + VirtualSize: sect.virtualSize, + SizeOfRawData: sect.sizeOfRawData, + PointerToRawData: sect.pointerToRawData, + PointerToRelocations: sect.pointerToRelocations, + NumberOfRelocations: sect.numberOfRelocations, + Characteristics: sect.characteristics, + } + if linkmode != LinkExternal { + h.VirtualAddress = sect.virtualAddress + } + copy(h.Name[:], sect.shortName) + return binary.Write(out, binary.LittleEndian, h) +} + +// emitRelocations emits the relocation entries for the sect. +// The actual relocations are emitted by relocfn. +// This updates the corresponding PE section table entry +// with the relocation offset and count. +func (sect *peSection) emitRelocations(out *OutBuf, relocfn func() int) { + sect.pointerToRelocations = uint32(out.Offset()) + // first entry: extended relocs + out.Write32(0) // placeholder for number of relocation + 1 + out.Write32(0) + out.Write16(0) + + n := relocfn() + 1 + + cpos := out.Offset() + out.SeekSet(int64(sect.pointerToRelocations)) + out.Write32(uint32(n)) + out.SeekSet(cpos) + if n > 0x10000 { + n = 0x10000 + sect.characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL + } else { + sect.pointerToRelocations += 10 // skip the extend reloc entry + } + sect.numberOfRelocations = uint16(n - 1) +} + +// peFile is used to build COFF file. +type peFile struct { + sections []*peSection + stringTable peStringTable + textSect *peSection + rdataSect *peSection + dataSect *peSection + bssSect *peSection + ctorsSect *peSection + nextSectOffset uint32 + nextFileOffset uint32 + symtabOffset int64 // offset to the start of symbol table + symbolCount int // number of symbol table records written + dataDirectory [16]pe.DataDirectory +} + +// addSection adds section to the COFF file f. +func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection { + sect := &peSection{ + name: name, + shortName: name, + index: len(f.sections) + 1, + virtualSize: uint32(sectsize), + virtualAddress: f.nextSectOffset, + pointerToRawData: f.nextFileOffset, + } + f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN)) + if filesize > 0 { + sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN)) + f.nextFileOffset += sect.sizeOfRawData + } + f.sections = append(f.sections, sect) + return sect +} + +// addDWARFSection adds DWARF section to the COFF file f. +// This function is similar to addSection, but DWARF section names are +// longer than 8 characters, so they need to be stored in the string table. +func (f *peFile) addDWARFSection(name string, size int) *peSection { + if size == 0 { + Exitf("DWARF section %q is empty", name) + } + // DWARF section names are longer than 8 characters. + // PE format requires such names to be stored in string table, + // and section names replaced with slash (/) followed by + // correspondent string table index. + // see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx + // for details + off := f.stringTable.add(name) + h := f.addSection(name, size, size) + h.shortName = fmt.Sprintf("/%d", off) + h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + return h +} + +// addDWARF adds DWARF information to the COFF file f. +func (f *peFile) addDWARF() { + if *FlagS { // disable symbol table + return + } + if *FlagW { // disable dwarf + return + } + for _, sect := range Segdwarf.Sections { + h := f.addDWARFSection(sect.Name, int(sect.Length)) + fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff + if uint64(h.pointerToRawData) != fileoff { + Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff) + } + } +} + +// addInitArray adds .ctors COFF section to the file f. +func (f *peFile) addInitArray(ctxt *Link) *peSection { + // The size below was determined by the specification for array relocations, + // and by observing what GCC writes here. If the initarray section grows to + // contain more than one constructor entry, the size will need to be 8 * constructor_count. + // However, the entire Go runtime is initialized from just one function, so it is unlikely + // that this will need to grow in the future. + var size int + switch objabi.GOARCH { + default: + Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", objabi.GOARCH) + case "386": + size = 4 + case "amd64": + size = 8 + case "arm": + size = 4 + } + sect := f.addSection(".ctors", size, size) + sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + sect.sizeOfRawData = uint32(size) + ctxt.Out.SeekSet(int64(sect.pointerToRawData)) + sect.checkOffset(ctxt.Out.Offset()) + + init_entry := ctxt.loader.Lookup(*flagEntrySymbol, 0) + addr := uint64(ctxt.loader.SymValue(init_entry)) - ctxt.loader.SymSect(init_entry).Vaddr + switch objabi.GOARCH { + case "386", "arm": + ctxt.Out.Write32(uint32(addr)) + case "amd64": + ctxt.Out.Write64(addr) + } + return sect +} + +// emitRelocations emits relocation entries for go.o in external linking. +func (f *peFile) emitRelocations(ctxt *Link) { + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + ldr := ctxt.loader + + // relocsect relocates symbols from first in section sect, and returns + // the total number of relocations emitted. + relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) int { + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return 0 + } + nrelocs := 0 + sect.Reloff = uint64(ctxt.Out.Offset()) + for i, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + eaddr := int32(sect.Vaddr + sect.Length) + for _, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymValue(s) >= int64(eaddr) { + break + } + // Compute external relocations on the go, and pass to PEreloc1 + // to stream out. + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + rr, ok := extreloc(ctxt, ldr, s, r) + if !ok { + continue + } + if rr.Xsym == 0 { + ctxt.Errorf(s, "missing xsym in relocation") + continue + } + if ldr.SymDynid(rr.Xsym) < 0 { + ctxt.Errorf(s, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym())) + } + if !thearch.PEreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) { + ctxt.Errorf(s, "unsupported obj reloc %d/%d to %s", r.Type(), r.Siz(), ldr.SymName(r.Sym())) + } + nrelocs++ + } + } + sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff + return nrelocs + } + + sects := []struct { + peSect *peSection + seg *sym.Segment + syms []loader.Sym + }{ + {f.textSect, &Segtext, ctxt.Textp}, + {f.rdataSect, &Segrodata, ctxt.datap}, + {f.dataSect, &Segdata, ctxt.datap}, + } + for _, s := range sects { + s.peSect.emitRelocations(ctxt.Out, func() int { + var n int + for _, sect := range s.seg.Sections { + n += relocsect(sect, s.syms, s.seg.Vaddr) + } + return n + }) + } + +dwarfLoop: + for i := 0; i < len(Segdwarf.Sections); i++ { + sect := Segdwarf.Sections[i] + si := dwarfp[i] + if si.secSym() != loader.Sym(sect.Sym) || + ldr.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + for _, pesect := range f.sections { + if sect.Name == pesect.name { + pesect.emitRelocations(ctxt.Out, func() int { + return relocsect(sect, si.syms, sect.Vaddr) + }) + continue dwarfLoop + } + } + Errorf(nil, "emitRelocations: could not find %q section", sect.Name) + } + + f.ctorsSect.emitRelocations(ctxt.Out, func() int { + dottext := ldr.Lookup(".text", 0) + ctxt.Out.Write32(0) + ctxt.Out.Write32(uint32(ldr.SymDynid(dottext))) + switch objabi.GOARCH { + default: + ctxt.Errorf(dottext, "unknown architecture for PE: %q\n", objabi.GOARCH) + case "386": + ctxt.Out.Write16(IMAGE_REL_I386_DIR32) + case "amd64": + ctxt.Out.Write16(IMAGE_REL_AMD64_ADDR64) + case "arm": + ctxt.Out.Write16(IMAGE_REL_ARM_ADDR32) + } + return 1 + }) +} + +// writeSymbol appends symbol s to file f symbol table. +// It also sets s.Dynid to written symbol number. +func (f *peFile) writeSymbol(out *OutBuf, ldr *loader.Loader, s loader.Sym, name string, value int64, sectidx int, typ uint16, class uint8) { + if len(name) > 8 { + out.Write32(0) + out.Write32(uint32(f.stringTable.add(name))) + } else { + out.WriteStringN(name, 8) + } + out.Write32(uint32(value)) + out.Write16(uint16(sectidx)) + out.Write16(typ) + out.Write8(class) + out.Write8(0) // no aux entries + + ldr.SetSymDynid(s, int32(f.symbolCount)) + + f.symbolCount++ +} + +// mapToPESection searches peFile f for s symbol's location. +// It returns PE section index, and offset within that section. +func (f *peFile) mapToPESection(ldr *loader.Loader, s loader.Sym, linkmode LinkMode) (pesectidx int, offset int64, err error) { + sect := ldr.SymSect(s) + if sect == nil { + return 0, 0, fmt.Errorf("could not map %s symbol with no section", ldr.SymName(s)) + } + if sect.Seg == &Segtext { + return f.textSect.index, int64(uint64(ldr.SymValue(s)) - Segtext.Vaddr), nil + } + if sect.Seg == &Segrodata { + return f.rdataSect.index, int64(uint64(ldr.SymValue(s)) - Segrodata.Vaddr), nil + } + if sect.Seg != &Segdata { + return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", ldr.SymName(s)) + } + v := uint64(ldr.SymValue(s)) - Segdata.Vaddr + if linkmode != LinkExternal { + return f.dataSect.index, int64(v), nil + } + if ldr.SymType(s) == sym.SDATA { + return f.dataSect.index, int64(v), nil + } + // Note: although address of runtime.edata (type sym.SDATA) is at the start of .bss section + // it still belongs to the .data section, not the .bss section. + if v < Segdata.Filelen { + return f.dataSect.index, int64(v), nil + } + return f.bssSect.index, int64(v - Segdata.Filelen), nil +} + +// writeSymbols writes all COFF symbol table records. +func (f *peFile) writeSymbols(ctxt *Link) { + ldr := ctxt.loader + addsym := func(s loader.Sym) { + t := ldr.SymType(s) + if ldr.SymSect(s) == nil && t != sym.SDYNIMPORT && t != sym.SHOSTOBJ && t != sym.SUNDEFEXT { + return + } + + name := ldr.SymName(s) + + // Only windows/386 requires underscore prefix on external symbols. + if ctxt.Is386() && ctxt.IsExternal() && + (t == sym.SHOSTOBJ || t == sym.SUNDEFEXT || ldr.AttrCgoExport(s)) { + name = "_" + name + } + + var peSymType uint16 + if ctxt.IsExternal() { + peSymType = IMAGE_SYM_TYPE_NULL + } else { + // TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308 + // peSymType = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT + peSymType = 0x0308 // "array of structs" + } + sect, value, err := f.mapToPESection(ldr, s, ctxt.LinkMode) + if err != nil { + if t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT { + peSymType = IMAGE_SYM_DTYPE_FUNCTION + } else { + ctxt.Errorf(s, "addpesym: %v", err) + } + } + class := IMAGE_SYM_CLASS_EXTERNAL + if ldr.IsFileLocal(s) || ldr.AttrVisibilityHidden(s) || ldr.AttrLocal(s) { + class = IMAGE_SYM_CLASS_STATIC + } + f.writeSymbol(ctxt.Out, ldr, s, name, value, sect, peSymType, uint8(class)) + } + + if ctxt.LinkMode == LinkExternal { + // Include section symbols as external, because + // .ctors and .debug_* section relocations refer to it. + for _, pesect := range f.sections { + s := ldr.LookupOrCreateSym(pesect.name, 0) + f.writeSymbol(ctxt.Out, ldr, s, pesect.name, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC) + } + } + + // Add special runtime.text and runtime.etext symbols. + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + + // Add text symbols. + for _, s := range ctxt.Textp { + addsym(s) + } + + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + name := ldr.RawSymName(s) // TODO: try not to read the name + if name == "" || name[0] == '.' { + return false + } + return true + } + + // Add data symbols and external references. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + t := ldr.SymType(s) + if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata + if t == sym.STLSBSS { + continue + } + if !shouldBeInSymbolTable(s) { + continue + } + addsym(s) + } + + switch t { + case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT: + addsym(s) + } + } +} + +// writeSymbolTableAndStringTable writes out symbol and string tables for peFile f. +func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) { + f.symtabOffset = ctxt.Out.Offset() + + // write COFF symbol table + if !*FlagS || ctxt.LinkMode == LinkExternal { + f.writeSymbols(ctxt) + } + + // update COFF file header and section table + size := f.stringTable.size() + 18*f.symbolCount + var h *peSection + if ctxt.LinkMode != LinkExternal { + // We do not really need .symtab for go.o, and if we have one, ld + // will also include it in the exe, and that will confuse windows. + h = f.addSection(".symtab", size, size) + h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + h.checkOffset(f.symtabOffset) + } + + // write COFF string table + f.stringTable.write(ctxt.Out) + if ctxt.LinkMode != LinkExternal { + h.pad(ctxt.Out, uint32(size)) + } +} + +// writeFileHeader writes COFF file header for peFile f. +func (f *peFile) writeFileHeader(ctxt *Link) { + var fh pe.FileHeader + + switch ctxt.Arch.Family { + default: + Exitf("unknown PE architecture: %v", ctxt.Arch.Family) + case sys.AMD64: + fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64 + case sys.I386: + fh.Machine = pe.IMAGE_FILE_MACHINE_I386 + case sys.ARM: + fh.Machine = pe.IMAGE_FILE_MACHINE_ARMNT + } + + fh.NumberOfSections = uint16(len(f.sections)) + + // Being able to produce identical output for identical input is + // much more beneficial than having build timestamp in the header. + fh.TimeDateStamp = 0 + + if ctxt.LinkMode == LinkExternal { + fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED + } else { + fh.Characteristics = pe.IMAGE_FILE_EXECUTABLE_IMAGE | pe.IMAGE_FILE_DEBUG_STRIPPED + switch ctxt.Arch.Family { + case sys.AMD64, sys.I386: + if ctxt.BuildMode != BuildModePIE { + fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED + } + } + } + if pe64 != 0 { + var oh64 pe.OptionalHeader64 + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64)) + fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE + } else { + var oh pe.OptionalHeader32 + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh)) + fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE + } + + fh.PointerToSymbolTable = uint32(f.symtabOffset) + fh.NumberOfSymbols = uint32(f.symbolCount) + + binary.Write(ctxt.Out, binary.LittleEndian, &fh) +} + +// writeOptionalHeader writes COFF optional header for peFile f. +func (f *peFile) writeOptionalHeader(ctxt *Link) { + var oh pe.OptionalHeader32 + var oh64 pe.OptionalHeader64 + + if pe64 != 0 { + oh64.Magic = 0x20b // PE32+ + } else { + oh.Magic = 0x10b // PE32 + oh.BaseOfData = f.dataSect.virtualAddress + } + + // Fill out both oh64 and oh. We only use one. Oh well. + oh64.MajorLinkerVersion = 3 + oh.MajorLinkerVersion = 3 + oh64.MinorLinkerVersion = 0 + oh.MinorLinkerVersion = 0 + oh64.SizeOfCode = f.textSect.sizeOfRawData + oh.SizeOfCode = f.textSect.sizeOfRawData + oh64.SizeOfInitializedData = f.dataSect.sizeOfRawData + oh.SizeOfInitializedData = f.dataSect.sizeOfRawData + oh64.SizeOfUninitializedData = 0 + oh.SizeOfUninitializedData = 0 + if ctxt.LinkMode != LinkExternal { + oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE) + oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE) + } + oh64.BaseOfCode = f.textSect.virtualAddress + oh.BaseOfCode = f.textSect.virtualAddress + oh64.ImageBase = PEBASE + oh.ImageBase = PEBASE + oh64.SectionAlignment = uint32(PESECTALIGN) + oh.SectionAlignment = uint32(PESECTALIGN) + oh64.FileAlignment = uint32(PEFILEALIGN) + oh.FileAlignment = uint32(PEFILEALIGN) + oh64.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion + oh.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion + oh64.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion + oh.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion + oh64.MajorImageVersion = 1 + oh.MajorImageVersion = 1 + oh64.MinorImageVersion = 0 + oh.MinorImageVersion = 0 + oh64.MajorSubsystemVersion = PeMinimumTargetMajorVersion + oh.MajorSubsystemVersion = PeMinimumTargetMajorVersion + oh64.MinorSubsystemVersion = PeMinimumTargetMinorVersion + oh.MinorSubsystemVersion = PeMinimumTargetMinorVersion + oh64.SizeOfImage = f.nextSectOffset + oh.SizeOfImage = f.nextSectOffset + oh64.SizeOfHeaders = uint32(PEFILEHEADR) + oh.SizeOfHeaders = uint32(PEFILEHEADR) + if windowsgui { + oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI + oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI + } else { + oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI + oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI + } + + // Mark as having awareness of terminal services, to avoid ancient compatibility hacks. + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE + + // Enable DEP + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT + + // The DLL can be relocated at load time. + switch ctxt.Arch.Family { + case sys.AMD64, sys.I386: + if ctxt.BuildMode == BuildModePIE { + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + } + case sys.ARM: + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + } + + // Image can handle a high entropy 64-bit virtual address space. + if ctxt.BuildMode == BuildModePIE { + oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA + } + + // Disable stack growth as we don't want Windows to + // fiddle with the thread stack limits, which we set + // ourselves to circumvent the stack checks in the + // Windows exception dispatcher. + // Commit size must be strictly less than reserve + // size otherwise reserve will be rounded up to a + // larger size, as verified with VMMap. + + // On 64-bit, we always reserve 2MB stacks. "Pure" Go code is + // okay with much smaller stacks, but the syscall package + // makes it easy to call into arbitrary C code without cgo, + // and system calls even in "pure" Go code are actually C + // calls that may need more stack than we think. + // + // The default stack reserve size directly affects only the main + // thread, ctrlhandler thread, and profileloop thread. For + // these, it must be greater than the stack size assumed by + // externalthreadhandler. + // + // For other threads, the runtime explicitly asks the kernel + // to use the default stack size so that all stacks are + // consistent. + // + // At thread start, in minit, the runtime queries the OS for + // the actual stack bounds so that the stack size doesn't need + // to be hard-coded into the runtime. + oh64.SizeOfStackReserve = 0x00200000 + if !iscgo { + oh64.SizeOfStackCommit = 0x00001000 + } else { + // TODO(brainman): Maybe remove optional header writing altogether for cgo. + // For cgo it is the external linker that is building final executable. + // And it probably does not use any information stored in optional header. + oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages + } + + oh.SizeOfStackReserve = 0x00100000 + if !iscgo { + oh.SizeOfStackCommit = 0x00001000 + } else { + oh.SizeOfStackCommit = 0x00100000 - 0x2000 // account for 2 guard pages + } + + oh64.SizeOfHeapReserve = 0x00100000 + oh.SizeOfHeapReserve = 0x00100000 + oh64.SizeOfHeapCommit = 0x00001000 + oh.SizeOfHeapCommit = 0x00001000 + oh64.NumberOfRvaAndSizes = 16 + oh.NumberOfRvaAndSizes = 16 + + if pe64 != 0 { + oh64.DataDirectory = f.dataDirectory + } else { + oh.DataDirectory = f.dataDirectory + } + + if pe64 != 0 { + binary.Write(ctxt.Out, binary.LittleEndian, &oh64) + } else { + binary.Write(ctxt.Out, binary.LittleEndian, &oh) + } +} + +var pefile peFile + +func Peinit(ctxt *Link) { + var l int + + switch ctxt.Arch.Family { + // 64-bit architectures + case sys.AMD64: + pe64 = 1 + var oh64 pe.OptionalHeader64 + l = binary.Size(&oh64) + + // 32-bit architectures + default: + var oh pe.OptionalHeader32 + l = binary.Size(&oh) + + } + + if ctxt.LinkMode == LinkExternal { + // .rdata section will contain "masks" and "shifts" symbols, and they + // need to be aligned to 16-bytes. So make all sections aligned + // to 32-byte and mark them all IMAGE_SCN_ALIGN_32BYTES so external + // linker will honour that requirement. + PESECTALIGN = 32 + PEFILEALIGN = 0 + } + + var sh [16]pe.SectionHeader32 + var fh pe.FileHeader + PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN)) + if ctxt.LinkMode != LinkExternal { + PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN)) + } else { + PESECTHEADR = 0 + } + pefile.nextSectOffset = uint32(PESECTHEADR) + pefile.nextFileOffset = uint32(PEFILEHEADR) + + if ctxt.LinkMode == LinkInternal { + // some mingw libs depend on this symbol, for example, FindPESectionByName + for _, name := range [2]string{"__image_base__", "_image_base__"} { + sb := ctxt.loader.CreateSymForUpdate(name, 0) + sb.SetType(sym.SDATA) + sb.SetValue(PEBASE) + ctxt.loader.SetAttrSpecial(sb.Sym(), true) + ctxt.loader.SetAttrLocal(sb.Sym(), true) + } + } + + HEADR = PEFILEHEADR + if *FlagTextAddr == -1 { + *FlagTextAddr = PEBASE + int64(PESECTHEADR) + } + if *FlagRound == -1 { + *FlagRound = int(PESECTALIGN) + } +} + +func pewrite(ctxt *Link) { + ctxt.Out.SeekSet(0) + if ctxt.LinkMode != LinkExternal { + ctxt.Out.Write(dosstub) + ctxt.Out.WriteStringN("PE", 4) + } + + pefile.writeFileHeader(ctxt) + + pefile.writeOptionalHeader(ctxt) + + for _, sect := range pefile.sections { + sect.write(ctxt.Out, ctxt.LinkMode) + } +} + +func strput(out *OutBuf, s string) { + out.WriteString(s) + out.Write8(0) + // string must be padded to even size + if (len(s)+1)%2 != 0 { + out.Write8(0) + } +} + +func initdynimport(ctxt *Link) *Dll { + ldr := ctxt.loader + var d *Dll + + dr = nil + var m *Imp + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) || ldr.SymType(s) != sym.SDYNIMPORT { + continue + } + dynlib := ldr.SymDynimplib(s) + for d = dr; d != nil; d = d.next { + if d.name == dynlib { + m = new(Imp) + break + } + } + + if d == nil { + d = new(Dll) + d.name = dynlib + d.next = dr + dr = d + m = new(Imp) + } + + // Because external link requires properly stdcall decorated name, + // all external symbols in runtime use %n to denote that the number + // of uinptrs this function consumes. Store the argsize and discard + // the %n suffix if any. + m.argsize = -1 + extName := ldr.SymExtname(s) + if i := strings.IndexByte(extName, '%'); i >= 0 { + var err error + m.argsize, err = strconv.Atoi(extName[i+1:]) + if err != nil { + ctxt.Errorf(s, "failed to parse stdcall decoration: %v", err) + } + m.argsize *= ctxt.Arch.PtrSize + ldr.SetSymExtname(s, extName[:i]) + } + + m.s = s + m.next = d.ms + d.ms = m + } + + if ctxt.IsExternal() { + // Add real symbol name + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + sb := ldr.MakeSymbolUpdater(m.s) + sb.SetType(sym.SDATA) + sb.Grow(int64(ctxt.Arch.PtrSize)) + dynName := sb.Extname() + // only windows/386 requires stdcall decoration + if ctxt.Is386() && m.argsize >= 0 { + dynName += fmt.Sprintf("@%d", m.argsize) + } + dynSym := ldr.CreateSymForUpdate(dynName, 0) + dynSym.SetType(sym.SHOSTOBJ) + r, _ := sb.AddRel(objabi.R_ADDR) + r.SetSym(dynSym.Sym()) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + } + } + } else { + dynamic := ldr.CreateSymForUpdate(".windynamic", 0) + dynamic.SetType(sym.SWINDOWS) + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + sb := ldr.MakeSymbolUpdater(m.s) + sb.SetType(sym.SWINDOWS) + sb.SetValue(dynamic.Size()) + dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) + dynamic.AddInteriorSym(m.s) + } + + dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) + } + } + + return dr +} + +// peimporteddlls returns the gcc command line argument to link all imported +// DLLs. +func peimporteddlls() []string { + var dlls []string + + for d := dr; d != nil; d = d.next { + dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll")) + } + + return dlls +} + +func addimports(ctxt *Link, datsect *peSection) { + ldr := ctxt.loader + startoff := ctxt.Out.Offset() + dynamic := ldr.LookupOrCreateSym(".windynamic", 0) + + // skip import descriptor table (will write it later) + n := uint64(0) + + for d := dr; d != nil; d = d.next { + n++ + } + ctxt.Out.SeekSet(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1)) + + // write dll names + for d := dr; d != nil; d = d.next { + d.nameoff = uint64(ctxt.Out.Offset()) - uint64(startoff) + strput(ctxt.Out, d.name) + } + + // write function names + for d := dr; d != nil; d = d.next { + for m := d.ms; m != nil; m = m.next { + m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff) + ctxt.Out.Write16(0) // hint + strput(ctxt.Out, ldr.SymExtname(m.s)) + } + } + + // write OriginalFirstThunks + oftbase := uint64(ctxt.Out.Offset()) - uint64(startoff) + + n = uint64(ctxt.Out.Offset()) + for d := dr; d != nil; d = d.next { + d.thunkoff = uint64(ctxt.Out.Offset()) - n + for m := d.ms; m != nil; m = m.next { + if pe64 != 0 { + ctxt.Out.Write64(m.off) + } else { + ctxt.Out.Write32(uint32(m.off)) + } + } + + if pe64 != 0 { + ctxt.Out.Write64(0) + } else { + ctxt.Out.Write32(0) + } + } + + // add pe section and pad it at the end + n = uint64(ctxt.Out.Offset()) - uint64(startoff) + + isect := pefile.addSection(".idata", int(n), int(n)) + isect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + isect.checkOffset(startoff) + isect.pad(ctxt.Out, uint32(n)) + endoff := ctxt.Out.Offset() + + // write FirstThunks (allocated in .data section) + ftbase := uint64(ldr.SymValue(dynamic)) - uint64(datsect.virtualAddress) - PEBASE + + ctxt.Out.SeekSet(int64(uint64(datsect.pointerToRawData) + ftbase)) + for d := dr; d != nil; d = d.next { + for m := d.ms; m != nil; m = m.next { + if pe64 != 0 { + ctxt.Out.Write64(m.off) + } else { + ctxt.Out.Write32(uint32(m.off)) + } + } + + if pe64 != 0 { + ctxt.Out.Write64(0) + } else { + ctxt.Out.Write32(0) + } + } + + // finally write import descriptor table + out := ctxt.Out + out.SeekSet(startoff) + + for d := dr; d != nil; d = d.next { + out.Write32(uint32(uint64(isect.virtualAddress) + oftbase + d.thunkoff)) + out.Write32(0) + out.Write32(0) + out.Write32(uint32(uint64(isect.virtualAddress) + d.nameoff)) + out.Write32(uint32(uint64(datsect.virtualAddress) + ftbase + d.thunkoff)) + } + + out.Write32(0) //end + out.Write32(0) + out.Write32(0) + out.Write32(0) + out.Write32(0) + + // update data directory + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(ldr.SymValue(dynamic) - PEBASE) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(ldr.SymSize(dynamic)) + + out.SeekSet(endoff) +} + +func initdynexport(ctxt *Link) { + ldr := ctxt.loader + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) || !ldr.AttrCgoExportDynamic(s) { + continue + } + if len(dexport)+1 > cap(dexport) { + ctxt.Errorf(s, "pe dynexport table is full") + errorexit() + } + + dexport = append(dexport, s) + } + + sort.Slice(dexport, func(i, j int) bool { return ldr.SymExtname(dexport[i]) < ldr.SymExtname(dexport[j]) }) +} + +func addexports(ctxt *Link) { + ldr := ctxt.loader + var e IMAGE_EXPORT_DIRECTORY + + nexport := len(dexport) + size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1 + for _, s := range dexport { + size += len(ldr.SymExtname(s)) + 1 + } + + if nexport == 0 { + return + } + + sect := pefile.addSection(".edata", size, size) + sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + sect.checkOffset(ctxt.Out.Offset()) + va := int(sect.virtualAddress) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize + + vaName := va + binary.Size(&e) + nexport*4 + vaAddr := va + binary.Size(&e) + vaNa := va + binary.Size(&e) + nexport*8 + + e.Characteristics = 0 + e.MajorVersion = 0 + e.MinorVersion = 0 + e.NumberOfFunctions = uint32(nexport) + e.NumberOfNames = uint32(nexport) + e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names. + e.Base = 1 + e.AddressOfFunctions = uint32(vaAddr) + e.AddressOfNames = uint32(vaName) + e.AddressOfNameOrdinals = uint32(vaNa) + + out := ctxt.Out + + // put IMAGE_EXPORT_DIRECTORY + binary.Write(out, binary.LittleEndian, &e) + + // put EXPORT Address Table + for _, s := range dexport { + out.Write32(uint32(ldr.SymValue(s) - PEBASE)) + } + + // put EXPORT Name Pointer Table + v := int(e.Name + uint32(len(*flagOutfile)) + 1) + + for _, s := range dexport { + out.Write32(uint32(v)) + v += len(ldr.SymExtname(s)) + 1 + } + + // put EXPORT Ordinal Table + for i := 0; i < nexport; i++ { + out.Write16(uint16(i)) + } + + // put Names + out.WriteStringN(*flagOutfile, len(*flagOutfile)+1) + + for _, s := range dexport { + name := ldr.SymExtname(s) + out.WriteStringN(name, len(name)+1) + } + sect.pad(out, uint32(size)) +} + +// peBaseRelocEntry represents a single relocation entry. +type peBaseRelocEntry struct { + typeOff uint16 +} + +// peBaseRelocBlock represents a Base Relocation Block. A block +// is a collection of relocation entries in a page, where each +// entry describes a single relocation. +// The block page RVA (Relative Virtual Address) is the index +// into peBaseRelocTable.blocks. +type peBaseRelocBlock struct { + entries []peBaseRelocEntry +} + +// pePages is a type used to store the list of pages for which there +// are base relocation blocks. This is defined as a type so that +// it can be sorted. +type pePages []uint32 + +func (p pePages) Len() int { return len(p) } +func (p pePages) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p pePages) Less(i, j int) bool { return p[i] < p[j] } + +// A PE base relocation table is a list of blocks, where each block +// contains relocation information for a single page. The blocks +// must be emitted in order of page virtual address. +// See https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-reloc-section-image-only +type peBaseRelocTable struct { + blocks map[uint32]peBaseRelocBlock + + // pePages is a list of keys into blocks map. + // It is stored separately for ease of sorting. + pages pePages +} + +func (rt *peBaseRelocTable) init(ctxt *Link) { + rt.blocks = make(map[uint32]peBaseRelocBlock) +} + +func (rt *peBaseRelocTable) addentry(ldr *loader.Loader, s loader.Sym, r *loader.Reloc) { + // pageSize is the size in bytes of a page + // described by a base relocation block. + const pageSize = 0x1000 + const pageMask = pageSize - 1 + + addr := ldr.SymValue(s) + int64(r.Off()) - int64(PEBASE) + page := uint32(addr &^ pageMask) + off := uint32(addr & pageMask) + + b, ok := rt.blocks[page] + if !ok { + rt.pages = append(rt.pages, page) + } + + e := peBaseRelocEntry{ + typeOff: uint16(off & 0xFFF), + } + + // Set entry type + switch r.Siz() { + default: + Exitf("unsupported relocation size %d\n", r.Siz) + case 4: + e.typeOff |= uint16(IMAGE_REL_BASED_HIGHLOW << 12) + case 8: + e.typeOff |= uint16(IMAGE_REL_BASED_DIR64 << 12) + } + + b.entries = append(b.entries, e) + rt.blocks[page] = b +} + +func (rt *peBaseRelocTable) write(ctxt *Link) { + out := ctxt.Out + + // sort the pages array + sort.Sort(rt.pages) + + for _, p := range rt.pages { + b := rt.blocks[p] + const sizeOfPEbaseRelocBlock = 8 // 2 * sizeof(uint32) + blockSize := uint32(sizeOfPEbaseRelocBlock + len(b.entries)*2) + out.Write32(p) + out.Write32(blockSize) + + for _, e := range b.entries { + out.Write16(e.typeOff) + } + } +} + +func addPEBaseRelocSym(ldr *loader.Loader, s loader.Sym, rt *peBaseRelocTable) { + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.Type() >= objabi.ElfRelocOffset { + continue + } + if r.Siz() == 0 { // informational relocation + continue + } + if r.Type() == objabi.R_DWARFFILEREF { + continue + } + rs := r.Sym() + rs = ldr.ResolveABIAlias(rs) + if rs == 0 { + continue + } + if !ldr.AttrReachable(s) { + continue + } + + switch r.Type() { + default: + case objabi.R_ADDR: + rt.addentry(ldr, s, &r) + } + } +} + +func addPEBaseReloc(ctxt *Link) { + // Arm does not work without base relocation table. + // 386 and amd64 will only require the table for BuildModePIE. + switch ctxt.Arch.Family { + default: + return + case sys.I386, sys.AMD64: + if ctxt.BuildMode != BuildModePIE { + return + } + case sys.ARM: + } + + var rt peBaseRelocTable + rt.init(ctxt) + + // Get relocation information + ldr := ctxt.loader + for _, s := range ctxt.Textp { + addPEBaseRelocSym(ldr, s, &rt) + } + for _, s := range ctxt.datap { + addPEBaseRelocSym(ldr, s, &rt) + } + + // Write relocation information + startoff := ctxt.Out.Offset() + rt.write(ctxt) + size := ctxt.Out.Offset() - startoff + + // Add a PE section and pad it at the end + rsect := pefile.addSection(".reloc", int(size), int(size)) + rsect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + rsect.checkOffset(startoff) + rsect.pad(ctxt.Out, uint32(size)) + + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize +} + +func (ctxt *Link) dope() { + initdynimport(ctxt) + initdynexport(ctxt) +} + +func setpersrc(ctxt *Link, syms []loader.Sym) { + if len(rsrcsyms) != 0 { + Errorf(nil, "too many .rsrc sections") + } + rsrcsyms = syms +} + +func addpersrc(ctxt *Link) { + if len(rsrcsyms) == 0 { + return + } + + var size int64 + for _, rsrcsym := range rsrcsyms { + size += ctxt.loader.SymSize(rsrcsym) + } + h := pefile.addSection(".rsrc", int(size), int(size)) + h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA + h.checkOffset(ctxt.Out.Offset()) + + for _, rsrcsym := range rsrcsyms { + // A split resource happens when the actual resource data and its relocations are + // split across multiple sections, denoted by a $01 or $02 at the end of the .rsrc + // section name. + splitResources := strings.Contains(ctxt.loader.SymName(rsrcsym), ".rsrc$") + relocs := ctxt.loader.Relocs(rsrcsym) + data := ctxt.loader.Data(rsrcsym) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + p := data[r.Off():] + val := uint32(int64(h.virtualAddress) + r.Add()) + if splitResources { + // If we're a split resource section, and that section has relocation + // symbols, then the data that it points to doesn't actually begin at + // the virtual address listed in this current section, but rather + // begins at the section immediately after this one. So, in order to + // calculate the proper virtual address of the data it's pointing to, + // we have to add the length of this section to the virtual address. + // This works because .rsrc sections are divided into two (but not more) + // of these sections. + val += uint32(len(data)) + } + binary.LittleEndian.PutUint32(p, val) + } + ctxt.Out.Write(data) + } + h.pad(ctxt.Out, uint32(size)) + + // update data directory + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize +} + +func asmbPe(ctxt *Link) { + switch ctxt.Arch.Family { + default: + Exitf("unknown PE architecture: %v", ctxt.Arch.Family) + case sys.AMD64, sys.I386, sys.ARM: + } + + t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length)) + t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ + if ctxt.LinkMode == LinkExternal { + // some data symbols (e.g. masks) end up in the .text section, and they normally + // expect larger alignment requirement than the default text section alignment. + t.characteristics |= IMAGE_SCN_ALIGN_32BYTES + } + t.checkSegment(&Segtext) + pefile.textSect = t + + ro := pefile.addSection(".rdata", int(Segrodata.Length), int(Segrodata.Length)) + ro.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + if ctxt.LinkMode == LinkExternal { + // some data symbols (e.g. masks) end up in the .rdata section, and they normally + // expect larger alignment requirement than the default text section alignment. + ro.characteristics |= IMAGE_SCN_ALIGN_32BYTES + } + ro.checkSegment(&Segrodata) + pefile.rdataSect = ro + + var d *peSection + if ctxt.LinkMode != LinkExternal { + d = pefile.addSection(".data", int(Segdata.Length), int(Segdata.Filelen)) + d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + d.checkSegment(&Segdata) + pefile.dataSect = d + } else { + d = pefile.addSection(".data", int(Segdata.Filelen), int(Segdata.Filelen)) + d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES + d.checkSegment(&Segdata) + pefile.dataSect = d + + b := pefile.addSection(".bss", int(Segdata.Length-Segdata.Filelen), 0) + b.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES + b.pointerToRawData = 0 + pefile.bssSect = b + } + + pefile.addDWARF() + + if ctxt.LinkMode == LinkExternal { + pefile.ctorsSect = pefile.addInitArray(ctxt) + } + + ctxt.Out.SeekSet(int64(pefile.nextFileOffset)) + if ctxt.LinkMode != LinkExternal { + addimports(ctxt, d) + addexports(ctxt) + addPEBaseReloc(ctxt) + } + pefile.writeSymbolTableAndStringTable(ctxt) + addpersrc(ctxt) + if ctxt.LinkMode == LinkExternal { + pefile.emitRelocations(ctxt) + } + + pewrite(ctxt) +} diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go new file mode 100644 index 0000000..7548972 --- /dev/null +++ b/src/cmd/link/internal/ld/sym.go @@ -0,0 +1,116 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "log" + "runtime" +) + +func linknew(arch *sys.Arch) *Link { + ler := loader.ErrorReporter{AfterErrorAction: afterErrorAction} + ctxt := &Link{ + Target: Target{Arch: arch}, + version: sym.SymVerStatic, + outSem: make(chan int, 2*runtime.GOMAXPROCS(0)), + Out: NewOutBuf(arch), + LibraryByPkg: make(map[string]*sym.Library), + numelfsym: 1, + ErrorReporter: ErrorReporter{ErrorReporter: ler}, + generatorSyms: make(map[loader.Sym]generatorFunc), + } + + if objabi.GOARCH != arch.Name { + log.Fatalf("invalid objabi.GOARCH %s (want %s)", objabi.GOARCH, arch.Name) + } + + AtExit(func() { + if nerrors > 0 { + ctxt.Out.Close() + mayberemoveoutfile() + } + }) + + return ctxt +} + +// computeTLSOffset records the thread-local storage offset. +// Not used for Android where the TLS offset is determined at runtime. +func (ctxt *Link) computeTLSOffset() { + switch ctxt.HeadType { + default: + log.Fatalf("unknown thread-local storage offset for %v", ctxt.HeadType) + + case objabi.Hplan9, objabi.Hwindows, objabi.Hjs, objabi.Haix: + break + + case objabi.Hlinux, + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd, + objabi.Hdragonfly, + objabi.Hsolaris: + /* + * ELF uses TLS offset negative from FS. + * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). + * Known to low-level assembly in package runtime and runtime/cgo. + */ + ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize + + case objabi.Hdarwin: + /* + * OS X system constants - offset from 0(GS) to our TLS. + */ + switch ctxt.Arch.Family { + default: + log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) + + /* + * For x86, Apple has reserved a slot in the TLS for Go. See issue 23617. + * That slot is at offset 0x30 on amd64. + * The slot will hold the G pointer. + * These constants should match those in runtime/sys_darwin_amd64.s + * and runtime/cgo/gcc_darwin_amd64.c. + */ + case sys.AMD64: + ctxt.Tlsoffset = 0x30 + + case sys.ARM64: + ctxt.Tlsoffset = 0 // dummy value, not needed + } + } + +} diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go new file mode 100644 index 0000000..f54cf9e --- /dev/null +++ b/src/cmd/link/internal/ld/symtab.go @@ -0,0 +1,832 @@ +// Inferno utils/6l/span.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "fmt" + "path/filepath" + "strings" +) + +// Symbol table. + +func putelfstr(s string) int { + if len(Elfstrdat) == 0 && s != "" { + // first entry must be empty string + putelfstr("") + } + + off := len(Elfstrdat) + Elfstrdat = append(Elfstrdat, s...) + Elfstrdat = append(Elfstrdat, 0) + return off +} + +func putelfsyment(out *OutBuf, off int, addr int64, size int64, info uint8, shndx elf.SectionIndex, other int) { + if elf64 { + out.Write32(uint32(off)) + out.Write8(info) + out.Write8(uint8(other)) + out.Write16(uint16(shndx)) + out.Write64(uint64(addr)) + out.Write64(uint64(size)) + symSize += ELF64SYMSIZE + } else { + out.Write32(uint32(off)) + out.Write32(uint32(addr)) + out.Write32(uint32(size)) + out.Write8(info) + out.Write8(uint8(other)) + out.Write16(uint16(shndx)) + symSize += ELF32SYMSIZE + } +} + +func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) { + ldr := ctxt.loader + addr := ldr.SymValue(x) + size := ldr.SymSize(x) + + xo := x + if ldr.OuterSym(x) != 0 { + xo = ldr.OuterSym(x) + } + xot := ldr.SymType(xo) + xosect := ldr.SymSect(xo) + + var elfshnum elf.SectionIndex + if xot == sym.SDYNIMPORT || xot == sym.SHOSTOBJ || xot == sym.SUNDEFEXT { + elfshnum = elf.SHN_UNDEF + size = 0 + } else { + if xosect == nil { + ldr.Errorf(x, "missing section in putelfsym") + return + } + if xosect.Elfsect == nil { + ldr.Errorf(x, "missing ELF section in putelfsym") + return + } + elfshnum = xosect.Elfsect.(*ElfShdr).shnum + } + + sname := ldr.SymExtname(x) + + // One pass for each binding: elf.STB_LOCAL, elf.STB_GLOBAL, + // maybe one day elf.STB_WEAK. + bind := elf.STB_GLOBAL + if ldr.IsFileLocal(x) && !isStaticTmp(sname) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) { + // Static tmp is package local, but a package can be shared among multiple DSOs. + // They need to have a single view of the static tmp that are writable. + bind = elf.STB_LOCAL + } + + // In external linking mode, we have to invoke gcc with -rdynamic + // to get the exported symbols put into the dynamic symbol table. + // To avoid filling the dynamic table with lots of unnecessary symbols, + // mark all Go symbols local (not global) in the final executable. + // But when we're dynamically linking, we need all those global symbols. + if !ctxt.DynlinkingGo() && ctxt.IsExternal() && !ldr.AttrCgoExportStatic(x) && elfshnum != elf.SHN_UNDEF { + bind = elf.STB_LOCAL + } + + if ctxt.LinkMode == LinkExternal && elfshnum != elf.SHN_UNDEF { + addr -= int64(xosect.Vaddr) + } + other := int(elf.STV_DEFAULT) + if ldr.AttrVisibilityHidden(x) { + // TODO(mwhudson): We only set AttrVisibilityHidden in ldelf, i.e. when + // internally linking. But STV_HIDDEN visibility only matters in object + // files and shared libraries, and as we are a long way from implementing + // internal linking for shared libraries and only create object files when + // externally linking, I don't think this makes a lot of sense. + other = int(elf.STV_HIDDEN) + } + if ctxt.IsPPC64() && typ == elf.STT_FUNC && ldr.AttrShared(x) && ldr.SymName(x) != "runtime.duffzero" && ldr.SymName(x) != "runtime.duffcopy" { + // On ppc64 the top three bits of the st_other field indicate how + // many instructions separate the global and local entry points. In + // our case it is two instructions, indicated by the value 3. + // The conditions here match those in preprocess in + // cmd/internal/obj/ppc64/obj9.go, which is where the + // instructions are inserted. + other |= 3 << 5 + } + + // When dynamically linking, we create Symbols by reading the names from + // the symbol tables of the shared libraries and so the names need to + // match exactly. Tools like DTrace will have to wait for now. + if !ctxt.DynlinkingGo() { + // Rewrite · to . for ASCII-only tools like DTrace (sigh) + sname = strings.Replace(sname, "·", ".", -1) + } + + if ctxt.DynlinkingGo() && bind == elf.STB_GLOBAL && curbind == elf.STB_LOCAL && ldr.SymType(x) == sym.STEXT { + // When dynamically linking, we want references to functions defined + // in this module to always be to the function object, not to the + // PLT. We force this by writing an additional local symbol for every + // global function symbol and making all relocations against the + // global symbol refer to this local symbol instead (see + // (*sym.Symbol).ElfsymForReloc). This is approximately equivalent to the + // ELF linker -Bsymbolic-functions option, but that is buggy on + // several platforms. + putelfsyment(ctxt.Out, putelfstr("local."+sname), addr, size, elf.ST_INFO(elf.STB_LOCAL, typ), elfshnum, other) + ldr.SetSymLocalElfSym(x, int32(ctxt.numelfsym)) + ctxt.numelfsym++ + return + } else if bind != curbind { + return + } + + putelfsyment(ctxt.Out, putelfstr(sname), addr, size, elf.ST_INFO(bind, typ), elfshnum, other) + ldr.SetSymElfSym(x, int32(ctxt.numelfsym)) + ctxt.numelfsym++ +} + +func putelfsectionsym(ctxt *Link, out *OutBuf, s loader.Sym, shndx elf.SectionIndex) { + putelfsyment(out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_SECTION), shndx, 0) + ctxt.loader.SetSymElfSym(s, int32(ctxt.numelfsym)) + ctxt.numelfsym++ +} + +func genelfsym(ctxt *Link, elfbind elf.SymBind) { + ldr := ctxt.loader + + // runtime.text marker symbol(s). + s := ldr.Lookup("runtime.text", 0) + putelfsym(ctxt, s, elf.STT_FUNC, elfbind) + for k, sect := range Segtext.Sections[1:] { + n := k + 1 + if sect.Name != ".text" || (ctxt.IsAIX() && ctxt.IsExternal()) { + // On AIX, runtime.text.X are symbols already in the symtab. + break + } + s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0) + if s == 0 { + break + } + if ldr.SymType(s) != sym.STEXT { + panic("unexpected type for runtime.text symbol") + } + putelfsym(ctxt, s, elf.STT_FUNC, elfbind) + } + + // Text symbols. + for _, s := range ctxt.Textp { + putelfsym(ctxt, s, elf.STT_FUNC, elfbind) + } + + // runtime.etext marker symbol. + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + putelfsym(ctxt, s, elf.STT_FUNC, elfbind) + } + + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + // FIXME: avoid having to do name inspections here. + // NB: the restrictions below on file local symbols are a bit + // arbitrary -- if it turns out we need nameless static + // symbols they could be relaxed/removed. + sn := ldr.SymName(s) + if (sn == "" || sn[0] == '.') && ldr.IsFileLocal(s) { + panic(fmt.Sprintf("unexpected file local symbol %d %s<%d>\n", + s, sn, ldr.SymVersion(s))) + } + if (sn == "" || sn[0] == '.') && !ldr.IsFileLocal(s) { + return false + } + return true + } + + // Data symbols. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + st := ldr.SymType(s) + if st >= sym.SELFRXSECT && st < sym.SXREF { + typ := elf.STT_OBJECT + if st == sym.STLSBSS { + if ctxt.IsInternal() { + continue + } + typ = elf.STT_TLS + } + if !shouldBeInSymbolTable(s) { + continue + } + putelfsym(ctxt, s, typ, elfbind) + continue + } + if st == sym.SHOSTOBJ || st == sym.SDYNIMPORT || st == sym.SUNDEFEXT { + putelfsym(ctxt, s, ldr.SymElfType(s), elfbind) + } + } +} + +func asmElfSym(ctxt *Link) { + + // the first symbol entry is reserved + putelfsyment(ctxt.Out, 0, 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE), 0, 0) + + dwarfaddelfsectionsyms(ctxt) + + // Some linkers will add a FILE sym if one is not present. + // Avoid having the working directory inserted into the symbol table. + // It is added with a name to avoid problems with external linking + // encountered on some versions of Solaris. See issue #14957. + putelfsyment(ctxt.Out, putelfstr("go.go"), 0, 0, elf.ST_INFO(elf.STB_LOCAL, elf.STT_FILE), elf.SHN_ABS, 0) + ctxt.numelfsym++ + + bindings := []elf.SymBind{elf.STB_LOCAL, elf.STB_GLOBAL} + for _, elfbind := range bindings { + if elfbind == elf.STB_GLOBAL { + elfglobalsymndx = ctxt.numelfsym + } + genelfsym(ctxt, elfbind) + } +} + +func putplan9sym(ctxt *Link, ldr *loader.Loader, s loader.Sym, char SymbolType) { + t := int(char) + if ldr.IsFileLocal(s) { + t += 'a' - 'A' + } + l := 4 + addr := ldr.SymValue(s) + if ctxt.IsAMD64() && !flag8 { + ctxt.Out.Write32b(uint32(addr >> 32)) + l = 8 + } + + ctxt.Out.Write32b(uint32(addr)) + ctxt.Out.Write8(uint8(t + 0x80)) /* 0x80 is variable length */ + + name := ldr.SymName(s) + ctxt.Out.WriteString(name) + ctxt.Out.Write8(0) + + symSize += int32(l) + 1 + int32(len(name)) + 1 +} + +func asmbPlan9Sym(ctxt *Link) { + ldr := ctxt.loader + + // Add special runtime.text and runtime.etext symbols. + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + putplan9sym(ctxt, ldr, s, TextSym) + } + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + putplan9sym(ctxt, ldr, s, TextSym) + } + + // Add text symbols. + for _, s := range ctxt.Textp { + putplan9sym(ctxt, ldr, s, TextSym) + } + + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + name := ldr.RawSymName(s) // TODO: try not to read the name + if name == "" || name[0] == '.' { + return false + } + return true + } + + // Add data symbols and external references. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + t := ldr.SymType(s) + if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata + if t == sym.STLSBSS { + continue + } + if !shouldBeInSymbolTable(s) { + continue + } + char := DataSym + if t == sym.SBSS || t == sym.SNOPTRBSS { + char = BSSSym + } + putplan9sym(ctxt, ldr, s, char) + } + } +} + +type byPkg []*sym.Library + +func (libs byPkg) Len() int { + return len(libs) +} + +func (libs byPkg) Less(a, b int) bool { + return libs[a].Pkg < libs[b].Pkg +} + +func (libs byPkg) Swap(a, b int) { + libs[a], libs[b] = libs[b], libs[a] +} + +// Create a table with information on the text sections. +// Return the symbol of the table, and number of sections. +func textsectionmap(ctxt *Link) (loader.Sym, uint32) { + ldr := ctxt.loader + t := ldr.CreateSymForUpdate("runtime.textsectionmap", 0) + t.SetType(sym.SRODATA) + nsections := int64(0) + + for _, sect := range Segtext.Sections { + if sect.Name == ".text" { + nsections++ + } else { + break + } + } + t.Grow(3 * nsections * int64(ctxt.Arch.PtrSize)) + + off := int64(0) + n := 0 + + // The vaddr for each text section is the difference between the section's + // Vaddr and the Vaddr for the first text section as determined at compile + // time. + + // The symbol for the first text section is named runtime.text as before. + // Additional text sections are named runtime.text.n where n is the + // order of creation starting with 1. These symbols provide the section's + // address after relocation by the linker. + + textbase := Segtext.Sections[0].Vaddr + for _, sect := range Segtext.Sections { + if sect.Name != ".text" { + break + } + off = t.SetUint(ctxt.Arch, off, sect.Vaddr-textbase) + off = t.SetUint(ctxt.Arch, off, sect.Length) + if n == 0 { + s := ldr.Lookup("runtime.text", 0) + if s == 0 { + ctxt.Errorf(s, "Unable to find symbol runtime.text\n") + } + off = t.SetAddr(ctxt.Arch, off, s) + + } else { + s := ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0) + if s == 0 { + ctxt.Errorf(s, "Unable to find symbol runtime.text.%d\n", n) + } + off = t.SetAddr(ctxt.Arch, off, s) + } + n++ + } + return t.Sym(), uint32(n) +} + +func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { + ldr := ctxt.loader + + if !ctxt.IsAIX() { + switch ctxt.BuildMode { + case BuildModeCArchive, BuildModeCShared: + s := ldr.Lookup(*flagEntrySymbol, sym.SymVerABI0) + if s != 0 { + addinitarrdata(ctxt, ldr, s) + } + } + } + + // Define these so that they'll get put into the symbol table. + // data.c:/^address will provide the actual values. + ctxt.xdefine("runtime.rodata", sym.SRODATA, 0) + ctxt.xdefine("runtime.erodata", sym.SRODATA, 0) + ctxt.xdefine("runtime.types", sym.SRODATA, 0) + ctxt.xdefine("runtime.etypes", sym.SRODATA, 0) + ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, 0) + ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, 0) + ctxt.xdefine("runtime.data", sym.SDATA, 0) + ctxt.xdefine("runtime.edata", sym.SDATA, 0) + ctxt.xdefine("runtime.bss", sym.SBSS, 0) + ctxt.xdefine("runtime.ebss", sym.SBSS, 0) + ctxt.xdefine("runtime.noptrbss", sym.SNOPTRBSS, 0) + ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, 0) + ctxt.xdefine("runtime.end", sym.SBSS, 0) + ctxt.xdefine("runtime.epclntab", sym.SRODATA, 0) + ctxt.xdefine("runtime.esymtab", sym.SRODATA, 0) + + // garbage collection symbols + s := ldr.CreateSymForUpdate("runtime.gcdata", 0) + s.SetType(sym.SRODATA) + s.SetSize(0) + ctxt.xdefine("runtime.egcdata", sym.SRODATA, 0) + + s = ldr.CreateSymForUpdate("runtime.gcbss", 0) + s.SetType(sym.SRODATA) + s.SetSize(0) + ctxt.xdefine("runtime.egcbss", sym.SRODATA, 0) + + // pseudo-symbols to mark locations of type, string, and go string data. + var symtype, symtyperel loader.Sym + if !ctxt.DynlinkingGo() { + if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) { + s = ldr.CreateSymForUpdate("type.*", 0) + s.SetType(sym.STYPE) + s.SetSize(0) + symtype = s.Sym() + + s = ldr.CreateSymForUpdate("typerel.*", 0) + s.SetType(sym.STYPERELRO) + s.SetSize(0) + symtyperel = s.Sym() + } else { + s = ldr.CreateSymForUpdate("type.*", 0) + s.SetType(sym.STYPE) + s.SetSize(0) + symtype = s.Sym() + symtyperel = s.Sym() + } + setCarrierSym(sym.STYPE, symtype) + setCarrierSym(sym.STYPERELRO, symtyperel) + } + + groupSym := func(name string, t sym.SymKind) loader.Sym { + s := ldr.CreateSymForUpdate(name, 0) + s.SetType(t) + s.SetSize(0) + s.SetLocal(true) + setCarrierSym(t, s.Sym()) + return s.Sym() + } + var ( + symgostring = groupSym("go.string.*", sym.SGOSTRING) + symgofunc = groupSym("go.func.*", sym.SGOFUNC) + symgcbits = groupSym("runtime.gcbits.*", sym.SGCBITS) + ) + + var symgofuncrel loader.Sym + if !ctxt.DynlinkingGo() { + if ctxt.UseRelro() { + symgofuncrel = groupSym("go.funcrel.*", sym.SGOFUNCRELRO) + } else { + symgofuncrel = symgofunc + } + } + + symt := ldr.CreateSymForUpdate("runtime.symtab", 0) + symt.SetType(sym.SSYMTAB) + symt.SetSize(0) + symt.SetLocal(true) + + // assign specific types so that they sort together. + // within a type they sort by size, so the .* symbols + // just defined above will be first. + // hide the specific symbols. + nsym := loader.Sym(ldr.NSym()) + symGroupType := make([]sym.SymKind, nsym) + for s := loader.Sym(1); s < nsym; s++ { + if !ctxt.IsExternal() && ldr.IsFileLocal(s) && !ldr.IsFromAssembly(s) && ldr.SymPkg(s) != "" { + ldr.SetAttrNotInSymbolTable(s, true) + } + if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || (ldr.SymType(s) != sym.SRODATA && ldr.SymType(s) != sym.SGOFUNC) { + continue + } + + name := ldr.SymName(s) + switch { + case strings.HasPrefix(name, "type."): + if !ctxt.DynlinkingGo() { + ldr.SetAttrNotInSymbolTable(s, true) + } + if ctxt.UseRelro() { + symGroupType[s] = sym.STYPERELRO + if symtyperel != 0 { + ldr.SetCarrierSym(s, symtyperel) + } + } else { + symGroupType[s] = sym.STYPE + if symtyperel != 0 { + ldr.SetCarrierSym(s, symtype) + } + } + + case strings.HasPrefix(name, "go.importpath.") && ctxt.UseRelro(): + // Keep go.importpath symbols in the same section as types and + // names, as they can be referred to by a section offset. + symGroupType[s] = sym.STYPERELRO + + case strings.HasPrefix(name, "go.string."): + symGroupType[s] = sym.SGOSTRING + ldr.SetAttrNotInSymbolTable(s, true) + ldr.SetCarrierSym(s, symgostring) + + case strings.HasPrefix(name, "runtime.gcbits."): + symGroupType[s] = sym.SGCBITS + ldr.SetAttrNotInSymbolTable(s, true) + ldr.SetCarrierSym(s, symgcbits) + + case strings.HasSuffix(name, "·f"): + if !ctxt.DynlinkingGo() { + ldr.SetAttrNotInSymbolTable(s, true) + } + if ctxt.UseRelro() { + symGroupType[s] = sym.SGOFUNCRELRO + if symgofuncrel != 0 { + ldr.SetCarrierSym(s, symgofuncrel) + } + } else { + symGroupType[s] = sym.SGOFUNC + ldr.SetCarrierSym(s, symgofunc) + } + + case strings.HasPrefix(name, "gcargs."), + strings.HasPrefix(name, "gclocals."), + strings.HasPrefix(name, "gclocals·"), + ldr.SymType(s) == sym.SGOFUNC && s != symgofunc, + strings.HasSuffix(name, ".opendefer"): + symGroupType[s] = sym.SGOFUNC + ldr.SetAttrNotInSymbolTable(s, true) + ldr.SetCarrierSym(s, symgofunc) + 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) + } + } + + if ctxt.BuildMode == BuildModeShared { + abihashgostr := ldr.CreateSymForUpdate("go.link.abihash."+filepath.Base(*flagOutfile), 0) + abihashgostr.SetType(sym.SRODATA) + hashsym := ldr.LookupOrCreateSym("go.link.abihashbytes", 0) + abihashgostr.AddAddr(ctxt.Arch, hashsym) + abihashgostr.AddUint(ctxt.Arch, uint64(ldr.SymSize(hashsym))) + } + if ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() { + for _, l := range ctxt.Library { + s := ldr.CreateSymForUpdate("go.link.pkghashbytes."+l.Pkg, 0) + s.SetType(sym.SRODATA) + s.SetSize(int64(len(l.Fingerprint))) + s.SetData(l.Fingerprint[:]) + str := ldr.CreateSymForUpdate("go.link.pkghash."+l.Pkg, 0) + str.SetType(sym.SRODATA) + str.AddAddr(ctxt.Arch, s.Sym()) + str.AddUint(ctxt.Arch, uint64(len(l.Fingerprint))) + } + } + + textsectionmapSym, nsections := textsectionmap(ctxt) + + // Information about the layout of the executable image for the + // runtime to use. Any changes here must be matched by changes to + // the definition of moduledata in runtime/symtab.go. + // This code uses several global variables that are set by pcln.go:pclntab. + moduledata := ldr.MakeSymbolUpdater(ctxt.Moduledata) + // The pcHeader + moduledata.AddAddr(ctxt.Arch, pcln.pcheader) + // The function name slice + moduledata.AddAddr(ctxt.Arch, pcln.funcnametab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab))) + // The cutab slice + moduledata.AddAddr(ctxt.Arch, pcln.cutab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab))) + // The filetab slice + moduledata.AddAddr(ctxt.Arch, pcln.filetab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab))) + // The pctab slice + moduledata.AddAddr(ctxt.Arch, pcln.pctab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab))) + // The pclntab slice + moduledata.AddAddr(ctxt.Arch, pcln.pclntab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab))) + // The ftab slice + moduledata.AddAddr(ctxt.Arch, pcln.pclntab) + moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) + moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) + // findfunctab + moduledata.AddAddr(ctxt.Arch, pcln.findfunctab) + // minpc, maxpc + moduledata.AddAddr(ctxt.Arch, pcln.firstFunc) + moduledata.AddAddrPlus(ctxt.Arch, pcln.lastFunc, ldr.SymSize(pcln.lastFunc)) + // pointers to specific parts of the module + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.text", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etext", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.noptrdata", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.enoptrdata", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.data", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.edata", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.bss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.ebss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.noptrbss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.enoptrbss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.end", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.gcdata", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.gcbss", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.types", 0)) + moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etypes", 0)) + + if ctxt.IsAIX() && ctxt.IsExternal() { + // Add R_XCOFFREF relocation to prevent ld's garbage collection of + // runtime.rodata, runtime.erodata and runtime.epclntab. + addRef := func(name string) { + r, _ := moduledata.AddRel(objabi.R_XCOFFREF) + r.SetSym(ldr.Lookup(name, 0)) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + } + addRef("runtime.rodata") + addRef("runtime.erodata") + addRef("runtime.epclntab") + } + + // text section information + moduledata.AddAddr(ctxt.Arch, textsectionmapSym) + moduledata.AddUint(ctxt.Arch, uint64(nsections)) + moduledata.AddUint(ctxt.Arch, uint64(nsections)) + + // The typelinks slice + typelinkSym := ldr.Lookup("runtime.typelink", 0) + ntypelinks := uint64(ldr.SymSize(typelinkSym)) / 4 + moduledata.AddAddr(ctxt.Arch, typelinkSym) + moduledata.AddUint(ctxt.Arch, ntypelinks) + moduledata.AddUint(ctxt.Arch, ntypelinks) + // The itablinks slice + itablinkSym := ldr.Lookup("runtime.itablink", 0) + nitablinks := uint64(ldr.SymSize(itablinkSym)) / uint64(ctxt.Arch.PtrSize) + moduledata.AddAddr(ctxt.Arch, itablinkSym) + moduledata.AddUint(ctxt.Arch, nitablinks) + moduledata.AddUint(ctxt.Arch, nitablinks) + // The ptab slice + if ptab := ldr.Lookup("go.plugin.tabs", 0); ptab != 0 && ldr.AttrReachable(ptab) { + ldr.SetAttrLocal(ptab, true) + if ldr.SymType(ptab) != sym.SRODATA { + panic(fmt.Sprintf("go.plugin.tabs is %v, not SRODATA", ldr.SymType(ptab))) + } + nentries := uint64(len(ldr.Data(ptab)) / 8) // sizeof(nameOff) + sizeof(typeOff) + moduledata.AddAddr(ctxt.Arch, ptab) + moduledata.AddUint(ctxt.Arch, nentries) + moduledata.AddUint(ctxt.Arch, nentries) + } else { + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + } + if ctxt.BuildMode == BuildModePlugin { + addgostring(ctxt, ldr, moduledata, "go.link.thispluginpath", objabi.PathToPrefix(*flagPluginPath)) + + pkghashes := ldr.CreateSymForUpdate("go.link.pkghashes", 0) + pkghashes.SetLocal(true) + pkghashes.SetType(sym.SRODATA) + + for i, l := range ctxt.Library { + // pkghashes[i].name + addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go.link.pkgname.%d", i), l.Pkg) + // pkghashes[i].linktimehash + addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go.link.pkglinkhash.%d", i), string(l.Fingerprint[:])) + // pkghashes[i].runtimehash + hash := ldr.Lookup("go.link.pkghash."+l.Pkg, 0) + pkghashes.AddAddr(ctxt.Arch, hash) + } + moduledata.AddAddr(ctxt.Arch, pkghashes.Sym()) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library))) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library))) + } else { + moduledata.AddUint(ctxt.Arch, 0) // pluginpath + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) // pkghashes slice + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + } + if len(ctxt.Shlibs) > 0 { + thismodulename := filepath.Base(*flagOutfile) + switch ctxt.BuildMode { + case BuildModeExe, BuildModePIE: + // When linking an executable, outfile is just "a.out". Make + // it something slightly more comprehensible. + thismodulename = "the executable" + } + addgostring(ctxt, ldr, moduledata, "go.link.thismodulename", thismodulename) + + modulehashes := ldr.CreateSymForUpdate("go.link.abihashes", 0) + modulehashes.SetLocal(true) + modulehashes.SetType(sym.SRODATA) + + for i, shlib := range ctxt.Shlibs { + // modulehashes[i].modulename + modulename := filepath.Base(shlib.Path) + addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go.link.libname.%d", i), modulename) + + // modulehashes[i].linktimehash + addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go.link.linkhash.%d", i), string(shlib.Hash)) + + // modulehashes[i].runtimehash + abihash := ldr.LookupOrCreateSym("go.link.abihash."+modulename, 0) + ldr.SetAttrReachable(abihash, true) + modulehashes.AddAddr(ctxt.Arch, abihash) + } + + moduledata.AddAddr(ctxt.Arch, modulehashes.Sym()) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs))) + moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs))) + } else { + moduledata.AddUint(ctxt.Arch, 0) // modulename + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) // moduleshashes slice + moduledata.AddUint(ctxt.Arch, 0) + moduledata.AddUint(ctxt.Arch, 0) + } + + hasmain := ctxt.BuildMode == BuildModeExe || ctxt.BuildMode == BuildModePIE + if hasmain { + moduledata.AddUint8(1) + } else { + moduledata.AddUint8(0) + } + + // The rest of moduledata is zero initialized. + // When linking an object that does not contain the runtime we are + // creating the moduledata from scratch and it does not have a + // compiler-provided size, so read it from the type data. + moduledatatype := ldr.Lookup("type.runtime.moduledata", 0) + moduledata.SetSize(decodetypeSize(ctxt.Arch, ldr.Data(moduledatatype))) + moduledata.Grow(moduledata.Size()) + + lastmoduledatap := ldr.CreateSymForUpdate("runtime.lastmoduledatap", 0) + if lastmoduledatap.Type() != sym.SDYNIMPORT { + lastmoduledatap.SetType(sym.SNOPTRDATA) + lastmoduledatap.SetSize(0) // overwrite existing value + lastmoduledatap.SetData(nil) + lastmoduledatap.AddAddr(ctxt.Arch, moduledata.Sym()) + } + return symGroupType +} + +// CarrierSymByType tracks carrier symbols and their sizes. +var CarrierSymByType [sym.SXREF]struct { + Sym loader.Sym + Size int64 +} + +func setCarrierSym(typ sym.SymKind, s loader.Sym) { + if CarrierSymByType[typ].Sym != 0 { + panic(fmt.Sprintf("carrier symbol for type %v already set", typ)) + } + CarrierSymByType[typ].Sym = s +} + +func setCarrierSize(typ sym.SymKind, sz int64) { + if CarrierSymByType[typ].Size != 0 { + panic(fmt.Sprintf("carrier symbol size for type %v already set", typ)) + } + CarrierSymByType[typ].Size = sz +} + +func isStaticTmp(name string) bool { + return strings.Contains(name, "."+obj.StaticNamePref) +} diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go new file mode 100644 index 0000000..f68de8f --- /dev/null +++ b/src/cmd/link/internal/ld/target.go @@ -0,0 +1,187 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "encoding/binary" +) + +// Target holds the configuration we're building for. +type Target struct { + Arch *sys.Arch + + HeadType objabi.HeadType + + LinkMode LinkMode + BuildMode BuildMode + + linkShared bool + canUsePlugins bool + IsELF bool +} + +// +// Target type functions +// + +func (t *Target) IsExe() bool { + return t.BuildMode == BuildModeExe +} + +func (t *Target) IsShared() bool { + return t.BuildMode == BuildModeShared +} + +func (t *Target) IsPlugin() bool { + return t.BuildMode == BuildModePlugin +} + +func (t *Target) IsInternal() bool { + return t.LinkMode == LinkInternal +} + +func (t *Target) IsExternal() bool { + return t.LinkMode == LinkExternal +} + +func (t *Target) IsPIE() bool { + return t.BuildMode == BuildModePIE +} + +func (t *Target) IsSharedGoLink() bool { + return t.linkShared +} + +func (t *Target) CanUsePlugins() bool { + return t.canUsePlugins +} + +func (t *Target) IsElf() bool { + t.mustSetHeadType() + return t.IsELF +} + +func (t *Target) IsDynlinkingGo() bool { + return t.IsShared() || t.IsSharedGoLink() || t.IsPlugin() || t.CanUsePlugins() +} + +// UseRelro reports whether to make use of "read only relocations" aka +// relro. +func (t *Target) UseRelro() bool { + switch t.BuildMode { + case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin: + return t.IsELF || t.HeadType == objabi.Haix || t.HeadType == objabi.Hdarwin + default: + if t.HeadType == objabi.Hdarwin && t.IsARM64() { + // On darwin/ARM64, everything is PIE. + return true + } + return t.linkShared || (t.HeadType == objabi.Haix && t.LinkMode == LinkExternal) + } +} + +// +// Processor functions +// + +func (t *Target) Is386() bool { + return t.Arch.Family == sys.I386 +} + +func (t *Target) IsARM() bool { + return t.Arch.Family == sys.ARM +} + +func (t *Target) IsARM64() bool { + return t.Arch.Family == sys.ARM64 +} + +func (t *Target) IsAMD64() bool { + return t.Arch.Family == sys.AMD64 +} + +func (t *Target) IsMIPS() bool { + return t.Arch.Family == sys.MIPS +} + +func (t *Target) IsMIPS64() bool { + return t.Arch.Family == sys.MIPS64 +} + +func (t *Target) IsPPC64() bool { + return t.Arch.Family == sys.PPC64 +} + +func (t *Target) IsRISCV64() bool { + return t.Arch.Family == sys.RISCV64 +} + +func (t *Target) IsS390X() bool { + return t.Arch.Family == sys.S390X +} + +func (t *Target) IsWasm() bool { + return t.Arch.Family == sys.Wasm +} + +// +// OS Functions +// + +func (t *Target) IsLinux() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hlinux +} + +func (t *Target) IsDarwin() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hdarwin +} + +func (t *Target) IsWindows() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hwindows +} + +func (t *Target) IsPlan9() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hplan9 +} + +func (t *Target) IsAIX() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Haix +} + +func (t *Target) IsSolaris() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hsolaris +} + +func (t *Target) IsNetbsd() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hnetbsd +} + +func (t *Target) IsOpenbsd() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hopenbsd +} + +func (t *Target) mustSetHeadType() { + if t.HeadType == objabi.Hunknown { + panic("HeadType is not set") + } +} + +// +// MISC +// + +func (t *Target) IsBigEndian() bool { + return t.Arch.ByteOrder == binary.BigEndian +} diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go new file mode 100644 index 0000000..32a24cf --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that a method of a reachable type is not necessarily +// live even if it matches an interface method, as long as +// the type is never converted to an interface. + +package main + +type I interface{ M() } + +type T int + +func (T) M() { println("XXX") } + +var p *T +var e interface{} + +func main() { + p = new(T) // used T, but never converted to interface in any reachable code + e.(I).M() // used I and I.M +} + +func Unused() { // convert T to interface, but this function is not reachable + var i I = T(0) + i.M() +} + +var Unused2 interface{} = T(1) // convert T to interface, in an unreachable global initializer diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go new file mode 100644 index 0000000..48ba55d --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod2.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that a method *is* live if it matches an interface +// method and the type is "indirectly" converted to an +// interface through reflection. + +package main + +import "reflect" + +type I interface{ M() } + +type T int + +func (T) M() { println("XXX") } + +func main() { + e := reflect.ValueOf([]T{1}).Index(0).Interface() + e.(I).M() +} diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go new file mode 100644 index 0000000..9a8dfbc --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod3.go @@ -0,0 +1,29 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Like ifacemethod2.go, this tests that a method *is* live +// if the type is "indirectly" converted to an interface +// using reflection with a method descriptor as intermediate. + +package main + +import "reflect" + +type S int + +func (s S) M() { println("S.M") } + +type I interface { M() } + +type T float64 + +func (t T) F(s S) {} + +func main() { + var t T + ft := reflect.TypeOf(t).Method(0).Type + at := ft.In(1) + v := reflect.New(at).Elem() + v.Interface().(I).M() +} diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod4.go new file mode 100644 index 0000000..52ee2e3 --- /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/deadcode/reflectcall.go b/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go new file mode 100644 index 0000000..af95e46 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go @@ -0,0 +1,24 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This example uses reflect.Value.Call, but not +// reflect.{Value,Type}.Method. This should not +// need to bring all methods live. + +package main + +import "reflect" + +func f() { println("call") } + +type T int + +func (T) M() {} + +func main() { + v := reflect.ValueOf(f) + v.Call(nil) + i := interface{}(T(1)) + println(i) +} diff --git a/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go b/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go new file mode 100644 index 0000000..8246093 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/deadcode/typedesc.go @@ -0,0 +1,16 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that a live variable doesn't bring its type +// descriptor live. + +package main + +type T [10]string + +var t T + +func main() { + println(t[8]) +} diff --git a/src/cmd/link/internal/ld/testdata/httptest/main/main.go b/src/cmd/link/internal/ld/testdata/httptest/main/main.go new file mode 100644 index 0000000..1bce301 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/httptest/main/main.go @@ -0,0 +1,22 @@ +// A small test program that uses the net/http package. There is +// nothing special about net/http here, this is just a convenient way +// to pull in a lot of code. + +package main + +import ( + "net/http" + "net/http/httptest" +) + +type statusHandler int + +func (h *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(int(*h)) +} + +func main() { + status := statusHandler(http.StatusNotFound) + s := httptest.NewServer(&status) + defer s.Close() +} diff --git a/src/cmd/link/internal/ld/testdata/issue10978/main.go b/src/cmd/link/internal/ld/testdata/issue10978/main.go new file mode 100644 index 0000000..5e8c097 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue10978/main.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func undefined() + +func defined1() int { + // To check multiple errors for a single symbol, + // reference undefined more than once. + undefined() + undefined() + return 0 +} + +func defined2() { + undefined() + undefined() +} + +func init() { + _ = defined1() + defined2() +} + +// The "main" function remains undeclared. diff --git a/src/cmd/link/internal/ld/testdata/issue10978/main.s b/src/cmd/link/internal/ld/testdata/issue10978/main.s new file mode 100644 index 0000000..1d00e76 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue10978/main.s @@ -0,0 +1 @@ +// This file is needed to make "go build" work for package with external functions.
diff --git a/src/cmd/link/internal/ld/testdata/issue25459/a/a.go b/src/cmd/link/internal/ld/testdata/issue25459/a/a.go new file mode 100644 index 0000000..6032d76 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue25459/a/a.go @@ -0,0 +1,27 @@ +package a + +const Always = true + +var Count int + +type FuncReturningInt func() int + +var PointerToConstIf FuncReturningInt + +func ConstIf() int { + if Always { + return 1 + } + var imdead [4]int + imdead[Count] = 1 + return imdead[0] +} + +func CallConstIf() int { + Count += 3 + return ConstIf() +} + +func Another() { + defer func() { PointerToConstIf = ConstIf; Count += 1 }() +} diff --git a/src/cmd/link/internal/ld/testdata/issue25459/main/main.go b/src/cmd/link/internal/ld/testdata/issue25459/main/main.go new file mode 100644 index 0000000..7b5796d --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue25459/main/main.go @@ -0,0 +1,10 @@ +package main + +import "cmd/link/internal/ld/testdata/issue25459/a" + +var Glob int + +func main() { + a.Another() + Glob += a.ConstIf() + a.CallConstIf() +} diff --git a/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go b/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go new file mode 100644 index 0000000..ca57749 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue26237/b.dir/b.go @@ -0,0 +1,16 @@ +package b + +var q int + +func Top(x int) int { + q += 1 + if q != x { + return 3 + } + return 4 +} + +func OOO(x int) int { + defer func() { q += x & 7 }() + return Top(x + 1) +} diff --git a/src/cmd/link/internal/ld/testdata/issue26237/main/main.go b/src/cmd/link/internal/ld/testdata/issue26237/main/main.go new file mode 100644 index 0000000..fdb1223 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue26237/main/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + + b "cmd/link/internal/ld/testdata/issue26237/b.dir" +) + +var skyx int + +func main() { + skyx += b.OOO(skyx) + if b.Top(1) == 99 { + fmt.Printf("Beware the Jabberwock, my son!\n") + } +} diff --git a/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m b/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m new file mode 100644 index 0000000..9462788 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue32233/lib/ObjC.m @@ -0,0 +1,16 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#import <Foundation/Foundation.h> +#import <AppKit/NSAppearance.h> + +BOOL function(void) { +#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED > 101300) + NSAppearance *darkAppearance; + if (@available(macOS 10.14, *)) { + darkAppearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; + } +#endif + return NO; +} diff --git a/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go b/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go new file mode 100644 index 0000000..514b9b9 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue32233/lib/lib.go @@ -0,0 +1,19 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lib + +/* +#cgo darwin CFLAGS: -D__MAC_OS_X_VERSION_MAX_ALLOWED=101450 +#cgo darwin LDFLAGS: -framework Foundation -framework AppKit +#include "stdlib.h" +int function(void); +*/ +import "C" +import "fmt" + +func DoC() { + C.function() + fmt.Println("called c function") +} diff --git a/src/cmd/link/internal/ld/testdata/issue32233/main/main.go b/src/cmd/link/internal/ld/testdata/issue32233/main/main.go new file mode 100644 index 0000000..ed0838a --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue32233/main/main.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "cmd/link/internal/ld/testdata/issue32233/lib" + +func main() { + lib.DoC() +} diff --git a/src/cmd/link/internal/ld/testdata/issue38192/main.go b/src/cmd/link/internal/ld/testdata/issue38192/main.go new file mode 100644 index 0000000..3b7df60 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue38192/main.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func singleInstruction() + +func main() { + singleInstruction() +} diff --git a/src/cmd/link/internal/ld/testdata/issue38192/oneline.s b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s new file mode 100644 index 0000000..f5290d7 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s @@ -0,0 +1,8 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·singleInstruction(SB),NOSPLIT,$0 + RET diff --git a/src/cmd/link/internal/ld/testdata/issue39256/x.go b/src/cmd/link/internal/ld/testdata/issue39256/x.go new file mode 100644 index 0000000..97bc1cc --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue39256/x.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + _ "unsafe" +) + +//go:cgo_import_dynamic libc_getpid getpid "libc.so" +//go:cgo_import_dynamic libc_kill kill "libc.so" +//go:cgo_import_dynamic libc_close close "libc.so" +//go:cgo_import_dynamic libc_open open "libc.so" + +//go:cgo_import_dynamic _ _ "libc.so" + +func trampoline() + +func main() { + trampoline() +} diff --git a/src/cmd/link/internal/ld/testdata/issue39256/x.s b/src/cmd/link/internal/ld/testdata/issue39256/x.s new file mode 100644 index 0000000..41a54b2 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue39256/x.s @@ -0,0 +1,10 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +TEXT ·trampoline(SB),0,$0 + CALL libc_getpid(SB) + CALL libc_kill(SB) + CALL libc_open(SB) + CALL libc_close(SB) + RET diff --git a/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go new file mode 100644 index 0000000..76e0ea1 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go @@ -0,0 +1,15 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +var G int + +func main() { + if G != 101 { + println("not 101") + } else { + println("well now that's interesting") + } +} diff --git a/src/cmd/link/internal/ld/typelink.go b/src/cmd/link/internal/ld/typelink.go new file mode 100644 index 0000000..5eca6e0 --- /dev/null +++ b/src/cmd/link/internal/ld/typelink.go @@ -0,0 +1,72 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "sort" +) + +type byTypeStr []typelinkSortKey + +type typelinkSortKey struct { + TypeStr string + Type loader.Sym +} + +func (s byTypeStr) Less(i, j int) bool { return s[i].TypeStr < s[j].TypeStr } +func (s byTypeStr) Len() int { return len(s) } +func (s byTypeStr) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// typelink generates the typelink table which is used by reflect.typelinks(). +// Types that should be added to the typelinks table are marked with the +// MakeTypelink attribute by the compiler. +func (ctxt *Link) typelink() { + ldr := ctxt.loader + typelinks := byTypeStr{} + var itabs []loader.Sym + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + if ldr.IsTypelink(s) { + typelinks = append(typelinks, typelinkSortKey{decodetypeStr(ldr, ctxt.Arch, s), s}) + } else if ldr.IsItab(s) { + itabs = append(itabs, s) + } + } + sort.Sort(typelinks) + + tl := ldr.CreateSymForUpdate("runtime.typelink", 0) + tl.SetType(sym.STYPELINK) + ldr.SetAttrLocal(tl.Sym(), true) + tl.SetSize(int64(4 * len(typelinks))) + tl.Grow(tl.Size()) + relocs := tl.AddRelocs(len(typelinks)) + for i, s := range typelinks { + r := relocs.At(i) + r.SetSym(s.Type) + r.SetOff(int32(i * 4)) + r.SetSiz(4) + r.SetType(objabi.R_ADDROFF) + } + + ptrsize := ctxt.Arch.PtrSize + il := ldr.CreateSymForUpdate("runtime.itablink", 0) + il.SetType(sym.SITABLINK) + ldr.SetAttrLocal(il.Sym(), true) + il.SetSize(int64(ptrsize * len(itabs))) + il.Grow(il.Size()) + relocs = il.AddRelocs(len(itabs)) + for i, s := range itabs { + r := relocs.At(i) + r.SetSym(s) + r.SetOff(int32(i * ptrsize)) + r.SetSiz(uint8(ptrsize)) + r.SetType(objabi.R_ADDR) + } +} diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go new file mode 100644 index 0000000..9228ed1 --- /dev/null +++ b/src/cmd/link/internal/ld/util.go @@ -0,0 +1,114 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/link/internal/loader" + "encoding/binary" + "fmt" + "os" +) + +var atExitFuncs []func() + +func AtExit(f func()) { + atExitFuncs = append(atExitFuncs, f) +} + +// runAtExitFuncs runs the queued set of AtExit functions. +func runAtExitFuncs() { + for i := len(atExitFuncs) - 1; i >= 0; i-- { + atExitFuncs[i]() + } + atExitFuncs = nil +} + +// Exit exits with code after executing all atExitFuncs. +func Exit(code int) { + runAtExitFuncs() + os.Exit(code) +} + +// Exitf logs an error message then calls Exit(2). +func Exitf(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, os.Args[0]+": "+format+"\n", a...) + nerrors++ + Exit(2) +} + +// afterErrorAction updates 'nerrors' on error and invokes exit or +// panics in the proper circumstances. +func afterErrorAction() { + nerrors++ + if *flagH { + panic("error") + } + if nerrors > 20 { + Exitf("too many errors") + } +} + +// Errorf logs an error message. +// +// If more than 20 errors have been printed, exit with an error. +// +// Logging an error means that on exit cmd/link will delete any +// output file and return a non-zero error code. +// +// TODO: remove. Use ctxt.Errof instead. +// All remaining calls use nil as first arg. +func Errorf(dummy *int, format string, args ...interface{}) { + format += "\n" + fmt.Fprintf(os.Stderr, format, args...) + afterErrorAction() +} + +// Errorf method logs an error message. +// +// If more than 20 errors have been printed, exit with an error. +// +// Logging an error means that on exit cmd/link will delete any +// output file and return a non-zero error code. +func (ctxt *Link) Errorf(s loader.Sym, format string, args ...interface{}) { + if ctxt.loader != nil { + ctxt.loader.Errorf(s, format, args...) + return + } + // Note: this is not expected to happen very often. + format = fmt.Sprintf("sym %d: %s", s, format) + format += "\n" + fmt.Fprintf(os.Stderr, format, args...) + afterErrorAction() +} + +func artrim(x []byte) string { + i := 0 + j := len(x) + for i < len(x) && x[i] == ' ' { + i++ + } + for j > i && x[j-1] == ' ' { + j-- + } + return string(x[i:j]) +} + +func stringtouint32(x []uint32, s string) { + for i := 0; len(s) > 0; i++ { + var buf [4]byte + s = s[copy(buf[:], s):] + x[i] = binary.LittleEndian.Uint32(buf[:]) + } +} + +// contains reports whether v is in s. +func contains(s []string, v string) bool { + for _, x := range s { + if x == v { + return true + } + } + return false +} diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go new file mode 100644 index 0000000..ba818ea --- /dev/null +++ b/src/cmd/link/internal/ld/xcoff.go @@ -0,0 +1,1809 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "bytes" + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "encoding/binary" + "fmt" + "io/ioutil" + "math/bits" + "path/filepath" + "sort" + "strings" + "sync" +) + +// This file handles all algorithms related to XCOFF files generation. +// Most of them are adaptations of the ones in cmd/link/internal/pe.go +// as PE and XCOFF are based on COFF files. +// XCOFF files generated are 64 bits. + +const ( + // Total amount of space to reserve at the start of the file + // for File Header, Auxiliary Header, and Section Headers. + // May waste some. + XCOFFHDRRESERVE = FILHSZ_64 + AOUTHSZ_EXEC64 + SCNHSZ_64*23 + XCOFFSECTALIGN int64 = 32 // base on dump -o + + // XCOFF binaries should normally have all its sections position-independent. + // However, this is not yet possible for .text because of some R_ADDR relocations + // inside RODATA symbols. + // .data and .bss are position-independent so their address start inside a unreachable + // segment during execution to force segfault if something is wrong. + XCOFFTEXTBASE = 0x100000000 // Start of text address + XCOFFDATABASE = 0x200000000 // Start of data address +) + +// File Header +type XcoffFileHdr64 struct { + Fmagic uint16 // Target machine + Fnscns uint16 // Number of sections + Ftimedat int32 // Time and date of file creation + Fsymptr uint64 // Byte offset to symbol table start + Fopthdr uint16 // Number of bytes in optional header + Fflags uint16 // Flags + Fnsyms int32 // Number of entries in symbol table +} + +const ( + U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF +) + +// Flags that describe the type of the object file. +const ( + F_RELFLG = 0x0001 + F_EXEC = 0x0002 + F_LNNO = 0x0004 + F_FDPR_PROF = 0x0010 + F_FDPR_OPTI = 0x0020 + F_DSA = 0x0040 + F_VARPG = 0x0100 + F_DYNLOAD = 0x1000 + F_SHROBJ = 0x2000 + F_LOADONLY = 0x4000 +) + +// Auxiliary Header +type XcoffAoutHdr64 struct { + Omagic int16 // Flags - Ignored If Vstamp Is 1 + Ovstamp int16 // Version + Odebugger uint32 // Reserved For Debugger + Otextstart uint64 // Virtual Address Of Text + Odatastart uint64 // Virtual Address Of Data + Otoc uint64 // Toc Address + Osnentry int16 // Section Number For Entry Point + Osntext int16 // Section Number For Text + Osndata int16 // Section Number For Data + Osntoc int16 // Section Number For Toc + Osnloader int16 // Section Number For Loader + Osnbss int16 // Section Number For Bss + Oalgntext int16 // Max Text Alignment + Oalgndata int16 // Max Data Alignment + Omodtype [2]byte // Module Type Field + Ocpuflag uint8 // Bit Flags - Cputypes Of Objects + Ocputype uint8 // Reserved for CPU type + Otextpsize uint8 // Requested text page size + Odatapsize uint8 // Requested data page size + Ostackpsize uint8 // Requested stack page size + Oflags uint8 // Flags And TLS Alignment + Otsize uint64 // Text Size In Bytes + Odsize uint64 // Data Size In Bytes + Obsize uint64 // Bss Size In Bytes + Oentry uint64 // Entry Point Address + Omaxstack uint64 // Max Stack Size Allowed + Omaxdata uint64 // Max Data Size Allowed + Osntdata int16 // Section Number For Tdata Section + Osntbss int16 // Section Number For Tbss Section + Ox64flags uint16 // Additional Flags For 64-Bit Objects + Oresv3a int16 // Reserved + Oresv3 [2]int32 // Reserved +} + +// Section Header +type XcoffScnHdr64 struct { + Sname [8]byte // Section Name + Spaddr uint64 // Physical Address + Svaddr uint64 // Virtual Address + Ssize uint64 // Section Size + Sscnptr uint64 // File Offset To Raw Data + Srelptr uint64 // File Offset To Relocation + Slnnoptr uint64 // File Offset To Line Numbers + Snreloc uint32 // Number Of Relocation Entries + Snlnno uint32 // Number Of Line Number Entries + Sflags uint32 // flags +} + +// Flags defining the section type. +const ( + STYP_DWARF = 0x0010 + STYP_TEXT = 0x0020 + STYP_DATA = 0x0040 + STYP_BSS = 0x0080 + STYP_EXCEPT = 0x0100 + STYP_INFO = 0x0200 + STYP_TDATA = 0x0400 + STYP_TBSS = 0x0800 + STYP_LOADER = 0x1000 + STYP_DEBUG = 0x2000 + STYP_TYPCHK = 0x4000 + STYP_OVRFLO = 0x8000 +) +const ( + SSUBTYP_DWINFO = 0x10000 // DWARF info section + SSUBTYP_DWLINE = 0x20000 // DWARF line-number section + SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section + SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section + SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section + SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section + SSUBTYP_DWSTR = 0x70000 // DWARF strings section + SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section + SSUBTYP_DWLOC = 0x90000 // DWARF location lists section + SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section + SSUBTYP_DWMAC = 0xB0000 // DWARF macros section +) + +// Headers size +const ( + FILHSZ_32 = 20 + FILHSZ_64 = 24 + AOUTHSZ_EXEC32 = 72 + AOUTHSZ_EXEC64 = 120 + SCNHSZ_32 = 40 + SCNHSZ_64 = 72 + LDHDRSZ_32 = 32 + LDHDRSZ_64 = 56 + LDSYMSZ_64 = 24 + RELSZ_64 = 14 +) + +// Type representing all XCOFF symbols. +type xcoffSym interface { +} + +// Symbol Table Entry +type XcoffSymEnt64 struct { + Nvalue uint64 // Symbol value + Noffset uint32 // Offset of the name in string table or .debug section + Nscnum int16 // Section number of symbol + Ntype uint16 // Basic and derived type specification + Nsclass uint8 // Storage class of symbol + Nnumaux int8 // Number of auxiliary entries +} + +const SYMESZ = 18 + +const ( + // Nscnum + N_DEBUG = -2 + N_ABS = -1 + N_UNDEF = 0 + + //Ntype + SYM_V_INTERNAL = 0x1000 + SYM_V_HIDDEN = 0x2000 + SYM_V_PROTECTED = 0x3000 + SYM_V_EXPORTED = 0x4000 + SYM_TYPE_FUNC = 0x0020 // is function +) + +// Storage Class. +const ( + C_NULL = 0 // Symbol table entry marked for deletion + C_EXT = 2 // External symbol + C_STAT = 3 // Static symbol + C_BLOCK = 100 // Beginning or end of inner block + C_FCN = 101 // Beginning or end of function + C_FILE = 103 // Source file name and compiler information + C_HIDEXT = 107 // Unnamed external symbol + C_BINCL = 108 // Beginning of include file + C_EINCL = 109 // End of include file + C_WEAKEXT = 111 // Weak external symbol + C_DWARF = 112 // DWARF symbol + C_GSYM = 128 // Global variable + C_LSYM = 129 // Automatic variable allocated on stack + C_PSYM = 130 // Argument to subroutine allocated on stack + C_RSYM = 131 // Register variable + C_RPSYM = 132 // Argument to function or procedure stored in register + C_STSYM = 133 // Statically allocated symbol + C_BCOMM = 135 // Beginning of common block + C_ECOML = 136 // Local member of common block + C_ECOMM = 137 // End of common block + C_DECL = 140 // Declaration of object + C_ENTRY = 141 // Alternate entry + C_FUN = 142 // Function or procedure + C_BSTAT = 143 // Beginning of static block + C_ESTAT = 144 // End of static block + C_GTLS = 145 // Global thread-local variable + C_STTLS = 146 // Static thread-local variable +) + +// File Auxiliary Entry +type XcoffAuxFile64 struct { + Xzeroes uint32 // The name is always in the string table + Xoffset uint32 // Offset in the string table + X_pad1 [6]byte + Xftype uint8 // Source file string type + X_pad2 [2]byte + Xauxtype uint8 // Type of auxiliary entry +} + +// Function Auxiliary Entry +type XcoffAuxFcn64 struct { + Xlnnoptr uint64 // File pointer to line number + Xfsize uint32 // Size of function in bytes + Xendndx uint32 // Symbol table index of next entry + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +// csect Auxiliary Entry. +type XcoffAuxCSect64 struct { + Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index + Xparmhash uint32 // Offset of parameter type-check string + Xsnhash uint16 // .typchk section number + Xsmtyp uint8 // Symbol alignment and type + Xsmclas uint8 // Storage-mapping class + Xscnlenhi uint32 // Upper 4 bytes of length or symbol table index + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +// DWARF Auxiliary Entry +type XcoffAuxDWARF64 struct { + Xscnlen uint64 // Length of this symbol section + X_pad [9]byte + Xauxtype uint8 // Type of auxiliary entry +} + +// Auxiliary type +const ( + _AUX_EXCEPT = 255 + _AUX_FCN = 254 + _AUX_SYM = 253 + _AUX_FILE = 252 + _AUX_CSECT = 251 + _AUX_SECT = 250 +) + +// Xftype field +const ( + XFT_FN = 0 // Source File Name + XFT_CT = 1 // Compile Time Stamp + XFT_CV = 2 // Compiler Version Number + XFT_CD = 128 // Compiler Defined Information/ + +) + +// Symbol type field. +const ( + XTY_ER = 0 // External reference + XTY_SD = 1 // Section definition + XTY_LD = 2 // Label definition + XTY_CM = 3 // Common csect definition + XTY_WK = 0x8 // Weak symbol + XTY_EXP = 0x10 // Exported symbol + XTY_ENT = 0x20 // Entry point symbol + XTY_IMP = 0x40 // Imported symbol +) + +// Storage-mapping class. +const ( + XMC_PR = 0 // Program code + XMC_RO = 1 // Read-only constant + XMC_DB = 2 // Debug dictionary table + XMC_TC = 3 // TOC entry + XMC_UA = 4 // Unclassified + XMC_RW = 5 // Read/Write data + XMC_GL = 6 // Global linkage + XMC_XO = 7 // Extended operation + XMC_SV = 8 // 32-bit supervisor call descriptor + XMC_BS = 9 // BSS class + XMC_DS = 10 // Function descriptor + XMC_UC = 11 // Unnamed FORTRAN common + XMC_TC0 = 15 // TOC anchor + XMC_TD = 16 // Scalar data entry in the TOC + XMC_SV64 = 17 // 64-bit supervisor call descriptor + XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit + XMC_TL = 20 // Read/Write thread-local data + XMC_UL = 21 // Read/Write thread-local data (.tbss) + XMC_TE = 22 // TOC entry +) + +// Loader Header +type XcoffLdHdr64 struct { + Lversion int32 // Loader section version number + Lnsyms int32 // Number of symbol table entries + Lnreloc int32 // Number of relocation table entries + Listlen uint32 // Length of import file ID string table + Lnimpid int32 // Number of import file IDs + Lstlen uint32 // Length of string table + Limpoff uint64 // Offset to start of import file IDs + Lstoff uint64 // Offset to start of string table + Lsymoff uint64 // Offset to start of symbol table + Lrldoff uint64 // Offset to start of relocation entries +} + +// Loader Symbol +type XcoffLdSym64 struct { + Lvalue uint64 // Address field + Loffset uint32 // Byte offset into string table of symbol name + Lscnum int16 // Section number containing symbol + Lsmtype int8 // Symbol type, export, import flags + Lsmclas int8 // Symbol storage class + Lifile int32 // Import file ID; ordinal of import file IDs + Lparm uint32 // Parameter type-check field +} + +type xcoffLoaderSymbol struct { + sym loader.Sym + smtype int8 + smclas int8 +} + +type XcoffLdImportFile64 struct { + Limpidpath string + Limpidbase string + Limpidmem string +} + +type XcoffLdRel64 struct { + Lvaddr uint64 // Address Field + Lrtype uint16 // Relocation Size and Type + Lrsecnm int16 // Section Number being relocated + Lsymndx int32 // Loader-Section symbol table index +} + +// xcoffLoaderReloc holds information about a relocation made by the loader. +type xcoffLoaderReloc struct { + sym loader.Sym + roff int32 + rtype uint16 + symndx int32 +} + +const ( + XCOFF_R_POS = 0x00 // A(sym) Positive Relocation + XCOFF_R_NEG = 0x01 // -A(sym) Negative Relocation + XCOFF_R_REL = 0x02 // A(sym-*) Relative to self + XCOFF_R_TOC = 0x03 // A(sym-TOC) Relative to TOC + XCOFF_R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load. + + XCOFF_R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst + XCOFF_R_GL = 0x05 // A(external TOC of sym) Global Linkage + XCOFF_R_TCL = 0x06 // A(local TOC of sym) Local object TOC address + XCOFF_R_RL = 0x0C // A(sym) Pos indirect load. modifiable instruction + XCOFF_R_RLA = 0x0D // A(sym) Pos Load Address. modifiable instruction + XCOFF_R_REF = 0x0F // AL0(sym) Non relocating ref. No garbage collect + XCOFF_R_BA = 0x08 // A(sym) Branch absolute. Cannot modify instruction + XCOFF_R_RBA = 0x18 // A(sym) Branch absolute. modifiable instruction + XCOFF_R_BR = 0x0A // A(sym-*) Branch rel to self. non modifiable + XCOFF_R_RBR = 0x1A // A(sym-*) Branch rel to self. modifiable instr + + XCOFF_R_TLS = 0x20 // General-dynamic reference to TLS symbol + XCOFF_R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol + XCOFF_R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol + XCOFF_R_TLS_LE = 0x23 // Local-exec reference to TLS symbol + XCOFF_R_TLSM = 0x24 // Module reference to TLS symbol + XCOFF_R_TLSML = 0x25 // Module reference to local (own) module + + XCOFF_R_TOCU = 0x30 // Relative to TOC - high order bits + XCOFF_R_TOCL = 0x31 // Relative to TOC - low order bits +) + +type XcoffLdStr64 struct { + size uint16 + name string +} + +// xcoffFile is used to build XCOFF file. +type xcoffFile struct { + xfhdr XcoffFileHdr64 + xahdr XcoffAoutHdr64 + sections []*XcoffScnHdr64 + sectText *XcoffScnHdr64 + sectData *XcoffScnHdr64 + sectBss *XcoffScnHdr64 + stringTable xcoffStringTable + sectNameToScnum map[string]int16 + loaderSize uint64 + symtabOffset int64 // offset to the start of symbol table + symbolCount uint32 // number of symbol table records written + symtabSym []xcoffSym // XCOFF symbols for the symbol table + dynLibraries map[string]int // Dynamic libraries in .loader section. The integer represents its import file number (- 1) + loaderSymbols []*xcoffLoaderSymbol // symbols inside .loader symbol table + loaderReloc []*xcoffLoaderReloc // Reloc that must be made inside loader + sync.Mutex // currently protect loaderReloc +} + +// Var used by XCOFF Generation algorithms +var ( + xfile xcoffFile +) + +// xcoffStringTable is a XCOFF string table. +type xcoffStringTable struct { + strings []string + stringsLen int +} + +// size returns size of string table t. +func (t *xcoffStringTable) size() int { + // string table starts with 4-byte length at the beginning + return t.stringsLen + 4 +} + +// add adds string str to string table t. +func (t *xcoffStringTable) add(str string) int { + off := t.size() + t.strings = append(t.strings, str) + t.stringsLen += len(str) + 1 // each string will have 0 appended to it + return off +} + +// write writes string table t into the output file. +func (t *xcoffStringTable) write(out *OutBuf) { + out.Write32(uint32(t.size())) + for _, s := range t.strings { + out.WriteString(s) + out.Write8(0) + } +} + +// write writes XCOFF section sect into the output file. +func (sect *XcoffScnHdr64) write(ctxt *Link) { + binary.Write(ctxt.Out, binary.BigEndian, sect) + ctxt.Out.Write32(0) // Add 4 empty bytes at the end to match alignment +} + +// addSection adds section to the XCOFF file f. +func (f *xcoffFile) addSection(name string, addr uint64, size uint64, fileoff uint64, flags uint32) *XcoffScnHdr64 { + sect := &XcoffScnHdr64{ + Spaddr: addr, + Svaddr: addr, + Ssize: size, + Sscnptr: fileoff, + Sflags: flags, + } + copy(sect.Sname[:], name) // copy string to [8]byte + f.sections = append(f.sections, sect) + f.sectNameToScnum[name] = int16(len(f.sections)) + return sect +} + +// addDwarfSection adds a dwarf section to the XCOFF file f. +// This function is similar to addSection, but Dwarf section names +// must be modified to conventional names and they are various subtypes. +func (f *xcoffFile) addDwarfSection(s *sym.Section) *XcoffScnHdr64 { + newName, subtype := xcoffGetDwarfSubtype(s.Name) + return f.addSection(newName, 0, s.Length, s.Seg.Fileoff+s.Vaddr-s.Seg.Vaddr, STYP_DWARF|subtype) +} + +// xcoffGetDwarfSubtype returns the XCOFF name of the DWARF section str +// and its subtype constant. +func xcoffGetDwarfSubtype(str string) (string, uint32) { + switch str { + default: + Exitf("unknown DWARF section name for XCOFF: %s", str) + case ".debug_abbrev": + return ".dwabrev", SSUBTYP_DWABREV + case ".debug_info": + return ".dwinfo", SSUBTYP_DWINFO + case ".debug_frame": + return ".dwframe", SSUBTYP_DWFRAME + case ".debug_line": + return ".dwline", SSUBTYP_DWLINE + case ".debug_loc": + return ".dwloc", SSUBTYP_DWLOC + case ".debug_pubnames": + return ".dwpbnms", SSUBTYP_DWPBNMS + case ".debug_pubtypes": + return ".dwpbtyp", SSUBTYP_DWPBTYP + case ".debug_ranges": + return ".dwrnges", SSUBTYP_DWRNGES + } + // never used + return "", 0 +} + +// getXCOFFscnum returns the XCOFF section number of a Go section. +func (f *xcoffFile) getXCOFFscnum(sect *sym.Section) int16 { + switch sect.Seg { + case &Segtext: + return f.sectNameToScnum[".text"] + case &Segdata: + if sect.Name == ".noptrbss" || sect.Name == ".bss" { + return f.sectNameToScnum[".bss"] + } + if sect.Name == ".tbss" { + return f.sectNameToScnum[".tbss"] + } + return f.sectNameToScnum[".data"] + case &Segdwarf: + name, _ := xcoffGetDwarfSubtype(sect.Name) + return f.sectNameToScnum[name] + case &Segrelrodata: + return f.sectNameToScnum[".data"] + } + Errorf(nil, "getXCOFFscnum not implemented for section %s", sect.Name) + return -1 +} + +// Xcoffinit initialised some internal value and setups +// already known header information +func Xcoffinit(ctxt *Link) { + xfile.dynLibraries = make(map[string]int) + + HEADR = int32(Rnd(XCOFFHDRRESERVE, XCOFFSECTALIGN)) + if *FlagTextAddr != -1 { + Errorf(nil, "-T not available on AIX") + } + *FlagTextAddr = XCOFFTEXTBASE + int64(HEADR) + if *FlagRound != -1 { + Errorf(nil, "-R not available on AIX") + } + *FlagRound = int(XCOFFSECTALIGN) + +} + +// SYMBOL TABLE + +// type records C_FILE information needed for genasmsym in XCOFF. +type xcoffSymSrcFile struct { + name string + file *XcoffSymEnt64 // Symbol of this C_FILE + csectAux *XcoffAuxCSect64 // Symbol for the current .csect + csectSymNb uint64 // Symbol number for the current .csect + csectSize int64 +} + +var ( + currDwscnoff = make(map[string]uint64) // Needed to create C_DWARF symbols + currSymSrcFile xcoffSymSrcFile + outerSymSize = make(map[string]int64) +) + +// xcoffUpdateOuterSize stores the size of outer symbols in order to have it +// in the symbol table. +func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) { + if size == 0 { + return + } + // TODO: use CarrierSymByType + + ldr := ctxt.loader + switch stype { + default: + Errorf(nil, "unknown XCOFF outer symbol for type %s", stype.String()) + case sym.SRODATA, sym.SRODATARELRO, sym.SFUNCTAB, sym.SSTRING: + // Nothing to do + case sym.STYPERELRO: + if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) { + // runtime.types size must be removed, as it's a real symbol. + tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0)) + outerSymSize["typerel.*"] = size - tsize + return + } + fallthrough + case sym.STYPE: + if !ctxt.DynlinkingGo() { + // runtime.types size must be removed, as it's a real symbol. + tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0)) + outerSymSize["type.*"] = size - tsize + } + case sym.SGOSTRING: + outerSymSize["go.string.*"] = size + case sym.SGOFUNC: + if !ctxt.DynlinkingGo() { + outerSymSize["go.func.*"] = size + } + case sym.SGOFUNCRELRO: + outerSymSize["go.funcrel.*"] = size + case sym.SGCBITS: + outerSymSize["runtime.gcbits.*"] = size + case sym.SPCLNTAB: + outerSymSize["runtime.pclntab"] = size + } +} + +// addSymbol writes a symbol or an auxiliary symbol entry on ctxt.out. +func (f *xcoffFile) addSymbol(sym xcoffSym) { + f.symtabSym = append(f.symtabSym, sym) + f.symbolCount++ +} + +// xcoffAlign returns the log base 2 of the symbol's alignment. +func xcoffAlign(ldr *loader.Loader, x loader.Sym, t SymbolType) uint8 { + align := ldr.SymAlign(x) + if align == 0 { + if t == TextSym { + align = int32(Funcalign) + } else { + align = symalign(ldr, x) + } + } + return logBase2(int(align)) +} + +// logBase2 returns the log in base 2 of a. +func logBase2(a int) uint8 { + return uint8(bits.Len(uint(a)) - 1) +} + +// Write symbols needed when a new file appeared: +// - a C_FILE with one auxiliary entry for its name +// - C_DWARF symbols to provide debug information +// - a C_HIDEXT which will be a csect containing all of its functions +// It needs several parameters to create .csect symbols such as its entry point and its section number. +// +// Currently, a new file is in fact a new package. It seems to be OK, but it might change +// in the future. +func (f *xcoffFile) writeSymbolNewFile(ctxt *Link, name string, firstEntry uint64, extnum int16) { + ldr := ctxt.loader + /* C_FILE */ + s := &XcoffSymEnt64{ + Noffset: uint32(f.stringTable.add(".file")), + Nsclass: C_FILE, + Nscnum: N_DEBUG, + Ntype: 0, // Go isn't inside predefined language. + Nnumaux: 1, + } + f.addSymbol(s) + currSymSrcFile.file = s + + // Auxiliary entry for file name. + auxf := &XcoffAuxFile64{ + Xoffset: uint32(f.stringTable.add(name)), + Xftype: XFT_FN, + Xauxtype: _AUX_FILE, + } + f.addSymbol(auxf) + + /* Dwarf */ + for _, sect := range Segdwarf.Sections { + var dwsize uint64 + if ctxt.LinkMode == LinkInternal { + // Find the size of this corresponding package DWARF compilation unit. + // This size is set during DWARF generation (see dwarf.go). + dwsize = getDwsectCUSize(sect.Name, name) + // .debug_abbrev is common to all packages and not found with the previous function + if sect.Name == ".debug_abbrev" { + dwsize = uint64(ldr.SymSize(loader.Sym(sect.Sym))) + + } + } else { + // There is only one .FILE with external linking. + dwsize = sect.Length + } + + // get XCOFF name + name, _ := xcoffGetDwarfSubtype(sect.Name) + s := &XcoffSymEnt64{ + Nvalue: currDwscnoff[sect.Name], + Noffset: uint32(f.stringTable.add(name)), + Nsclass: C_DWARF, + Nscnum: f.getXCOFFscnum(sect), + Nnumaux: 1, + } + + if currSymSrcFile.csectAux == nil { + // Dwarf relocations need the symbol number of .dw* symbols. + // It doesn't need to know it for each package, one is enough. + // currSymSrcFile.csectAux == nil means first package. + ldr.SetSymDynid(loader.Sym(sect.Sym), int32(f.symbolCount)) + + if sect.Name == ".debug_frame" && ctxt.LinkMode != LinkExternal { + // CIE size must be added to the first package. + dwsize += 48 + } + } + + f.addSymbol(s) + + // update the DWARF section offset in this file + if sect.Name != ".debug_abbrev" { + currDwscnoff[sect.Name] += dwsize + } + + // Auxiliary dwarf section + auxd := &XcoffAuxDWARF64{ + Xscnlen: dwsize, + Xauxtype: _AUX_SECT, + } + + f.addSymbol(auxd) + } + + /* .csect */ + // Check if extnum is in text. + // This is temporary and only here to check if this algorithm is correct. + if extnum != 1 { + Exitf("XCOFF symtab: A new file was detected with its first symbol not in .text") + } + + currSymSrcFile.csectSymNb = uint64(f.symbolCount) + + // No offset because no name + s = &XcoffSymEnt64{ + Nvalue: firstEntry, + Nscnum: extnum, + Nsclass: C_HIDEXT, + Ntype: 0, // check visibility ? + Nnumaux: 1, + } + f.addSymbol(s) + + aux := &XcoffAuxCSect64{ + Xsmclas: XMC_PR, + Xsmtyp: XTY_SD | logBase2(Funcalign)<<3, + Xauxtype: _AUX_CSECT, + } + f.addSymbol(aux) + + currSymSrcFile.csectAux = aux + currSymSrcFile.csectSize = 0 +} + +// Update values for the previous package. +// - Svalue of the C_FILE symbol: if it is the last one, this Svalue must be -1 +// - Xsclen of the csect symbol. +func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) { + // first file + if currSymSrcFile.file == nil { + return + } + + // Update C_FILE + cfile := currSymSrcFile.file + if last { + cfile.Nvalue = 0xFFFFFFFFFFFFFFFF + } else { + cfile.Nvalue = uint64(f.symbolCount) + } + + // update csect scnlen in this auxiliary entry + aux := currSymSrcFile.csectAux + aux.Xscnlenlo = uint32(currSymSrcFile.csectSize & 0xFFFFFFFF) + aux.Xscnlenhi = uint32(currSymSrcFile.csectSize >> 32) +} + +// Write symbol representing a .text function. +// The symbol table is split with C_FILE corresponding to each package +// and not to each source file as it should be. +func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x loader.Sym) []xcoffSym { + // New XCOFF symbols which will be written. + syms := []xcoffSym{} + + // Check if a new file is detected. + ldr := ctxt.loader + name := ldr.SymName(x) + if strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") { + // Trampoline don't have a FILE so there are considered + // in the current file. + // Same goes for runtime.text.X symbols. + } else if ldr.SymPkg(x) == "" { // Undefined global symbol + // If this happens, the algorithm must be redone. + if currSymSrcFile.name != "" { + Exitf("undefined global symbol found inside another file") + } + } else { + // Current file has changed. New C_FILE, C_DWARF, etc must be generated. + if currSymSrcFile.name != ldr.SymPkg(x) { + if ctxt.LinkMode == LinkInternal { + // update previous file values + xfile.updatePreviousFile(ctxt, false) + currSymSrcFile.name = ldr.SymPkg(x) + f.writeSymbolNewFile(ctxt, ldr.SymPkg(x), uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x))) + } else { + // With external linking, ld will crash if there is several + // .FILE and DWARF debugging enable, somewhere during + // the relocation phase. + // Therefore, all packages are merged under a fake .FILE + // "go_functions". + // TODO(aix); remove once ld has been fixed or the triggering + // relocation has been found and fixed. + if currSymSrcFile.name == "" { + currSymSrcFile.name = ldr.SymPkg(x) + f.writeSymbolNewFile(ctxt, "go_functions", uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x))) + } + } + + } + } + + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(ldr.SymExtname(x))), + Nvalue: uint64(ldr.SymValue(x)), + Nscnum: f.getXCOFFscnum(ldr.SymSect(x)), + Ntype: SYM_TYPE_FUNC, + Nnumaux: 2, + } + + if ldr.SymVersion(x) != 0 || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) { + s.Nsclass = C_HIDEXT + } + + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + // Update current csect size + currSymSrcFile.csectSize += ldr.SymSize(x) + + // create auxiliary entries + a2 := &XcoffAuxFcn64{ + Xfsize: uint32(ldr.SymSize(x)), + Xlnnoptr: 0, // TODO + Xendndx: xfile.symbolCount + 3, // this symbol + 2 aux entries + Xauxtype: _AUX_FCN, + } + syms = append(syms, a2) + + a4 := &XcoffAuxCSect64{ + Xscnlenlo: uint32(currSymSrcFile.csectSymNb & 0xFFFFFFFF), + Xscnlenhi: uint32(currSymSrcFile.csectSymNb >> 32), + Xsmclas: XMC_PR, // Program Code + Xsmtyp: XTY_LD, // label definition (based on C) + Xauxtype: _AUX_CSECT, + } + a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3) + + syms = append(syms, a4) + return syms +} + +// put function used by genasmsym to write symbol table +func putaixsym(ctxt *Link, x loader.Sym, t SymbolType) { + // All XCOFF symbols generated by this GO symbols + // Can be a symbol entry or a auxiliary entry + syms := []xcoffSym{} + + ldr := ctxt.loader + name := ldr.SymName(x) + if t == UndefinedSym { + name = ldr.SymExtname(x) + } + + switch t { + default: + return + + case TextSym: + if ldr.SymPkg(x) != "" || strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") { + // Function within a file + syms = xfile.writeSymbolFunc(ctxt, x) + } else { + // Only runtime.text and runtime.etext come through this way + if name != "runtime.text" && name != "runtime.etext" && name != "go.buildid" { + Exitf("putaixsym: unknown text symbol %s", name) + } + s := &XcoffSymEnt64{ + Nsclass: C_HIDEXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nvalue: uint64(ldr.SymValue(x)), + Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)), + Ntype: SYM_TYPE_FUNC, + Nnumaux: 1, + } + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + size := uint64(ldr.SymSize(x)) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + Xsmclas: XMC_PR, + Xsmtyp: XTY_SD, + } + a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3) + syms = append(syms, a4) + } + + case DataSym, BSSSym: + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nvalue: uint64(ldr.SymValue(x)), + Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)), + Nnumaux: 1, + } + + if ldr.SymVersion(x) != 0 || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) { + // There is more symbols in the case of a global data + // which are related to the assembly generated + // to access such symbols. + // But as Golang as its own way to check if a symbol is + // global or local (the capital letter), we don't need to + // implement them yet. + s.Nsclass = C_HIDEXT + } + + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + // Create auxiliary entry + + // Normally, size should be the size of csect containing all + // the data and bss symbols of one file/package. + // However, it's easier to just have a csect for each symbol. + // It might change + size := uint64(ldr.SymSize(x)) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + } + + if ty := ldr.SymType(x); ty >= sym.STYPE && ty <= sym.SPCLNTAB { + if ctxt.IsExternal() && strings.HasPrefix(ldr.SymSect(x).Name, ".data.rel.ro") { + // During external linking, read-only datas with relocation + // must be in .data. + a4.Xsmclas = XMC_RW + } else { + // Read only data + a4.Xsmclas = XMC_RO + } + } else if /*ty == sym.SDATA &&*/ strings.HasPrefix(ldr.SymName(x), "TOC.") && ctxt.IsExternal() { + a4.Xsmclas = XMC_TC + } else if ldr.SymName(x) == "TOC" { + a4.Xsmclas = XMC_TC0 + } else { + a4.Xsmclas = XMC_RW + } + if t == DataSym { + a4.Xsmtyp |= XTY_SD + } else { + a4.Xsmtyp |= XTY_CM + } + + a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, t) << 3) + + syms = append(syms, a4) + + case UndefinedSym: + if ty := ldr.SymType(x); ty != sym.SDYNIMPORT && ty != sym.SHOSTOBJ && ty != sym.SUNDEFEXT { + return + } + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nnumaux: 1, + } + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xsmclas: XMC_DS, + Xsmtyp: XTY_ER | XTY_IMP, + } + + if ldr.SymName(x) == "__n_pthreads" { + // Currently, all imported symbols made by cgo_import_dynamic are + // syscall functions, except __n_pthreads which is a variable. + // TODO(aix): Find a way to detect variables imported by cgo. + a4.Xsmclas = XMC_RW + } + + syms = append(syms, a4) + + case TLSSym: + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)), + Nvalue: uint64(ldr.SymValue(x)), + Nnumaux: 1, + } + + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + size := uint64(ldr.SymSize(x)) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xsmclas: XMC_UL, + Xsmtyp: XTY_CM, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + } + + syms = append(syms, a4) + } + + for _, s := range syms { + xfile.addSymbol(s) + } +} + +// Generate XCOFF Symbol table. +// It will be written in out file in Asmbxcoff, because it must be +// at the very end, especially after relocation sections which needs symbols' index. +func (f *xcoffFile) asmaixsym(ctxt *Link) { + ldr := ctxt.loader + // Get correct size for symbols wrapping others symbols like go.string.* + // sym.Size can be used directly as the symbols have already been written. + for name, size := range outerSymSize { + sym := ldr.Lookup(name, 0) + if sym == 0 { + Errorf(nil, "unknown outer symbol with name %s", name) + } else { + s := ldr.MakeSymbolUpdater(sym) + s.SetSize(size) + } + } + + // These symbols won't show up in the first loop below because we + // skip sym.STEXT symbols. Normal sym.STEXT symbols are emitted by walking textp. + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + // We've already included this symbol in ctxt.Textp on AIX with external linker. + // See data.go:/textaddress + if !ctxt.IsExternal() { + putaixsym(ctxt, s, TextSym) + } + } + + n := 1 + // Generate base addresses for all text sections if there are multiple + for _, sect := range Segtext.Sections[1:] { + if sect.Name != ".text" || ctxt.IsExternal() { + // On AIX, runtime.text.X are symbols already in the symtab. + break + } + s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0) + if s == 0 { + break + } + if ldr.SymType(s) == sym.STEXT { + putaixsym(ctxt, s, TextSym) + } + n++ + } + + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + // We've already included this symbol in ctxt.Textp + // on AIX with external linker. + // See data.go:/textaddress + if !ctxt.IsExternal() { + putaixsym(ctxt, s, TextSym) + } + } + + shouldBeInSymbolTable := func(s loader.Sym, name string) bool { + if name == ".go.buildinfo" { + // On AIX, .go.buildinfo must be in the symbol table as + // it has relocations. + return true + } + if ldr.AttrNotInSymbolTable(s) { + return false + } + if (name == "" || name[0] == '.') && !ldr.IsFileLocal(s) && name != ".TOC." { + return false + } + return true + } + + for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ { + if !shouldBeInSymbolTable(s, ldr.SymName(s)) { + continue + } + st := ldr.SymType(s) + switch { + case st == sym.STLSBSS: + if ctxt.IsExternal() { + putaixsym(ctxt, s, TLSSym) + } + + case st == sym.SBSS, st == sym.SNOPTRBSS, st == sym.SLIBFUZZER_EXTRA_COUNTER: + if ldr.AttrReachable(s) { + data := ldr.Data(s) + if len(data) > 0 { + ldr.Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(data), ldr.SymType(s), ldr.AttrSpecial(s)) + } + putaixsym(ctxt, s, BSSSym) + } + + case st >= sym.SELFRXSECT && st < sym.SXREF: // data sections handled in dodata + if ldr.AttrReachable(s) { + putaixsym(ctxt, s, DataSym) + } + + case st == sym.SUNDEFEXT: + putaixsym(ctxt, s, UndefinedSym) + + case st == sym.SDYNIMPORT: + if ldr.AttrReachable(s) { + putaixsym(ctxt, s, UndefinedSym) + } + } + } + + for _, s := range ctxt.Textp { + putaixsym(ctxt, s, TextSym) + } + + if ctxt.Debugvlog != 0 || *flagN { + ctxt.Logf("symsize = %d\n", uint32(symSize)) + } + xfile.updatePreviousFile(ctxt, true) +} + +func (f *xcoffFile) genDynSym(ctxt *Link) { + ldr := ctxt.loader + var dynsyms []loader.Sym + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + if t := ldr.SymType(s); t != sym.SHOSTOBJ && t != sym.SDYNIMPORT { + continue + } + dynsyms = append(dynsyms, s) + } + + for _, s := range dynsyms { + f.adddynimpsym(ctxt, s) + + if _, ok := f.dynLibraries[ldr.SymDynimplib(s)]; !ok { + f.dynLibraries[ldr.SymDynimplib(s)] = len(f.dynLibraries) + } + } +} + +// (*xcoffFile)adddynimpsym adds the dynamic symbol "s" to a XCOFF file. +// A new symbol named s.Extname() is created to be the actual dynamic symbol +// in the .loader section and in the symbol table as an External Reference. +// The symbol "s" is transformed to SXCOFFTOC to end up in .data section. +// However, there is no writing protection on those symbols and +// it might need to be added. +// TODO(aix): Handles dynamic symbols without library. +func (f *xcoffFile) adddynimpsym(ctxt *Link, s loader.Sym) { + // Check that library name is given. + // Pattern is already checked when compiling. + ldr := ctxt.loader + if ctxt.IsInternal() && ldr.SymDynimplib(s) == "" { + ctxt.Errorf(s, "imported symbol must have a given library") + } + + sb := ldr.MakeSymbolUpdater(s) + sb.SetReachable(true) + sb.SetType(sym.SXCOFFTOC) + + // Create new dynamic symbol + extsym := ldr.CreateSymForUpdate(ldr.SymExtname(s), 0) + extsym.SetType(sym.SDYNIMPORT) + extsym.SetDynimplib(ldr.SymDynimplib(s)) + extsym.SetExtname(ldr.SymExtname(s)) + extsym.SetDynimpvers(ldr.SymDynimpvers(s)) + + // Add loader symbol + lds := &xcoffLoaderSymbol{ + sym: extsym.Sym(), + smtype: XTY_IMP, + smclas: XMC_DS, + } + if ldr.SymName(s) == "__n_pthreads" { + // Currently, all imported symbols made by cgo_import_dynamic are + // syscall functions, except __n_pthreads which is a variable. + // TODO(aix): Find a way to detect variables imported by cgo. + lds.smclas = XMC_RW + } + f.loaderSymbols = append(f.loaderSymbols, lds) + + // Relocation to retrieve the external address + sb.AddBytes(make([]byte, 8)) + r, _ := sb.AddRel(objabi.R_ADDR) + r.SetSym(extsym.Sym()) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + // TODO: maybe this could be + // sb.SetSize(0) + // sb.SetData(nil) + // sb.AddAddr(ctxt.Arch, extsym.Sym()) + // If the size is not 0 to begin with, I don't think the added 8 bytes + // of zeros are necessary. +} + +// Xcoffadddynrel adds a dynamic relocation in a XCOFF file. +// This relocation will be made by the loader. +func Xcoffadddynrel(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + if target.IsExternal() { + return true + } + if ldr.SymType(s) <= sym.SPCLNTAB { + ldr.Errorf(s, "cannot have a relocation to %s in a text section symbol", ldr.SymName(r.Sym())) + return false + } + + xldr := &xcoffLoaderReloc{ + sym: s, + roff: r.Off(), + } + targ := ldr.ResolveABIAlias(r.Sym()) + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch r.Type() { + default: + ldr.Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", ldr.SymName(targ), r.Type().String()) + return false + case objabi.R_ADDR: + if ldr.SymType(s) == sym.SXCOFFTOC && targType == sym.SDYNIMPORT { + // Imported symbol relocation + for i, dynsym := range xfile.loaderSymbols { + if ldr.SymName(dynsym.sym) == ldr.SymName(targ) { + xldr.symndx = int32(i + 3) // +3 because of 3 section symbols + break + } + } + } else if t := ldr.SymType(s); t == sym.SDATA || t == sym.SNOPTRDATA || t == sym.SBUILDINFO || t == sym.SXCOFFTOC { + switch ldr.SymSect(targ).Seg { + default: + ldr.Errorf(s, "unknown segment for .loader relocation with symbol %s", ldr.SymName(targ)) + case &Segtext: + case &Segrodata: + xldr.symndx = 0 // .text + case &Segdata: + if targType == sym.SBSS || targType == sym.SNOPTRBSS { + xldr.symndx = 2 // .bss + } else { + xldr.symndx = 1 // .data + } + } + + } else { + ldr.Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", ldr.SymName(targ), ldr.SymType(s), ldr.SymType(targ)) + return false + } + + xldr.rtype = 0x3F<<8 + XCOFF_R_POS + } + + xfile.Lock() + xfile.loaderReloc = append(xfile.loaderReloc, xldr) + xfile.Unlock() + return true +} + +func (ctxt *Link) doxcoff() { + if *FlagD { + // All XCOFF files have dynamic symbols because of the syscalls. + Exitf("-d is not available on AIX") + } + ldr := ctxt.loader + + // TOC + toc := ldr.CreateSymForUpdate("TOC", 0) + toc.SetType(sym.SXCOFFTOC) + toc.SetVisibilityHidden(true) + + // Add entry point to .loader symbols. + ep := ldr.Lookup(*flagEntrySymbol, 0) + if ep == 0 || !ldr.AttrReachable(ep) { + Exitf("wrong entry point") + } + + xfile.loaderSymbols = append(xfile.loaderSymbols, &xcoffLoaderSymbol{ + sym: ep, + smtype: XTY_ENT | XTY_SD, + smclas: XMC_DS, + }) + + xfile.genDynSym(ctxt) + + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if strings.HasPrefix(ldr.SymName(s), "TOC.") { + sb := ldr.MakeSymbolUpdater(s) + sb.SetType(sym.SXCOFFTOC) + } + } + + if ctxt.IsExternal() { + // Change rt0_go name to match name in runtime/cgo:main(). + rt0 := ldr.Lookup("runtime.rt0_go", 0) + ldr.SetSymExtname(rt0, "runtime_rt0_go") + + nsym := loader.Sym(ldr.NSym()) + for s := loader.Sym(1); s < nsym; s++ { + if !ldr.AttrCgoExport(s) { + continue + } + if ldr.SymVersion(s) != 0 { // sanity check + panic("cgo_export on non-version 0 symbol") + } + + if ldr.SymType(s) == sym.STEXT || ldr.SymType(s) == sym.SABIALIAS { + // On AIX, a exported function must have two symbols: + // - a .text symbol which must start with a ".". + // - a .data symbol which is a function descriptor. + // + // CgoExport attribute should only be set on a version 0 + // symbol, which can be TEXT or ABIALIAS. + // (before, setupdynexp copies the attribute from the + // alias to the aliased. Now we are before setupdynexp.) + name := ldr.SymExtname(s) + ldr.SetSymExtname(s, "."+name) + + desc := ldr.MakeSymbolUpdater(ldr.CreateExtSym(name, 0)) + desc.SetReachable(true) + desc.SetType(sym.SNOPTRDATA) + desc.AddAddr(ctxt.Arch, s) + desc.AddAddr(ctxt.Arch, toc.Sym()) + desc.AddUint64(ctxt.Arch, 0) + } + } + } +} + +// Loader section +// Currently, this section is created from scratch when assembling the XCOFF file +// according to information retrieved in xfile object. + +// Create loader section and returns its size +func Loaderblk(ctxt *Link, off uint64) { + xfile.writeLdrScn(ctxt, off) +} + +func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) { + var symtab []*XcoffLdSym64 + var strtab []*XcoffLdStr64 + var importtab []*XcoffLdImportFile64 + var reloctab []*XcoffLdRel64 + var dynimpreloc []*XcoffLdRel64 + + // As the string table is updated in any loader subsection, + // its length must be computed at the same time. + stlen := uint32(0) + + // Loader Header + hdr := &XcoffLdHdr64{ + Lversion: 2, + Lsymoff: LDHDRSZ_64, + } + + ldr := ctxt.loader + /* Symbol table */ + for _, s := range f.loaderSymbols { + lds := &XcoffLdSym64{ + Loffset: uint32(stlen + 2), + Lsmtype: s.smtype, + Lsmclas: s.smclas, + } + sym := s.sym + switch s.smtype { + default: + ldr.Errorf(sym, "unexpected loader symbol type: 0x%x", s.smtype) + case XTY_ENT | XTY_SD: + lds.Lvalue = uint64(ldr.SymValue(sym)) + lds.Lscnum = f.getXCOFFscnum(ldr.SymSect(sym)) + case XTY_IMP: + lds.Lifile = int32(f.dynLibraries[ldr.SymDynimplib(sym)] + 1) + } + ldstr := &XcoffLdStr64{ + size: uint16(len(ldr.SymName(sym)) + 1), // + null terminator + name: ldr.SymName(sym), + } + stlen += uint32(2 + ldstr.size) // 2 = sizeof ldstr.size + symtab = append(symtab, lds) + strtab = append(strtab, ldstr) + + } + + hdr.Lnsyms = int32(len(symtab)) + hdr.Lrldoff = hdr.Lsymoff + uint64(24*hdr.Lnsyms) // 24 = sizeof one symbol + off := hdr.Lrldoff // current offset is the same of reloc offset + + /* Reloc */ + // Ensure deterministic order + sort.Slice(f.loaderReloc, func(i, j int) bool { + r1, r2 := f.loaderReloc[i], f.loaderReloc[j] + if r1.sym != r2.sym { + return r1.sym < r2.sym + } + if r1.roff != r2.roff { + return r1.roff < r2.roff + } + if r1.rtype != r2.rtype { + return r1.rtype < r2.rtype + } + return r1.symndx < r2.symndx + }) + + ep := ldr.Lookup(*flagEntrySymbol, 0) + xldr := &XcoffLdRel64{ + Lvaddr: uint64(ldr.SymValue(ep)), + Lrtype: 0x3F00, + Lrsecnm: f.getXCOFFscnum(ldr.SymSect(ep)), + Lsymndx: 0, + } + off += 16 + reloctab = append(reloctab, xldr) + + off += uint64(16 * len(f.loaderReloc)) + for _, r := range f.loaderReloc { + symp := r.sym + if symp == 0 { + panic("unexpected 0 sym value") + } + xldr = &XcoffLdRel64{ + Lvaddr: uint64(ldr.SymValue(symp) + int64(r.roff)), + Lrtype: r.rtype, + Lsymndx: r.symndx, + } + + if ldr.SymSect(symp) != nil { + xldr.Lrsecnm = f.getXCOFFscnum(ldr.SymSect(symp)) + } + + reloctab = append(reloctab, xldr) + } + + off += uint64(16 * len(dynimpreloc)) + reloctab = append(reloctab, dynimpreloc...) + + hdr.Lnreloc = int32(len(reloctab)) + hdr.Limpoff = off + + /* Import */ + // Default import: /usr/lib:/lib + ldimpf := &XcoffLdImportFile64{ + Limpidpath: "/usr/lib:/lib", + } + off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter + importtab = append(importtab, ldimpf) + + // The map created by adddynimpsym associates the name to a number + // This number represents the librairie index (- 1) in this import files section + // Therefore, they must be sorted before being put inside the section + libsOrdered := make([]string, len(f.dynLibraries)) + for key, val := range f.dynLibraries { + if libsOrdered[val] != "" { + continue + } + libsOrdered[val] = key + } + + for _, lib := range libsOrdered { + // lib string is defined as base.a/mem.o or path/base.a/mem.o + n := strings.Split(lib, "/") + path := "" + base := n[len(n)-2] + mem := n[len(n)-1] + if len(n) > 2 { + path = lib[:len(lib)-len(base)-len(mem)-2] + + } + ldimpf = &XcoffLdImportFile64{ + Limpidpath: path, + Limpidbase: base, + Limpidmem: mem, + } + off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter + importtab = append(importtab, ldimpf) + } + + hdr.Lnimpid = int32(len(importtab)) + hdr.Listlen = uint32(off - hdr.Limpoff) + hdr.Lstoff = off + hdr.Lstlen = stlen + + /* Writing */ + ctxt.Out.SeekSet(int64(globalOff)) + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, hdr) + + for _, s := range symtab { + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s) + + } + for _, r := range reloctab { + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, r) + } + for _, f := range importtab { + ctxt.Out.WriteString(f.Limpidpath) + ctxt.Out.Write8(0) + ctxt.Out.WriteString(f.Limpidbase) + ctxt.Out.Write8(0) + ctxt.Out.WriteString(f.Limpidmem) + ctxt.Out.Write8(0) + } + for _, s := range strtab { + ctxt.Out.Write16(s.size) + ctxt.Out.WriteString(s.name) + ctxt.Out.Write8(0) // null terminator + } + + f.loaderSize = off + uint64(stlen) +} + +// XCOFF assembling and writing file + +func (f *xcoffFile) writeFileHeader(ctxt *Link) { + // File header + f.xfhdr.Fmagic = U64_TOCMAGIC + f.xfhdr.Fnscns = uint16(len(f.sections)) + f.xfhdr.Ftimedat = 0 + + if !*FlagS { + f.xfhdr.Fsymptr = uint64(f.symtabOffset) + f.xfhdr.Fnsyms = int32(f.symbolCount) + } + + if ctxt.BuildMode == BuildModeExe && ctxt.LinkMode == LinkInternal { + ldr := ctxt.loader + f.xfhdr.Fopthdr = AOUTHSZ_EXEC64 + f.xfhdr.Fflags = F_EXEC + + // auxiliary header + f.xahdr.Ovstamp = 1 // based on dump -o + f.xahdr.Omagic = 0x10b + copy(f.xahdr.Omodtype[:], "1L") + entry := ldr.Lookup(*flagEntrySymbol, 0) + f.xahdr.Oentry = uint64(ldr.SymValue(entry)) + f.xahdr.Osnentry = f.getXCOFFscnum(ldr.SymSect(entry)) + toc := ldr.Lookup("TOC", 0) + f.xahdr.Otoc = uint64(ldr.SymValue(toc)) + f.xahdr.Osntoc = f.getXCOFFscnum(ldr.SymSect(toc)) + + f.xahdr.Oalgntext = int16(logBase2(int(Funcalign))) + f.xahdr.Oalgndata = 0x5 + + binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr) + binary.Write(ctxt.Out, binary.BigEndian, &f.xahdr) + } else { + f.xfhdr.Fopthdr = 0 + binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr) + } + +} + +func xcoffwrite(ctxt *Link) { + ctxt.Out.SeekSet(0) + + xfile.writeFileHeader(ctxt) + + for _, sect := range xfile.sections { + sect.write(ctxt) + } +} + +// Generate XCOFF assembly file +func asmbXcoff(ctxt *Link) { + ctxt.Out.SeekSet(0) + fileoff := int64(Segdwarf.Fileoff + Segdwarf.Filelen) + fileoff = int64(Rnd(int64(fileoff), int64(*FlagRound))) + + xfile.sectNameToScnum = make(map[string]int16) + + // Add sections + s := xfile.addSection(".text", Segtext.Vaddr, Segtext.Length, Segtext.Fileoff, STYP_TEXT) + xfile.xahdr.Otextstart = s.Svaddr + xfile.xahdr.Osntext = xfile.sectNameToScnum[".text"] + xfile.xahdr.Otsize = s.Ssize + xfile.sectText = s + + segdataVaddr := Segdata.Vaddr + segdataFilelen := Segdata.Filelen + segdataFileoff := Segdata.Fileoff + segbssFilelen := Segdata.Length - Segdata.Filelen + if len(Segrelrodata.Sections) > 0 { + // Merge relro segment to data segment as + // relro data are inside data segment on AIX. + segdataVaddr = Segrelrodata.Vaddr + segdataFileoff = Segrelrodata.Fileoff + segdataFilelen = Segdata.Vaddr + Segdata.Filelen - Segrelrodata.Vaddr + } + + s = xfile.addSection(".data", segdataVaddr, segdataFilelen, segdataFileoff, STYP_DATA) + xfile.xahdr.Odatastart = s.Svaddr + xfile.xahdr.Osndata = xfile.sectNameToScnum[".data"] + xfile.xahdr.Odsize = s.Ssize + xfile.sectData = s + + s = xfile.addSection(".bss", segdataVaddr+segdataFilelen, segbssFilelen, 0, STYP_BSS) + xfile.xahdr.Osnbss = xfile.sectNameToScnum[".bss"] + xfile.xahdr.Obsize = s.Ssize + xfile.sectBss = s + + if ctxt.LinkMode == LinkExternal { + var tbss *sym.Section + for _, s := range Segdata.Sections { + if s.Name == ".tbss" { + tbss = s + break + } + } + s = xfile.addSection(".tbss", tbss.Vaddr, tbss.Length, 0, STYP_TBSS) + } + + // add dwarf sections + for _, sect := range Segdwarf.Sections { + xfile.addDwarfSection(sect) + } + + // add and write remaining sections + if ctxt.LinkMode == LinkInternal { + // Loader section + if ctxt.BuildMode == BuildModeExe { + Loaderblk(ctxt, uint64(fileoff)) + s = xfile.addSection(".loader", 0, xfile.loaderSize, uint64(fileoff), STYP_LOADER) + xfile.xahdr.Osnloader = xfile.sectNameToScnum[".loader"] + + // Update fileoff for symbol table + fileoff += int64(xfile.loaderSize) + } + } + + // Create Symbol table + xfile.asmaixsym(ctxt) + + if ctxt.LinkMode == LinkExternal { + xfile.emitRelocations(ctxt, fileoff) + } + + // Write Symbol table + xfile.symtabOffset = ctxt.Out.Offset() + for _, s := range xfile.symtabSym { + binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s) + } + // write string table + xfile.stringTable.write(ctxt.Out) + + // write headers + xcoffwrite(ctxt) +} + +// emitRelocations emits relocation entries for go.o in external linking. +func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) { + ctxt.Out.SeekSet(fileoff) + for ctxt.Out.Offset()&7 != 0 { + ctxt.Out.Write8(0) + } + + ldr := ctxt.loader + // relocsect relocates symbols from first in section sect, and returns + // the total number of relocations emitted. + relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) uint32 { + // ctxt.Logf("%s 0x%x\n", sect.Name, sect.Vaddr) + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return 0 + } + sect.Reloff = uint64(ctxt.Out.Offset()) + for i, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + eaddr := int64(sect.Vaddr + sect.Length) + for _, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymValue(s) >= int64(eaddr) { + break + } + + // Compute external relocations on the go, and pass to Xcoffreloc1 to stream out. + // Relocation must be ordered by address, so create a list of sorted indices. + relocs := ldr.Relocs(s) + sorted := make([]int, relocs.Count()) + for i := 0; i < relocs.Count(); i++ { + sorted[i] = i + } + sort.Slice(sorted, func(i, j int) bool { + return relocs.At(sorted[i]).Off() < relocs.At(sorted[j]).Off() + }) + + for _, ri := range sorted { + r := relocs.At(ri) + rr, ok := extreloc(ctxt, ldr, s, r) + if !ok { + continue + } + if rr.Xsym == 0 { + ldr.Errorf(s, "missing xsym in relocation") + continue + } + if ldr.SymDynid(rr.Xsym) < 0 { + ldr.Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymDynid(rr.Xsym)) + } + if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) { + ldr.Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type(), r.Type(), r.Siz(), ldr.SymName(r.Sym())) + } + } + } + sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff + return uint32(sect.Rellen) / RELSZ_64 + } + sects := []struct { + xcoffSect *XcoffScnHdr64 + segs []*sym.Segment + }{ + {f.sectText, []*sym.Segment{&Segtext}}, + {f.sectData, []*sym.Segment{&Segrelrodata, &Segdata}}, + } + for _, s := range sects { + s.xcoffSect.Srelptr = uint64(ctxt.Out.Offset()) + n := uint32(0) + for _, seg := range s.segs { + for _, sect := range seg.Sections { + if sect.Name == ".text" { + n += relocsect(sect, ctxt.Textp, 0) + } else { + n += relocsect(sect, ctxt.datap, 0) + } + } + } + s.xcoffSect.Snreloc += n + } + +dwarfLoop: + for i := 0; i < len(Segdwarf.Sections); i++ { + sect := Segdwarf.Sections[i] + si := dwarfp[i] + if si.secSym() != loader.Sym(sect.Sym) || + ldr.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + for _, xcoffSect := range f.sections { + _, subtyp := xcoffGetDwarfSubtype(sect.Name) + if xcoffSect.Sflags&0xF0000 == subtyp { + xcoffSect.Srelptr = uint64(ctxt.Out.Offset()) + xcoffSect.Snreloc = relocsect(sect, si.syms, sect.Vaddr) + continue dwarfLoop + } + } + Errorf(nil, "emitRelocations: could not find %q section", sect.Name) + } +} + +// xcoffCreateExportFile creates a file with exported symbols for +// -Wl,-bE option. +// ld won't export symbols unless they are listed in an export file. +func xcoffCreateExportFile(ctxt *Link) (fname string) { + fname = filepath.Join(*flagTmpdir, "export_file.exp") + var buf bytes.Buffer + + ldr := ctxt.loader + for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ { + if !ldr.AttrCgoExport(s) { + continue + } + extname := ldr.SymExtname(s) + if !strings.HasPrefix(extname, "._cgoexp_") { + continue + } + if ldr.SymVersion(s) != 0 { + continue // Only export version 0 symbols. See the comment in doxcoff. + } + + // Retrieve the name of the initial symbol + // exported by cgo. + // The corresponding Go symbol is: + // _cgoexp_hashcode_symname. + name := strings.SplitN(extname, "_", 4)[3] + + buf.Write([]byte(name + "\n")) + } + + err := ioutil.WriteFile(fname, buf.Bytes(), 0666) + if err != nil { + Errorf(nil, "WriteFile %s failed: %v", fname, err) + } + + return fname +} diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go new file mode 100644 index 0000000..c698874 --- /dev/null +++ b/src/cmd/link/internal/loadelf/ldelf.go @@ -0,0 +1,1100 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package loadelf implements an ELF file reader. +package loadelf + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "encoding/binary" + "fmt" + "io" + "log" + "strings" +) + +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +const ( + SHT_ARM_ATTRIBUTES = 0x70000003 +) + +type ElfSect struct { + name string + nameoff uint32 + type_ elf.SectionType + flags elf.SectionFlag + addr uint64 + off uint64 + size uint64 + link uint32 + info uint32 + align uint64 + entsize uint64 + base []byte + readOnlyMem bool // Is this section in readonly memory? + sym loader.Sym +} + +type ElfObj struct { + f *bio.Reader + base int64 // offset in f where ELF begins + length int64 // length of ELF + is64 int + name string + e binary.ByteOrder + sect []ElfSect + nsect uint + nsymtab int + symtab *ElfSect + symstr *ElfSect + type_ uint32 + machine uint32 + version uint32 + entry uint64 + phoff uint64 + shoff uint64 + flags uint32 + ehsize uint32 + phentsize uint32 + phnum uint32 + shentsize uint32 + shnum uint32 + shstrndx uint32 +} + +type ElfSym struct { + name string + value uint64 + size uint64 + bind elf.SymBind + type_ elf.SymType + other uint8 + shndx elf.SectionIndex + sym loader.Sym +} + +const ( + TagFile = 1 + TagCPUName = 4 + TagCPURawName = 5 + TagCompatibility = 32 + TagNoDefaults = 64 + TagAlsoCompatibleWith = 65 + TagABIVFPArgs = 28 +) + +type elfAttribute struct { + tag uint64 + sval string + ival uint64 +} + +type elfAttributeList struct { + data []byte + err error +} + +func (a *elfAttributeList) string() string { + if a.err != nil { + return "" + } + nul := bytes.IndexByte(a.data, 0) + if nul < 0 { + a.err = io.EOF + return "" + } + s := string(a.data[:nul]) + a.data = a.data[nul+1:] + return s +} + +func (a *elfAttributeList) uleb128() uint64 { + if a.err != nil { + return 0 + } + v, size := binary.Uvarint(a.data) + a.data = a.data[size:] + return v +} + +// Read an elfAttribute from the list following the rules used on ARM systems. +func (a *elfAttributeList) armAttr() elfAttribute { + attr := elfAttribute{tag: a.uleb128()} + switch { + case attr.tag == TagCompatibility: + attr.ival = a.uleb128() + attr.sval = a.string() + + case attr.tag == TagNoDefaults: // Tag_nodefaults has no argument + + case attr.tag == TagAlsoCompatibleWith: + // Not really, but we don't actually care about this tag. + attr.sval = a.string() + + // Tag with string argument + case attr.tag == TagCPUName || attr.tag == TagCPURawName || (attr.tag >= 32 && attr.tag&1 != 0): + attr.sval = a.string() + + default: // Tag with integer argument + attr.ival = a.uleb128() + } + return attr +} + +func (a *elfAttributeList) done() bool { + if a.err != nil || len(a.data) == 0 { + return true + } + return false +} + +// Look for the attribute that indicates the object uses the hard-float ABI (a +// file-level attribute with tag Tag_VFP_arch and value 1). Unfortunately the +// format used means that we have to parse all of the file-level attributes to +// find the one we are looking for. This format is slightly documented in "ELF +// for the ARM Architecture" but mostly this is derived from reading the source +// to gold and readelf. +func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags uint32, err error) { + found = false + if data[0] != 'A' { + return false, 0, fmt.Errorf(".ARM.attributes has unexpected format %c\n", data[0]) + } + data = data[1:] + for len(data) != 0 { + sectionlength := e.Uint32(data) + sectiondata := data[4:sectionlength] + data = data[sectionlength:] + + nulIndex := bytes.IndexByte(sectiondata, 0) + if nulIndex < 0 { + return false, 0, fmt.Errorf("corrupt .ARM.attributes (section name not NUL-terminated)\n") + } + name := string(sectiondata[:nulIndex]) + sectiondata = sectiondata[nulIndex+1:] + + if name != "aeabi" { + continue + } + for len(sectiondata) != 0 { + subsectiontag, sz := binary.Uvarint(sectiondata) + subsectionsize := e.Uint32(sectiondata[sz:]) + subsectiondata := sectiondata[sz+4 : subsectionsize] + sectiondata = sectiondata[subsectionsize:] + + if subsectiontag != TagFile { + continue + } + attrList := elfAttributeList{data: subsectiondata} + for !attrList.done() { + attr := attrList.armAttr() + if attr.tag == TagABIVFPArgs && attr.ival == 1 { + found = true + ehdrFlags = 0x5000402 // has entry point, Version5 EABI, hard-float ABI + } + } + if attrList.err != nil { + return false, 0, fmt.Errorf("could not parse .ARM.attributes\n") + } + } + } + return found, ehdrFlags, nil +} + +// Load loads the ELF file pn from f. +// Symbols are installed into the loader, and a slice of the text symbols is returned. +// +// On ARM systems, Load will attempt to determine what ELF header flags to +// emit by scanning the attributes in the ELF file being loaded. The +// parameter initEhdrFlags contains the current header flags for the output +// object, and the returned ehdrFlags contains what this Load function computes. +// TODO: find a better place for this logic. +func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []loader.Sym, ehdrFlags uint32, err error) { + newSym := func(name string, version int) loader.Sym { + return l.CreateStaticSym(name) + } + lookup := func(name string, version int) loader.Sym { + return l.LookupOrCreateSym(name, version) + } + errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) { + return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...)) + } + + base := f.Offset() + + var hdrbuf [64]byte + if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { + return errorf("malformed elf file: %v", err) + } + + var e binary.ByteOrder + switch elf.Data(hdrbuf[elf.EI_DATA]) { + case elf.ELFDATA2LSB: + e = binary.LittleEndian + + case elf.ELFDATA2MSB: + e = binary.BigEndian + + default: + return errorf("malformed elf file, unknown header") + } + + hdr := new(elf.Header32) + binary.Read(bytes.NewReader(hdrbuf[:]), e, hdr) + + if string(hdr.Ident[:elf.EI_CLASS]) != elf.ELFMAG { + return errorf("malformed elf file, bad header") + } + + // read header + elfobj := new(ElfObj) + + elfobj.e = e + elfobj.f = f + elfobj.base = base + elfobj.length = length + elfobj.name = pn + + is64 := 0 + class := elf.Class(hdrbuf[elf.EI_CLASS]) + if class == elf.ELFCLASS64 { + is64 = 1 + hdr := new(elf.Header64) + binary.Read(bytes.NewReader(hdrbuf[:]), e, hdr) + elfobj.type_ = uint32(hdr.Type) + elfobj.machine = uint32(hdr.Machine) + elfobj.version = hdr.Version + elfobj.entry = hdr.Entry + elfobj.phoff = hdr.Phoff + elfobj.shoff = hdr.Shoff + elfobj.flags = hdr.Flags + elfobj.ehsize = uint32(hdr.Ehsize) + elfobj.phentsize = uint32(hdr.Phentsize) + elfobj.phnum = uint32(hdr.Phnum) + elfobj.shentsize = uint32(hdr.Shentsize) + elfobj.shnum = uint32(hdr.Shnum) + elfobj.shstrndx = uint32(hdr.Shstrndx) + } else { + elfobj.type_ = uint32(hdr.Type) + elfobj.machine = uint32(hdr.Machine) + elfobj.version = hdr.Version + elfobj.entry = uint64(hdr.Entry) + elfobj.phoff = uint64(hdr.Phoff) + elfobj.shoff = uint64(hdr.Shoff) + elfobj.flags = hdr.Flags + elfobj.ehsize = uint32(hdr.Ehsize) + elfobj.phentsize = uint32(hdr.Phentsize) + elfobj.phnum = uint32(hdr.Phnum) + elfobj.shentsize = uint32(hdr.Shentsize) + elfobj.shnum = uint32(hdr.Shnum) + elfobj.shstrndx = uint32(hdr.Shstrndx) + } + + elfobj.is64 = is64 + + if v := uint32(hdrbuf[elf.EI_VERSION]); v != elfobj.version { + return errorf("malformed elf version: got %d, want %d", v, elfobj.version) + } + + if elf.Type(elfobj.type_) != elf.ET_REL { + return errorf("elf but not elf relocatable object") + } + + mach := elf.Machine(elfobj.machine) + switch arch.Family { + default: + return errorf("elf %s unimplemented", arch.Name) + + case sys.MIPS: + if mach != elf.EM_MIPS || class != elf.ELFCLASS32 { + return errorf("elf object but not mips") + } + + case sys.MIPS64: + if mach != elf.EM_MIPS || class != elf.ELFCLASS64 { + return errorf("elf object but not mips64") + } + + case sys.ARM: + if e != binary.LittleEndian || mach != elf.EM_ARM || class != elf.ELFCLASS32 { + return errorf("elf object but not arm") + } + + case sys.AMD64: + if e != binary.LittleEndian || mach != elf.EM_X86_64 || class != elf.ELFCLASS64 { + return errorf("elf object but not amd64") + } + + case sys.ARM64: + if e != binary.LittleEndian || mach != elf.EM_AARCH64 || class != elf.ELFCLASS64 { + return errorf("elf object but not arm64") + } + + case sys.I386: + if e != binary.LittleEndian || mach != elf.EM_386 || class != elf.ELFCLASS32 { + return errorf("elf object but not 386") + } + + case sys.PPC64: + if mach != elf.EM_PPC64 || class != elf.ELFCLASS64 { + return errorf("elf object but not ppc64") + } + + case sys.RISCV64: + if mach != elf.EM_RISCV || class != elf.ELFCLASS64 { + return errorf("elf object but not riscv64") + } + + case sys.S390X: + if mach != elf.EM_S390 || class != elf.ELFCLASS64 { + return errorf("elf object but not s390x") + } + } + + // load section list into memory. + elfobj.sect = make([]ElfSect, elfobj.shnum) + + elfobj.nsect = uint(elfobj.shnum) + for i := 0; uint(i) < elfobj.nsect; i++ { + f.MustSeek(int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) + sect := &elfobj.sect[i] + if is64 != 0 { + var b elf.Section64 + if err := binary.Read(f, e, &b); err != nil { + return errorf("malformed elf file: %v", err) + } + + sect.nameoff = b.Name + sect.type_ = elf.SectionType(b.Type) + sect.flags = elf.SectionFlag(b.Flags) + sect.addr = b.Addr + sect.off = b.Off + sect.size = b.Size + sect.link = b.Link + sect.info = b.Info + sect.align = b.Addralign + sect.entsize = b.Entsize + } else { + var b elf.Section32 + + if err := binary.Read(f, e, &b); err != nil { + return errorf("malformed elf file: %v", err) + } + sect.nameoff = b.Name + sect.type_ = elf.SectionType(b.Type) + sect.flags = elf.SectionFlag(b.Flags) + sect.addr = uint64(b.Addr) + sect.off = uint64(b.Off) + sect.size = uint64(b.Size) + sect.link = b.Link + sect.info = b.Info + sect.align = uint64(b.Addralign) + sect.entsize = uint64(b.Entsize) + } + } + + // read section string table and translate names + if elfobj.shstrndx >= uint32(elfobj.nsect) { + return errorf("malformed elf file: shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect) + } + + sect := &elfobj.sect[elfobj.shstrndx] + if err := elfmap(elfobj, sect); err != nil { + return errorf("malformed elf file: %v", err) + } + for i := 0; uint(i) < elfobj.nsect; i++ { + if elfobj.sect[i].nameoff != 0 { + elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:]) + } + } + + // load string table for symbols into memory. + elfobj.symtab = section(elfobj, ".symtab") + + if elfobj.symtab == nil { + // our work is done here - no symbols means nothing can refer to this file + return + } + + if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) { + return errorf("elf object has symbol table with invalid string table link") + } + + elfobj.symstr = &elfobj.sect[elfobj.symtab.link] + if is64 != 0 { + elfobj.nsymtab = int(elfobj.symtab.size / elf.Sym64Size) + } else { + elfobj.nsymtab = int(elfobj.symtab.size / elf.Sym32Size) + } + + if err := elfmap(elfobj, elfobj.symtab); err != nil { + return errorf("malformed elf file: %v", err) + } + if err := elfmap(elfobj, elfobj.symstr); err != nil { + return errorf("malformed elf file: %v", err) + } + + // load text and data segments into memory. + // they are not as small as the section lists, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + + // create symbols for elfmapped sections + sectsymNames := make(map[string]bool) + counter := 0 + for i := 0; uint(i) < elfobj.nsect; i++ { + sect = &elfobj.sect[i] + if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" { + if err := elfmap(elfobj, sect); err != nil { + return errorf("%s: malformed elf file: %v", pn, err) + } + // We assume the soft-float ABI unless we see a tag indicating otherwise. + if initEhdrFlags == 0x5000002 { + ehdrFlags = 0x5000202 + } else { + ehdrFlags = initEhdrFlags + } + found, newEhdrFlags, err := parseArmAttributes(e, sect.base[:sect.size]) + if err != nil { + // TODO(dfc) should this return an error? + log.Printf("%s: %v", pn, err) + } + if found { + ehdrFlags = newEhdrFlags + } + } + if (sect.type_ != elf.SHT_PROGBITS && sect.type_ != elf.SHT_NOBITS) || sect.flags&elf.SHF_ALLOC == 0 { + continue + } + if sect.type_ != elf.SHT_NOBITS { + if err := elfmap(elfobj, sect); err != nil { + return errorf("%s: malformed elf file: %v", pn, err) + } + } + + name := fmt.Sprintf("%s(%s)", pkg, sect.name) + for sectsymNames[name] { + counter++ + name = fmt.Sprintf("%s(%s%d)", pkg, sect.name, counter) + } + sectsymNames[name] = true + + sb := l.MakeSymbolUpdater(lookup(name, localSymVersion)) + + switch sect.flags & (elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_EXECINSTR) { + default: + return errorf("%s: unexpected flags for ELF section %s", pn, sect.name) + + case elf.SHF_ALLOC: + sb.SetType(sym.SRODATA) + + case elf.SHF_ALLOC + elf.SHF_WRITE: + if sect.type_ == elf.SHT_NOBITS { + sb.SetType(sym.SNOPTRBSS) + } else { + sb.SetType(sym.SNOPTRDATA) + } + + case elf.SHF_ALLOC + elf.SHF_EXECINSTR: + sb.SetType(sym.STEXT) + } + + if sect.name == ".got" || sect.name == ".toc" { + sb.SetType(sym.SELFGOT) + } + if sect.type_ == elf.SHT_PROGBITS { + sb.SetData(sect.base[:sect.size]) + } + + sb.SetSize(int64(sect.size)) + sb.SetAlign(int32(sect.align)) + sb.SetReadOnly(sect.readOnlyMem) + + sect.sym = sb.Sym() + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + symbols := make([]loader.Sym, elfobj.nsymtab) + + for i := 1; i < elfobj.nsymtab; i++ { + var elfsym ElfSym + if err := readelfsym(newSym, lookup, l, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil { + return errorf("%s: malformed elf file: %v", pn, err) + } + symbols[i] = elfsym.sym + if elfsym.type_ != elf.STT_FUNC && elfsym.type_ != elf.STT_OBJECT && elfsym.type_ != elf.STT_NOTYPE && elfsym.type_ != elf.STT_COMMON { + continue + } + if elfsym.shndx == elf.SHN_COMMON || elfsym.type_ == elf.STT_COMMON { + sb := l.MakeSymbolUpdater(elfsym.sym) + if uint64(sb.Size()) < elfsym.size { + sb.SetSize(int64(elfsym.size)) + } + if sb.Type() == 0 || sb.Type() == sym.SXREF { + sb.SetType(sym.SNOPTRBSS) + } + continue + } + + if uint(elfsym.shndx) >= elfobj.nsect || elfsym.shndx == 0 { + continue + } + + // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols + if elfsym.sym == 0 { + continue + } + sect = &elfobj.sect[elfsym.shndx] + if sect.sym == 0 { + if strings.HasPrefix(elfsym.name, ".Linfo_string") { // clang does this + continue + } + + if elfsym.name == "" && elfsym.type_ == 0 && sect.name == ".debug_str" { + // This reportedly happens with clang 3.7 on ARM. + // See issue 13139. + continue + } + + if strings.HasPrefix(elfsym.name, "$d") && elfsym.type_ == 0 && sect.name == ".debug_frame" { + // "$d" is a marker, not a real symbol. + // This happens with gcc on ARM64. + // See https://sourceware.org/bugzilla/show_bug.cgi?id=21809 + continue + } + + if strings.HasPrefix(elfsym.name, ".LASF") { // gcc on s390x does this + continue + } + return errorf("%v: sym#%d: ignoring symbol in section %d (type %d)", elfsym.sym, i, elfsym.shndx, elfsym.type_) + } + + s := elfsym.sym + if l.OuterSym(s) != 0 { + if l.AttrDuplicateOK(s) { + continue + } + return errorf("duplicate symbol reference: %s in both %s and %s", + l.SymName(s), l.SymName(l.OuterSym(s)), l.SymName(sect.sym)) + } + + sectsb := l.MakeSymbolUpdater(sect.sym) + sb := l.MakeSymbolUpdater(s) + + sb.SetType(sectsb.Type()) + sectsb.AddInteriorSym(s) + if !l.AttrCgoExportDynamic(s) { + sb.SetDynimplib("") // satisfy dynimport + } + sb.SetValue(int64(elfsym.value)) + sb.SetSize(int64(elfsym.size)) + if sectsb.Type() == sym.STEXT { + if l.AttrExternal(s) && !l.AttrDuplicateOK(s) { + return errorf("%s: duplicate symbol definition", sb.Name()) + } + l.SetAttrExternal(s, true) + } + + if elf.Machine(elfobj.machine) == elf.EM_PPC64 { + flag := int(elfsym.other) >> 5 + if 2 <= flag && flag <= 6 { + l.SetSymLocalentry(s, 1<<uint(flag-2)) + } else if flag == 7 { + return errorf("%s: invalid sym.other 0x%x", sb.Name(), elfsym.other) + } + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i := uint(0); i < elfobj.nsect; i++ { + s := elfobj.sect[i].sym + if s == 0 { + continue + } + sb := l.MakeSymbolUpdater(s) + if l.SubSym(s) != 0 { + sb.SortSub() + } + if sb.Type() == sym.STEXT { + if l.AttrOnList(s) { + return errorf("symbol %s listed multiple times", + l.SymName(s)) + } + l.SetAttrOnList(s, true) + textp = append(textp, s) + for ss := l.SubSym(s); ss != 0; ss = l.SubSym(ss) { + if l.AttrOnList(ss) { + return errorf("symbol %s listed multiple times", + l.SymName(ss)) + } + l.SetAttrOnList(ss, true) + textp = append(textp, ss) + } + } + } + + // load relocations + for i := uint(0); i < elfobj.nsect; i++ { + rsect := &elfobj.sect[i] + if rsect.type_ != elf.SHT_RELA && rsect.type_ != elf.SHT_REL { + continue + } + if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil { + continue + } + sect = &elfobj.sect[rsect.info] + if err := elfmap(elfobj, rsect); err != nil { + return errorf("malformed elf file: %v", err) + } + rela := 0 + if rsect.type_ == elf.SHT_RELA { + rela = 1 + } + n := int(rsect.size / uint64(4+4*is64) / uint64(2+rela)) + p := rsect.base + sb := l.MakeSymbolUpdater(sect.sym) + for j := 0; j < n; j++ { + var add uint64 + var symIdx int + var relocType uint64 + var rOff int32 + var rAdd int64 + var rSym loader.Sym + + if is64 != 0 { + // 64-bit rel/rela + rOff = int32(e.Uint64(p)) + + p = p[8:] + switch arch.Family { + case sys.MIPS64: + // https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf + // The doc shows it's different with general Linux ELF + symIdx = int(e.Uint32(p)) + relocType = uint64(p[7]) + default: + info := e.Uint64(p) + relocType = info & 0xffffffff + symIdx = int(info >> 32) + } + p = p[8:] + if rela != 0 { + add = e.Uint64(p) + p = p[8:] + } + } else { + // 32-bit rel/rela + rOff = int32(e.Uint32(p)) + + p = p[4:] + info := e.Uint32(p) + relocType = uint64(info & 0xff) + symIdx = int(info >> 8) + p = p[4:] + if rela != 0 { + add = uint64(e.Uint32(p)) + p = p[4:] + } + } + + if relocType == 0 { // skip R_*_NONE relocation + j-- + n-- + continue + } + + if symIdx == 0 { // absolute relocation, don't bother reading the null symbol + rSym = 0 + } else { + var elfsym ElfSym + if err := readelfsym(newSym, lookup, l, arch, elfobj, int(symIdx), &elfsym, 0, 0); err != nil { + return errorf("malformed elf file: %v", err) + } + elfsym.sym = symbols[symIdx] + if elfsym.sym == 0 { + return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", l.SymName(sect.sym), j, int(symIdx), elfsym.name, elfsym.shndx, elfsym.type_) + } + + rSym = elfsym.sym + } + + rType := objabi.ElfRelocOffset + objabi.RelocType(relocType) + rSize, err := relSize(arch, pn, uint32(relocType)) + if err != nil { + return nil, 0, err + } + if rela != 0 { + rAdd = int64(add) + } else { + // load addend from image + if rSize == 4 { + rAdd = int64(e.Uint32(sect.base[rOff:])) + } else if rSize == 8 { + rAdd = int64(e.Uint64(sect.base[rOff:])) + } else { + return errorf("invalid rela size %d", rSize) + } + } + + if rSize == 2 { + rAdd = int64(int16(rAdd)) + } + if rSize == 4 { + rAdd = int64(int32(rAdd)) + } + + r, _ := sb.AddRel(rType) + r.SetOff(rOff) + r.SetSiz(rSize) + r.SetSym(rSym) + r.SetAdd(rAdd) + } + + sb.SortRelocs() // just in case + } + + return textp, ehdrFlags, nil +} + +func section(elfobj *ElfObj, name string) *ElfSect { + for i := 0; uint(i) < elfobj.nsect; i++ { + if elfobj.sect[i].name != "" && name != "" && elfobj.sect[i].name == name { + return &elfobj.sect[i] + } + } + return nil +} + +func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { + if sect.base != nil { + return nil + } + + if sect.off+sect.size > uint64(elfobj.length) { + err = fmt.Errorf("elf section past end of file") + return err + } + + elfobj.f.MustSeek(int64(uint64(elfobj.base)+sect.off), 0) + sect.base, sect.readOnlyMem, err = elfobj.f.Slice(uint64(sect.size)) + if err != nil { + return fmt.Errorf("short read: %v", err) + } + + return nil +} + +func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) { + if i >= elfobj.nsymtab || i < 0 { + err = fmt.Errorf("invalid elf symbol index") + return err + } + + if i == 0 { + return fmt.Errorf("readym: read null symbol!") + } + + if elfobj.is64 != 0 { + b := new(elf.Sym64) + binary.Read(bytes.NewReader(elfobj.symtab.base[i*elf.Sym64Size:(i+1)*elf.Sym64Size]), elfobj.e, b) + elfsym.name = cstring(elfobj.symstr.base[b.Name:]) + elfsym.value = b.Value + elfsym.size = b.Size + elfsym.shndx = elf.SectionIndex(b.Shndx) + elfsym.bind = elf.ST_BIND(b.Info) + elfsym.type_ = elf.ST_TYPE(b.Info) + elfsym.other = b.Other + } else { + b := new(elf.Sym32) + binary.Read(bytes.NewReader(elfobj.symtab.base[i*elf.Sym32Size:(i+1)*elf.Sym32Size]), elfobj.e, b) + elfsym.name = cstring(elfobj.symstr.base[b.Name:]) + elfsym.value = uint64(b.Value) + elfsym.size = uint64(b.Size) + elfsym.shndx = elf.SectionIndex(b.Shndx) + elfsym.bind = elf.ST_BIND(b.Info) + elfsym.type_ = elf.ST_TYPE(b.Info) + elfsym.other = b.Other + } + + var s loader.Sym + + if elfsym.name == "_GLOBAL_OFFSET_TABLE_" { + elfsym.name = ".got" + } + if elfsym.name == ".TOC." { + // Magic symbol on ppc64. Will be set to this object + // file's .got+0x8000. + elfsym.bind = elf.STB_LOCAL + } + + switch elfsym.type_ { + case elf.STT_SECTION: + s = elfobj.sect[elfsym.shndx].sym + + case elf.STT_OBJECT, elf.STT_FUNC, elf.STT_NOTYPE, elf.STT_COMMON: + switch elfsym.bind { + case elf.STB_GLOBAL: + if needSym != 0 { + s = lookup(elfsym.name, 0) + + // for global scoped hidden symbols we should insert it into + // symbol hash table, but mark them as hidden. + // __i686.get_pc_thunk.bx is allowed to be duplicated, to + // workaround that we set dupok. + // TODO(minux): correctly handle __i686.get_pc_thunk.bx without + // set dupok generally. See https://golang.org/cl/5823055 + // comment #5 for details. + if s != 0 && elfsym.other == 2 { + if !l.IsExternal(s) { + l.MakeSymbolUpdater(s) + } + l.SetAttrDuplicateOK(s, true) + l.SetAttrVisibilityHidden(s, true) + } + } + + case elf.STB_LOCAL: + if (arch.Family == sys.ARM || arch.Family == sys.ARM64) && (strings.HasPrefix(elfsym.name, "$a") || strings.HasPrefix(elfsym.name, "$d") || strings.HasPrefix(elfsym.name, "$x")) { + // binutils for arm and arm64 generate these mapping + // symbols, ignore these + break + } + + if elfsym.name == ".TOC." { + // We need to be able to look this up, + // so put it in the hash table. + if needSym != 0 { + s = lookup(elfsym.name, localSymVersion) + l.SetAttrVisibilityHidden(s, true) + } + break + } + + if needSym != 0 { + // local names and hidden global names are unique + // and should only be referenced by their index, not name, so we + // don't bother to add them into the hash table + // FIXME: pass empty string here for name? This would + // reduce mem use, but also (possibly) make it harder + // to debug problems. + s = newSym(elfsym.name, localSymVersion) + l.SetAttrVisibilityHidden(s, true) + } + + case elf.STB_WEAK: + if needSym != 0 { + s = lookup(elfsym.name, 0) + if elfsym.other == 2 { + l.SetAttrVisibilityHidden(s, true) + } + + // Allow weak symbols to be duplicated when already defined. + if l.OuterSym(s) != 0 { + l.SetAttrDuplicateOK(s, true) + } + } + + default: + err = fmt.Errorf("%s: invalid symbol binding %d", elfsym.name, elfsym.bind) + return err + } + } + + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if s != 0 && l.SymType(s) == 0 && !l.AttrVisibilityHidden(s) && elfsym.type_ != elf.STT_SECTION { + sb := l.MakeSymbolUpdater(s) + sb.SetType(sym.SXREF) + } + elfsym.sym = s + + return nil +} + +func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, error) { + // TODO(mdempsky): Replace this with a struct-valued switch statement + // once golang.org/issue/15164 is fixed or found to not impair cmd/link + // performance. + + const ( + AMD64 = uint32(sys.AMD64) + ARM = uint32(sys.ARM) + ARM64 = uint32(sys.ARM64) + I386 = uint32(sys.I386) + MIPS = uint32(sys.MIPS) + MIPS64 = uint32(sys.MIPS64) + PPC64 = uint32(sys.PPC64) + RISCV64 = uint32(sys.RISCV64) + S390X = uint32(sys.S390X) + ) + + switch uint32(arch.Family) | elftype<<16 { + default: + return 0, fmt.Errorf("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype) + + case MIPS | uint32(elf.R_MIPS_HI16)<<16, + MIPS | uint32(elf.R_MIPS_LO16)<<16, + MIPS | uint32(elf.R_MIPS_GOT16)<<16, + MIPS | uint32(elf.R_MIPS_GOT_HI16)<<16, + MIPS | uint32(elf.R_MIPS_GOT_LO16)<<16, + MIPS | uint32(elf.R_MIPS_GPREL16)<<16, + MIPS | uint32(elf.R_MIPS_GOT_PAGE)<<16, + MIPS | uint32(elf.R_MIPS_JALR)<<16, + MIPS | uint32(elf.R_MIPS_GOT_OFST)<<16, + MIPS64 | uint32(elf.R_MIPS_HI16)<<16, + MIPS64 | uint32(elf.R_MIPS_LO16)<<16, + MIPS64 | uint32(elf.R_MIPS_GOT16)<<16, + MIPS64 | uint32(elf.R_MIPS_GOT_HI16)<<16, + MIPS64 | uint32(elf.R_MIPS_GOT_LO16)<<16, + MIPS64 | uint32(elf.R_MIPS_GPREL16)<<16, + MIPS64 | uint32(elf.R_MIPS_GOT_PAGE)<<16, + MIPS64 | uint32(elf.R_MIPS_JALR)<<16, + MIPS64 | uint32(elf.R_MIPS_GOT_OFST)<<16: + return 4, nil + + case S390X | uint32(elf.R_390_8)<<16: + return 1, nil + + case PPC64 | uint32(elf.R_PPC64_TOC16)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_LO)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_HI)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_HA)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_DS)<<16, + PPC64 | uint32(elf.R_PPC64_TOC16_LO_DS)<<16, + PPC64 | uint32(elf.R_PPC64_REL16_LO)<<16, + PPC64 | uint32(elf.R_PPC64_REL16_HI)<<16, + PPC64 | uint32(elf.R_PPC64_REL16_HA)<<16, + S390X | uint32(elf.R_390_16)<<16, + S390X | uint32(elf.R_390_GOT16)<<16, + S390X | uint32(elf.R_390_PC16)<<16, + S390X | uint32(elf.R_390_PC16DBL)<<16, + S390X | uint32(elf.R_390_PLT16DBL)<<16: + return 2, nil + + case ARM | uint32(elf.R_ARM_ABS32)<<16, + ARM | uint32(elf.R_ARM_GOT32)<<16, + ARM | uint32(elf.R_ARM_PLT32)<<16, + ARM | uint32(elf.R_ARM_GOTOFF)<<16, + ARM | uint32(elf.R_ARM_GOTPC)<<16, + ARM | uint32(elf.R_ARM_THM_PC22)<<16, + ARM | uint32(elf.R_ARM_REL32)<<16, + ARM | uint32(elf.R_ARM_CALL)<<16, + ARM | uint32(elf.R_ARM_V4BX)<<16, + ARM | uint32(elf.R_ARM_GOT_PREL)<<16, + ARM | uint32(elf.R_ARM_PC24)<<16, + ARM | uint32(elf.R_ARM_JUMP24)<<16, + ARM64 | uint32(elf.R_AARCH64_CALL26)<<16, + ARM64 | uint32(elf.R_AARCH64_ADR_GOT_PAGE)<<16, + ARM64 | uint32(elf.R_AARCH64_LD64_GOT_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_ADR_PREL_PG_HI21)<<16, + ARM64 | uint32(elf.R_AARCH64_ADD_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_LDST8_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_LDST16_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_LDST32_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_LDST64_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_LDST128_ABS_LO12_NC)<<16, + ARM64 | uint32(elf.R_AARCH64_PREL32)<<16, + ARM64 | uint32(elf.R_AARCH64_JUMP26)<<16, + AMD64 | uint32(elf.R_X86_64_PC32)<<16, + AMD64 | uint32(elf.R_X86_64_PLT32)<<16, + AMD64 | uint32(elf.R_X86_64_GOTPCREL)<<16, + AMD64 | uint32(elf.R_X86_64_GOTPCRELX)<<16, + AMD64 | uint32(elf.R_X86_64_REX_GOTPCRELX)<<16, + I386 | uint32(elf.R_386_32)<<16, + I386 | uint32(elf.R_386_PC32)<<16, + I386 | uint32(elf.R_386_GOT32)<<16, + I386 | uint32(elf.R_386_PLT32)<<16, + I386 | uint32(elf.R_386_GOTOFF)<<16, + I386 | uint32(elf.R_386_GOTPC)<<16, + I386 | uint32(elf.R_386_GOT32X)<<16, + PPC64 | uint32(elf.R_PPC64_REL24)<<16, + PPC64 | uint32(elf.R_PPC_REL32)<<16, + S390X | uint32(elf.R_390_32)<<16, + S390X | uint32(elf.R_390_PC32)<<16, + S390X | uint32(elf.R_390_GOT32)<<16, + S390X | uint32(elf.R_390_PLT32)<<16, + S390X | uint32(elf.R_390_PC32DBL)<<16, + S390X | uint32(elf.R_390_PLT32DBL)<<16, + S390X | uint32(elf.R_390_GOTPCDBL)<<16, + S390X | uint32(elf.R_390_GOTENT)<<16: + return 4, nil + + case AMD64 | uint32(elf.R_X86_64_64)<<16, + AMD64 | uint32(elf.R_X86_64_PC64)<<16, + ARM64 | uint32(elf.R_AARCH64_ABS64)<<16, + ARM64 | uint32(elf.R_AARCH64_PREL64)<<16, + PPC64 | uint32(elf.R_PPC64_ADDR64)<<16, + S390X | uint32(elf.R_390_GLOB_DAT)<<16, + S390X | uint32(elf.R_390_RELATIVE)<<16, + S390X | uint32(elf.R_390_GOTOFF)<<16, + S390X | uint32(elf.R_390_GOTPC)<<16, + S390X | uint32(elf.R_390_64)<<16, + S390X | uint32(elf.R_390_PC64)<<16, + S390X | uint32(elf.R_390_GOT64)<<16, + S390X | uint32(elf.R_390_PLT64)<<16: + return 8, 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 + } +} + +func cstring(x []byte) string { + i := bytes.IndexByte(x, '\x00') + if i >= 0 { + x = x[:i] + } + return string(x) +} diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go new file mode 100644 index 0000000..85b9489 --- /dev/null +++ b/src/cmd/link/internal/loader/loader.go @@ -0,0 +1,2683 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loader + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/goobj" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/sym" + "debug/elf" + "fmt" + "log" + "math/bits" + "os" + "sort" + "strings" +) + +var _ = fmt.Print + +// Sym encapsulates a global symbol index, used to identify a specific +// Go symbol. The 0-valued Sym is corresponds to an invalid symbol. +type Sym int + +// Relocs encapsulates the set of relocations on a given symbol; an +// instance of this type is returned by the Loader Relocs() method. +type Relocs struct { + rs []goobj.Reloc + + li uint32 // local index of symbol whose relocs we're examining + r *oReader // object reader for containing package + l *Loader // loader +} + +// ExtReloc contains the payload for an external relocation. +type ExtReloc struct { + Xsym Sym + Xadd int64 + Type objabi.RelocType + Size uint8 +} + +// Reloc holds a "handle" to access a relocation record from an +// object file. +type Reloc struct { + *goobj.Reloc + r *oReader + l *Loader + + // External reloc types may not fit into a uint8 which the Go object file uses. + // Store it here, instead of in the byte of goobj.Reloc. + // For Go symbols this will always be zero. + // goobj.Reloc.Type() + typ is always the right type, for both Go and external + // symbols. + typ objabi.RelocType +} + +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)) { + panic("SetType: type doesn't fit into Reloc") + } + rel.Reloc.SetType(uint8(t)) + if rel.typ != 0 { + // should use SymbolBuilder.SetRelocType + panic("wrong method to set reloc type") + } +} + +// Aux holds a "handle" to access an aux symbol record from an +// object file. +type Aux struct { + *goobj.Aux + r *oReader + l *Loader +} + +func (a Aux) Sym() Sym { return a.l.resolve(a.r, a.Aux.Sym()) } + +// oReader is a wrapper type of obj.Reader, along with some +// extra information. +type oReader struct { + *goobj.Reader + unit *sym.CompilationUnit + version int // version of static symbol + flags uint32 // read from object file + pkgprefix string + syms []Sym // Sym's global index, indexed by local index + pkg []uint32 // indices of referenced package by PkgIdx (index into loader.objs array) + ndef int // cache goobj.Reader.NSym() + nhashed64def int // cache goobj.Reader.NHashed64Def() + nhasheddef int // cache goobj.Reader.NHashedDef() + objidx uint32 // index of this reader in the objs slice +} + +// Total number of defined symbols (package symbols, hashed symbols, and +// non-package symbols). +func (r *oReader) NAlldef() int { return r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef() } + +type objIdx struct { + r *oReader + i Sym // start index +} + +// objSym represents a symbol in an object file. It is a tuple of +// the object and the symbol's local index. +// For external symbols, objidx is the index of l.extReader (extObj), +// s is its index into the payload array. +// {0, 0} represents the nil symbol. +type objSym struct { + objidx uint32 // index of the object (in l.objs array) + s uint32 // local index +} + +type nameVer struct { + name string + v int +} + +type Bitmap []uint32 + +// set the i-th bit. +func (bm Bitmap) Set(i Sym) { + n, r := uint(i)/32, uint(i)%32 + bm[n] |= 1 << r +} + +// unset the i-th bit. +func (bm Bitmap) Unset(i Sym) { + n, r := uint(i)/32, uint(i)%32 + bm[n] &^= (1 << r) +} + +// whether the i-th bit is set. +func (bm Bitmap) Has(i Sym) bool { + n, r := uint(i)/32, uint(i)%32 + return bm[n]&(1<<r) != 0 +} + +// return current length of bitmap in bits. +func (bm Bitmap) Len() int { + return len(bm) * 32 +} + +// return the number of bits set. +func (bm Bitmap) Count() int { + s := 0 + for _, x := range bm { + s += bits.OnesCount32(x) + } + return s +} + +func MakeBitmap(n int) Bitmap { + return make(Bitmap, (n+31)/32) +} + +// growBitmap insures that the specified bitmap has enough capacity, +// reallocating (doubling the size) if needed. +func growBitmap(reqLen int, b Bitmap) Bitmap { + curLen := b.Len() + if reqLen > curLen { + b = append(b, MakeBitmap(reqLen+1-curLen)...) + } + return b +} + +type symAndSize struct { + sym Sym + size uint32 +} + +// A Loader loads new object files and resolves indexed symbol references. +// +// Notes on the layout of global symbol index space: +// +// - Go object files are read before host object files; each Go object +// read adds its defined package symbols to the global index space. +// Nonpackage symbols are not yet added. +// +// - In loader.LoadNonpkgSyms, add non-package defined symbols and +// references in all object files to the global index space. +// +// - Host object file loading happens; the host object loader does a +// name/version lookup for each symbol it finds; this can wind up +// extending the external symbol index space range. The host object +// loader stores symbol payloads in loader.payloads using SymbolBuilder. +// +// - Each symbol gets a unique global index. For duplicated and +// overwriting/overwritten symbols, the second (or later) appearance +// of the symbol gets the same global index as the first appearance. +type Loader struct { + start map[*oReader]Sym // map from object file to its start index + objs []objIdx // sorted by start index (i.e. objIdx.i) + extStart Sym // from this index on, the symbols are externally defined + builtinSyms []Sym // global index of builtin symbols + + objSyms []objSym // global index mapping to local index + + symsByName [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal + extStaticSyms map[nameVer]Sym // externally defined static symbols, keyed by name + + extReader *oReader // a dummy oReader, for external symbols + payloadBatch []extSymPayload + payloads []*extSymPayload // contents of linker-materialized external syms + values []int64 // symbol values, indexed by global sym index + + sects []*sym.Section // sections + symSects []uint16 // symbol's section, index to sects array + + align []uint8 // symbol 2^N alignment, indexed by global index + + deferReturnTramp map[Sym]bool // whether the symbol is a trampoline of a deferreturn call + + objByPkg map[string]uint32 // map package path to the index of its Go object reader + + anonVersion int // most recently assigned ext static sym pseudo-version + + // Bitmaps and other side structures used to store data used to store + // symbol flags/attributes; these are to be accessed via the + // corresponding loader "AttrXXX" and "SetAttrXXX" methods. Please + // visit the comments on these methods for more details on the + // semantics / interpretation of the specific flags or attribute. + attrReachable Bitmap // reachable symbols, indexed by global index + attrOnList Bitmap // "on list" symbols, indexed by global index + attrLocal Bitmap // "local" symbols, indexed by global index + attrNotInSymbolTable Bitmap // "not in symtab" symbols, indexed by global idx + attrUsedInIface Bitmap // "used in interface" symbols, indexed by global idx + attrVisibilityHidden Bitmap // hidden symbols, indexed by ext sym index + attrDuplicateOK Bitmap // dupOK symbols, indexed by ext sym index + attrShared Bitmap // shared symbols, indexed by ext sym index + attrExternal Bitmap // external symbols, indexed by ext sym index + + attrReadOnly map[Sym]bool // readonly data for this sym + attrTopFrame map[Sym]struct{} // top frame symbols + attrSpecial map[Sym]struct{} // "special" frame symbols + attrCgoExportDynamic map[Sym]struct{} // "cgo_export_dynamic" symbols + attrCgoExportStatic map[Sym]struct{} // "cgo_export_static" symbols + generatedSyms map[Sym]struct{} // symbols that generate their content + + // Outer and Sub relations for symbols. + // TODO: figure out whether it's more efficient to just have these + // as fields on extSymPayload (note that this won't be a viable + // strategy if somewhere in the linker we set sub/outer for a + // non-external sym). + outer map[Sym]Sym + sub map[Sym]Sym + + dynimplib map[Sym]string // stores Dynimplib symbol attribute + dynimpvers map[Sym]string // stores Dynimpvers symbol attribute + localentry map[Sym]uint8 // stores Localentry symbol attribute + extname map[Sym]string // stores Extname symbol attribute + elfType map[Sym]elf.SymType // stores elf type symbol property + elfSym map[Sym]int32 // stores elf sym symbol property + localElfSym map[Sym]int32 // stores "local" elf sym symbol property + symPkg map[Sym]string // stores package for symbol, or library for shlib-derived syms + plt map[Sym]int32 // stores dynimport for pe objects + got map[Sym]int32 // stores got for pe objects + dynid map[Sym]int32 // stores Dynid for symbol + + relocVariant map[relocId]sym.RelocVariant // stores variant relocs + + // Used to implement field tracking; created during deadcode if + // field tracking is enabled. Reachparent[K] contains the index of + // the symbol that triggered the marking of symbol K as live. + Reachparent []Sym + + flags uint32 + + hasUnknownPkgPath bool // if any Go object has unknown package path + + strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled + + elfsetstring elfsetstringFunc + + errorReporter *ErrorReporter + + npkgsyms int // number of package symbols, for accounting + nhashedsyms int // number of hashed symbols, for accounting +} + +const ( + pkgDef = iota + hashed64Def + hashedDef + nonPkgDef + nonPkgRef +) + +// objidx +const ( + nilObj = iota + extObj + goObjStart +) + +type elfsetstringFunc func(str string, off int) + +// extSymPayload holds the payload (data + relocations) for linker-synthesized +// external symbols (note that symbol value is stored in a separate slice). +type extSymPayload struct { + name string // TODO: would this be better as offset into str table? + size int64 + ver int + kind sym.SymKind + objidx uint32 // index of original object if sym made by cloneToExternal + relocs []goobj.Reloc + reltypes []objabi.RelocType // relocation types + data []byte + auxs []goobj.Aux +} + +const ( + // Loader.flags + FlagStrictDups = 1 << iota +) + +func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorReporter) *Loader { + nbuiltin := goobj.NBuiltin() + extReader := &oReader{objidx: extObj} + ldr := &Loader{ + start: make(map[*oReader]Sym), + objs: []objIdx{{}, {extReader, 0}}, // reserve index 0 for nil symbol, 1 for external symbols + objSyms: make([]objSym, 1, 1), // This will get overwritten later. + extReader: extReader, + symsByName: [2]map[string]Sym{make(map[string]Sym, 80000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols + objByPkg: make(map[string]uint32), + outer: make(map[Sym]Sym), + sub: make(map[Sym]Sym), + dynimplib: make(map[Sym]string), + dynimpvers: make(map[Sym]string), + localentry: make(map[Sym]uint8), + extname: make(map[Sym]string), + attrReadOnly: make(map[Sym]bool), + elfType: make(map[Sym]elf.SymType), + elfSym: make(map[Sym]int32), + localElfSym: make(map[Sym]int32), + symPkg: make(map[Sym]string), + plt: make(map[Sym]int32), + got: make(map[Sym]int32), + dynid: make(map[Sym]int32), + attrTopFrame: make(map[Sym]struct{}), + attrSpecial: make(map[Sym]struct{}), + attrCgoExportDynamic: make(map[Sym]struct{}), + attrCgoExportStatic: make(map[Sym]struct{}), + generatedSyms: make(map[Sym]struct{}), + deferReturnTramp: make(map[Sym]bool), + extStaticSyms: make(map[nameVer]Sym), + builtinSyms: make([]Sym, nbuiltin), + flags: flags, + elfsetstring: elfsetstring, + errorReporter: reporter, + sects: []*sym.Section{nil}, // reserve index 0 for nil section + } + reporter.ldr = ldr + return ldr +} + +// Add object file r, return the start index. +func (l *Loader) addObj(pkg string, r *oReader) Sym { + if _, ok := l.start[r]; ok { + panic("already added") + } + pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path + if _, ok := l.objByPkg[pkg]; !ok { + l.objByPkg[pkg] = r.objidx + } + i := Sym(len(l.objSyms)) + l.start[r] = i + l.objs = append(l.objs, objIdx{r, i}) + if r.NeedNameExpansion() && !r.FromAssembly() { + l.hasUnknownPkgPath = true + } + return i +} + +// Add a symbol from an object file, return the global index. +// If the symbol already exist, it returns the index of that symbol. +func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind int, osym *goobj.Sym) Sym { + l := st.l + if l.extStart != 0 { + panic("addSym called after external symbol is created") + } + i := Sym(len(l.objSyms)) + addToGlobal := func() { + l.objSyms = append(l.objSyms, objSym{r.objidx, li}) + } + if name == "" && kind != hashed64Def && kind != hashedDef { + addToGlobal() + return i // unnamed aux symbol + } + if ver == r.version { + // Static symbol. Add its global index but don't + // add to name lookup table, as it cannot be + // referenced by name. + addToGlobal() + return i + } + switch kind { + case pkgDef: + // Defined package symbols cannot be dup to each other. + // We load all the package symbols first, so we don't need + // to check dup here. + // We still add it to the lookup table, as it may still be + // referenced by name (e.g. through linkname). + l.symsByName[ver][name] = i + addToGlobal() + return i + case hashed64Def, hashedDef: + // Hashed (content-addressable) symbol. Check the hash + // but don't add to name lookup table, as they are not + // referenced by name. Also no need to do overwriting + // check, as same hash indicates same content. + var checkHash func() (symAndSize, bool) + var addToHashMap func(symAndSize) + var h64 uint64 // only used for hashed64Def + var h *goobj.HashType // only used for hashedDef + if kind == hashed64Def { + checkHash = func() (symAndSize, bool) { + h64 = r.Hash64(li - uint32(r.ndef)) + s, existed := st.hashed64Syms[h64] + return s, existed + } + addToHashMap = func(ss symAndSize) { st.hashed64Syms[h64] = ss } + } else { + checkHash = func() (symAndSize, bool) { + h = r.Hash(li - uint32(r.ndef+r.nhashed64def)) + s, existed := st.hashedSyms[*h] + return s, existed + } + addToHashMap = func(ss symAndSize) { st.hashedSyms[*h] = ss } + } + siz := osym.Siz() + if s, existed := checkHash(); existed { + // The content hash is built from symbol data and relocations. In the + // object file, the symbol data may not always contain trailing zeros, + // e.g. for [5]int{1,2,3} and [100]int{1,2,3}, the data is same + // (although the size is different). + // Also, for short symbols, the content hash is the identity function of + // the 8 bytes, and trailing zeros doesn't change the hash value, e.g. + // hash("A") == hash("A\0\0\0"). + // So when two symbols have the same hash, we need to use the one with + // larger size. + if siz > s.size { + // New symbol has larger size, use the new one. Rewrite the index mapping. + l.objSyms[s.sym] = objSym{r.objidx, li} + addToHashMap(symAndSize{s.sym, siz}) + } + return s.sym + } + addToHashMap(symAndSize{i, siz}) + addToGlobal() + return i + } + + // Non-package (named) symbol. Check if it already exists. + oldi, existed := l.symsByName[ver][name] + if !existed { + l.symsByName[ver][name] = i + addToGlobal() + return i + } + // symbol already exists + if osym.Dupok() { + if l.flags&FlagStrictDups != 0 { + l.checkdup(name, r, li, oldi) + } + // Fix for issue #47185 -- given two dupok symbols with + // different sizes, favor symbol with larger size. See + // also issue #46653. + szdup := l.SymSize(oldi) + sz := int64(r.Sym(li).Siz()) + if szdup < sz { + // new symbol overwrites old symbol. + l.objSyms[oldi] = objSym{r.objidx, li} + } + return oldi + } + oldr, oldli := l.toLocal(oldi) + oldsym := oldr.Sym(oldli) + if oldsym.Dupok() { + return oldi + } + overwrite := r.DataSize(li) != 0 + if overwrite { + // new symbol overwrites old symbol. + oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())] + if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) { + log.Fatalf("duplicated definition of symbol " + name) + } + l.objSyms[oldi] = objSym{r.objidx, li} + } else { + // old symbol overwrites new symbol. + typ := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())] + if !typ.IsData() { // only allow overwriting data symbol + log.Fatalf("duplicated definition of symbol " + name) + } + } + return oldi +} + +// newExtSym creates a new external sym with the specified +// name/version. +func (l *Loader) newExtSym(name string, ver int) Sym { + i := Sym(len(l.objSyms)) + if l.extStart == 0 { + l.extStart = i + } + l.growValues(int(i) + 1) + l.growAttrBitmaps(int(i) + 1) + pi := l.newPayload(name, ver) + l.objSyms = append(l.objSyms, objSym{l.extReader.objidx, uint32(pi)}) + l.extReader.syms = append(l.extReader.syms, i) + return i +} + +// LookupOrCreateSym looks up the symbol with the specified name/version, +// returning its Sym index if found. If the lookup fails, a new external +// Sym will be created, entered into the lookup tables, and returned. +func (l *Loader) LookupOrCreateSym(name string, ver int) Sym { + i := l.Lookup(name, ver) + if i != 0 { + return i + } + i = l.newExtSym(name, ver) + static := ver >= sym.SymVerStatic || ver < 0 + if static { + l.extStaticSyms[nameVer{name, ver}] = i + } else { + l.symsByName[ver][name] = i + } + return i +} + +func (l *Loader) IsExternal(i Sym) bool { + r, _ := l.toLocal(i) + return l.isExtReader(r) +} + +func (l *Loader) isExtReader(r *oReader) bool { + return r == l.extReader +} + +// For external symbol, return its index in the payloads array. +// XXX result is actually not a global index. We (ab)use the Sym type +// so we don't need conversion for accessing bitmaps. +func (l *Loader) extIndex(i Sym) Sym { + _, li := l.toLocal(i) + return Sym(li) +} + +// Get a new payload for external symbol, return its index in +// the payloads array. +func (l *Loader) newPayload(name string, ver int) int { + pi := len(l.payloads) + pp := l.allocPayload() + pp.name = name + pp.ver = ver + l.payloads = append(l.payloads, pp) + l.growExtAttrBitmaps() + return pi +} + +// getPayload returns a pointer to the extSymPayload struct for an +// external symbol if the symbol has a payload. Will panic if the +// symbol in question is bogus (zero or not an external sym). +func (l *Loader) getPayload(i Sym) *extSymPayload { + if !l.IsExternal(i) { + panic(fmt.Sprintf("bogus symbol index %d in getPayload", i)) + } + pi := l.extIndex(i) + return l.payloads[pi] +} + +// allocPayload allocates a new payload. +func (l *Loader) allocPayload() *extSymPayload { + batch := l.payloadBatch + if len(batch) == 0 { + batch = make([]extSymPayload, 1000) + } + p := &batch[0] + l.payloadBatch = batch[1:] + return p +} + +func (ms *extSymPayload) Grow(siz int64) { + if int64(int(siz)) != siz { + log.Fatalf("symgrow size %d too long", siz) + } + if int64(len(ms.data)) >= siz { + return + } + if cap(ms.data) < int(siz) { + cl := len(ms.data) + ms.data = append(ms.data, make([]byte, int(siz)+1-cl)...) + ms.data = ms.data[0:cl] + } + ms.data = ms.data[:siz] +} + +// Convert a local index to a global index. +func (l *Loader) toGlobal(r *oReader, i uint32) Sym { + return r.syms[i] +} + +// Convert a global index to a local index. +func (l *Loader) toLocal(i Sym) (*oReader, uint32) { + return l.objs[l.objSyms[i].objidx].r, l.objSyms[i].s +} + +// Resolve a local symbol reference. Return global index. +func (l *Loader) resolve(r *oReader, s goobj.SymRef) Sym { + var rr *oReader + switch p := s.PkgIdx; p { + case goobj.PkgIdxInvalid: + // {0, X} with non-zero X is never a valid sym reference from a Go object. + // We steal this space for symbol references from external objects. + // In this case, X is just the global index. + if l.isExtReader(r) { + return Sym(s.SymIdx) + } + if s.SymIdx != 0 { + panic("bad sym ref") + } + return 0 + case goobj.PkgIdxHashed64: + i := int(s.SymIdx) + r.ndef + return r.syms[i] + case goobj.PkgIdxHashed: + i := int(s.SymIdx) + r.ndef + r.nhashed64def + return r.syms[i] + case goobj.PkgIdxNone: + i := int(s.SymIdx) + r.ndef + r.nhashed64def + r.nhasheddef + return r.syms[i] + case goobj.PkgIdxBuiltin: + if bi := l.builtinSyms[s.SymIdx]; bi != 0 { + return bi + } + l.reportMissingBuiltin(int(s.SymIdx), r.unit.Lib.Pkg) + return 0 + case goobj.PkgIdxSelf: + rr = r + default: + rr = l.objs[r.pkg[p]].r + } + return l.toGlobal(rr, s.SymIdx) +} + +// reportMissingBuiltin issues an error in the case where we have a +// relocation against a runtime builtin whose definition is not found +// when the runtime package is built. The canonical example is +// "runtime.racefuncenter" -- currently if you do something like +// +// go build -gcflags=-race myprogram.go +// +// the compiler will insert calls to the builtin runtime.racefuncenter, +// but the version of the runtime used for linkage won't actually contain +// definitions of that symbol. See issue #42396 for details. +// +// As currently implemented, this is a fatal error. This has drawbacks +// in that if there are multiple missing builtins, the error will only +// cite the first one. On the plus side, terminating the link here has +// advantages in that we won't run the risk of panics or crashes later +// on in the linker due to R_CALL relocations with 0-valued target +// symbols. +func (l *Loader) reportMissingBuiltin(bsym int, reflib string) { + bname, _ := goobj.BuiltinName(bsym) + log.Fatalf("reference to undefined builtin %q from package %q", + bname, reflib) +} + +// Look up a symbol by name, return global index, or 0 if not found. +// This is more like Syms.ROLookup than Lookup -- it doesn't create +// new symbol. +func (l *Loader) Lookup(name string, ver int) Sym { + if ver >= sym.SymVerStatic || ver < 0 { + return l.extStaticSyms[nameVer{name, ver}] + } + return l.symsByName[ver][name] +} + +// Check that duplicate symbols have same contents. +func (l *Loader) checkdup(name string, r *oReader, li uint32, dup Sym) { + p := r.Data(li) + rdup, ldup := l.toLocal(dup) + pdup := rdup.Data(ldup) + if bytes.Equal(p, pdup) { + return + } + reason := "same length but different contents" + if len(p) != len(pdup) { + reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup)) + } + fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason) + + // For the moment, allow DWARF subprogram DIEs for + // auto-generated wrapper functions. What seems to happen + // here is that we get different line numbers on formal + // params; I am guessing that the pos is being inherited + // from the spot where the wrapper is needed. + allowed := strings.HasPrefix(name, "go.info.go.interface") || + strings.HasPrefix(name, "go.info.go.builtin") || + strings.HasPrefix(name, "go.debuglines") + if !allowed { + l.strictDupMsgs++ + } +} + +func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs } + +// Number of total symbols. +func (l *Loader) NSym() int { + return len(l.objSyms) +} + +// Number of defined Go symbols. +func (l *Loader) NDef() int { + return int(l.extStart) +} + +// Number of reachable symbols. +func (l *Loader) NReachableSym() int { + return l.attrReachable.Count() +} + +// SymNameLen returns the length of the symbol name, trying hard not to load +// the name. +func (l *Loader) SymNameLen(i Sym) int { + // Not much we can do about external symbols. + if l.IsExternal(i) { + return len(l.SymName(i)) + } + r, li := l.toLocal(i) + le := r.Sym(li).NameLen(r.Reader) + if !r.NeedNameExpansion() { + return le + } + // Just load the symbol name. We don't know how expanded it'll be. + return len(l.SymName(i)) +} + +// Returns the raw (unpatched) name of the i-th symbol. +func (l *Loader) RawSymName(i Sym) string { + if l.IsExternal(i) { + pp := l.getPayload(i) + return pp.name + } + r, li := l.toLocal(i) + return r.Sym(li).Name(r.Reader) +} + +// Returns the (patched) name of the i-th symbol. +func (l *Loader) SymName(i Sym) string { + if l.IsExternal(i) { + pp := l.getPayload(i) + return pp.name + } + r, li := l.toLocal(i) + name := r.Sym(li).Name(r.Reader) + if !r.NeedNameExpansion() { + return name + } + return strings.Replace(name, "\"\".", r.pkgprefix, -1) +} + +// Returns the version of the i-th symbol. +func (l *Loader) SymVersion(i Sym) int { + if l.IsExternal(i) { + pp := l.getPayload(i) + return pp.ver + } + r, li := l.toLocal(i) + return int(abiToVer(r.Sym(li).ABI(), r.version)) +} + +func (l *Loader) IsFileLocal(i Sym) bool { + return l.SymVersion(i) >= sym.SymVerStatic +} + +// IsFromAssembly returns true if this symbol is derived from an +// object file generated by the Go assembler. +func (l *Loader) IsFromAssembly(i Sym) bool { + if l.IsExternal(i) { + return false + } + r, _ := l.toLocal(i) + return r.FromAssembly() +} + +// Returns the type of the i-th symbol. +func (l *Loader) SymType(i Sym) sym.SymKind { + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp != nil { + return pp.kind + } + return 0 + } + r, li := l.toLocal(i) + return sym.AbiSymKindToSymKind[objabi.SymKind(r.Sym(li).Type())] +} + +// Returns the attributes of the i-th symbol. +func (l *Loader) SymAttr(i Sym) uint8 { + if l.IsExternal(i) { + // TODO: do something? External symbols have different representation of attributes. + // For now, ReflectMethod, NoSplit, GoType, and Typelink are used and they cannot be + // set by external symbol. + return 0 + } + r, li := l.toLocal(i) + return r.Sym(li).Flag() +} + +// Returns the size of the i-th symbol. +func (l *Loader) SymSize(i Sym) int64 { + if l.IsExternal(i) { + pp := l.getPayload(i) + return pp.size + } + r, li := l.toLocal(i) + return int64(r.Sym(li).Siz()) +} + +// AttrReachable returns true for symbols that are transitively +// referenced from the entry points. Unreachable symbols are not +// written to the output. +func (l *Loader) AttrReachable(i Sym) bool { + return l.attrReachable.Has(i) +} + +// SetAttrReachable sets the reachability property for a symbol (see +// AttrReachable). +func (l *Loader) SetAttrReachable(i Sym, v bool) { + if v { + l.attrReachable.Set(i) + } else { + l.attrReachable.Unset(i) + } +} + +// AttrOnList returns true for symbols that are on some list (such as +// the list of all text symbols, or one of the lists of data symbols) +// and is consulted to avoid bugs where a symbol is put on a list +// twice. +func (l *Loader) AttrOnList(i Sym) bool { + return l.attrOnList.Has(i) +} + +// SetAttrOnList sets the "on list" property for a symbol (see +// AttrOnList). +func (l *Loader) SetAttrOnList(i Sym, v bool) { + if v { + l.attrOnList.Set(i) + } else { + l.attrOnList.Unset(i) + } +} + +// AttrLocal returns true for symbols that are only visible within the +// module (executable or shared library) being linked. This attribute +// is applied to thunks and certain other linker-generated symbols. +func (l *Loader) AttrLocal(i Sym) bool { + return l.attrLocal.Has(i) +} + +// SetAttrLocal the "local" property for a symbol (see AttrLocal above). +func (l *Loader) SetAttrLocal(i Sym, v bool) { + if v { + l.attrLocal.Set(i) + } else { + l.attrLocal.Unset(i) + } +} + +// AttrUsedInIface returns true for a type symbol that is used in +// an interface. +func (l *Loader) AttrUsedInIface(i Sym) bool { + return l.attrUsedInIface.Has(i) +} + +func (l *Loader) SetAttrUsedInIface(i Sym, v bool) { + if v { + l.attrUsedInIface.Set(i) + } else { + l.attrUsedInIface.Unset(i) + } +} + +// SymAddr checks that a symbol is reachable, and returns its value. +func (l *Loader) SymAddr(i Sym) int64 { + if !l.AttrReachable(i) { + panic("unreachable symbol in symaddr") + } + return l.values[i] +} + +// AttrNotInSymbolTable returns true for symbols that should not be +// added to the symbol table of the final generated load module. +func (l *Loader) AttrNotInSymbolTable(i Sym) bool { + return l.attrNotInSymbolTable.Has(i) +} + +// SetAttrNotInSymbolTable the "not in symtab" property for a symbol +// (see AttrNotInSymbolTable above). +func (l *Loader) SetAttrNotInSymbolTable(i Sym, v bool) { + if v { + l.attrNotInSymbolTable.Set(i) + } else { + l.attrNotInSymbolTable.Unset(i) + } +} + +// AttrVisibilityHidden symbols returns true for ELF symbols with +// visibility set to STV_HIDDEN. They become local symbols in +// the final executable. Only relevant when internally linking +// on an ELF platform. +func (l *Loader) AttrVisibilityHidden(i Sym) bool { + if !l.IsExternal(i) { + return false + } + return l.attrVisibilityHidden.Has(l.extIndex(i)) +} + +// SetAttrVisibilityHidden sets the "hidden visibility" property for a +// symbol (see AttrVisibilityHidden). +func (l *Loader) SetAttrVisibilityHidden(i Sym, v bool) { + if !l.IsExternal(i) { + panic("tried to set visibility attr on non-external symbol") + } + if v { + l.attrVisibilityHidden.Set(l.extIndex(i)) + } else { + l.attrVisibilityHidden.Unset(l.extIndex(i)) + } +} + +// AttrDuplicateOK returns true for a symbol that can be present in +// multiple object files. +func (l *Loader) AttrDuplicateOK(i Sym) bool { + if !l.IsExternal(i) { + // TODO: if this path winds up being taken frequently, it + // might make more sense to copy the flag value out of the object + // into a larger bitmap during preload. + r, li := l.toLocal(i) + return r.Sym(li).Dupok() + } + return l.attrDuplicateOK.Has(l.extIndex(i)) +} + +// SetAttrDuplicateOK sets the "duplicate OK" property for an external +// symbol (see AttrDuplicateOK). +func (l *Loader) SetAttrDuplicateOK(i Sym, v bool) { + if !l.IsExternal(i) { + panic("tried to set dupok attr on non-external symbol") + } + if v { + l.attrDuplicateOK.Set(l.extIndex(i)) + } else { + l.attrDuplicateOK.Unset(l.extIndex(i)) + } +} + +// AttrShared returns true for symbols compiled with the -shared option. +func (l *Loader) AttrShared(i Sym) bool { + if !l.IsExternal(i) { + // TODO: if this path winds up being taken frequently, it + // might make more sense to copy the flag value out of the + // object into a larger bitmap during preload. + r, _ := l.toLocal(i) + return r.Shared() + } + return l.attrShared.Has(l.extIndex(i)) +} + +// SetAttrShared sets the "shared" property for an external +// symbol (see AttrShared). +func (l *Loader) SetAttrShared(i Sym, v bool) { + if !l.IsExternal(i) { + panic(fmt.Sprintf("tried to set shared attr on non-external symbol %d %s", i, l.SymName(i))) + } + if v { + l.attrShared.Set(l.extIndex(i)) + } else { + l.attrShared.Unset(l.extIndex(i)) + } +} + +// AttrExternal returns true for function symbols loaded from host +// object files. +func (l *Loader) AttrExternal(i Sym) bool { + if !l.IsExternal(i) { + return false + } + return l.attrExternal.Has(l.extIndex(i)) +} + +// SetAttrExternal sets the "external" property for an host object +// symbol (see AttrExternal). +func (l *Loader) SetAttrExternal(i Sym, v bool) { + if !l.IsExternal(i) { + panic(fmt.Sprintf("tried to set external attr on non-external symbol %q", l.RawSymName(i))) + } + if v { + l.attrExternal.Set(l.extIndex(i)) + } else { + l.attrExternal.Unset(l.extIndex(i)) + } +} + +// AttrTopFrame returns true for a function symbol that is an entry +// point, meaning that unwinders should stop when they hit this +// function. +func (l *Loader) AttrTopFrame(i Sym) bool { + _, ok := l.attrTopFrame[i] + return ok +} + +// SetAttrTopFrame sets the "top frame" property for a symbol (see +// AttrTopFrame). +func (l *Loader) SetAttrTopFrame(i Sym, v bool) { + if v { + l.attrTopFrame[i] = struct{}{} + } else { + delete(l.attrTopFrame, i) + } +} + +// AttrSpecial returns true for a symbols that do not have their +// address (i.e. Value) computed by the usual mechanism of +// data.go:dodata() & data.go:address(). +func (l *Loader) AttrSpecial(i Sym) bool { + _, ok := l.attrSpecial[i] + return ok +} + +// SetAttrSpecial sets the "special" property for a symbol (see +// AttrSpecial). +func (l *Loader) SetAttrSpecial(i Sym, v bool) { + if v { + l.attrSpecial[i] = struct{}{} + } else { + delete(l.attrSpecial, i) + } +} + +// AttrCgoExportDynamic returns true for a symbol that has been +// specially marked via the "cgo_export_dynamic" compiler directive +// written by cgo (in response to //export directives in the source). +func (l *Loader) AttrCgoExportDynamic(i Sym) bool { + _, ok := l.attrCgoExportDynamic[i] + return ok +} + +// SetAttrCgoExportDynamic sets the "cgo_export_dynamic" for a symbol +// (see AttrCgoExportDynamic). +func (l *Loader) SetAttrCgoExportDynamic(i Sym, v bool) { + if v { + l.attrCgoExportDynamic[i] = struct{}{} + } else { + delete(l.attrCgoExportDynamic, i) + } +} + +// AttrCgoExportStatic returns true for a symbol that has been +// specially marked via the "cgo_export_static" directive +// written by cgo. +func (l *Loader) AttrCgoExportStatic(i Sym) bool { + _, ok := l.attrCgoExportStatic[i] + return ok +} + +// SetAttrCgoExportStatic sets the "cgo_export_static" for a symbol +// (see AttrCgoExportStatic). +func (l *Loader) SetAttrCgoExportStatic(i Sym, v bool) { + if v { + l.attrCgoExportStatic[i] = struct{}{} + } else { + delete(l.attrCgoExportStatic, i) + } +} + +// IsGeneratedSym returns true if a symbol's been previously marked as a +// generator symbol through the SetIsGeneratedSym. The functions for generator +// symbols are kept in the Link context. +func (l *Loader) IsGeneratedSym(i Sym) bool { + _, ok := l.generatedSyms[i] + return ok +} + +// SetIsGeneratedSym marks symbols as generated symbols. Data shouldn't be +// stored in generated symbols, and a function is registered and called for +// each of these symbols. +func (l *Loader) SetIsGeneratedSym(i Sym, v bool) { + if !l.IsExternal(i) { + panic("only external symbols can be generated") + } + if v { + l.generatedSyms[i] = struct{}{} + } else { + delete(l.generatedSyms, i) + } +} + +func (l *Loader) AttrCgoExport(i Sym) bool { + return l.AttrCgoExportDynamic(i) || l.AttrCgoExportStatic(i) +} + +// AttrReadOnly returns true for a symbol whose underlying data +// is stored via a read-only mmap. +func (l *Loader) AttrReadOnly(i Sym) bool { + if v, ok := l.attrReadOnly[i]; ok { + return v + } + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp.objidx != 0 { + return l.objs[pp.objidx].r.ReadOnly() + } + return false + } + r, _ := l.toLocal(i) + return r.ReadOnly() +} + +// SetAttrReadOnly sets the "data is read only" property for a symbol +// (see AttrReadOnly). +func (l *Loader) SetAttrReadOnly(i Sym, v bool) { + l.attrReadOnly[i] = v +} + +// AttrSubSymbol returns true for symbols that are listed as a +// sub-symbol of some other outer symbol. The sub/outer mechanism is +// used when loading host objects (sections from the host object +// become regular linker symbols and symbols go on the Sub list of +// their section) and for constructing the global offset table when +// internally linking a dynamic executable. +// +// Note that in later stages of the linker, we set Outer(S) to some +// container symbol C, but don't set Sub(C). Thus we have two +// distinct scenarios: +// +// - Outer symbol covers the address ranges of its sub-symbols. +// Outer.Sub is set in this case. +// - Outer symbol doesn't conver the address ranges. It is zero-sized +// and doesn't have sub-symbols. In the case, the inner symbol is +// not actually a "SubSymbol". (Tricky!) +// +// This method returns TRUE only for sub-symbols in the first scenario. +// +// FIXME: would be better to do away with this and have a better way +// to represent container symbols. + +func (l *Loader) AttrSubSymbol(i Sym) bool { + // we don't explicitly store this attribute any more -- return + // a value based on the sub-symbol setting. + o := l.OuterSym(i) + if o == 0 { + return false + } + return l.SubSym(o) != 0 +} + +// Note that we don't have a 'SetAttrSubSymbol' method in the loader; +// clients should instead use the AddInteriorSym method to establish +// containment relationships for host object symbols. + +// Returns whether the i-th symbol has ReflectMethod attribute set. +func (l *Loader) IsReflectMethod(i Sym) bool { + return l.SymAttr(i)&goobj.SymFlagReflectMethod != 0 +} + +// Returns whether the i-th symbol is nosplit. +func (l *Loader) IsNoSplit(i Sym) bool { + return l.SymAttr(i)&goobj.SymFlagNoSplit != 0 +} + +// Returns whether this is a Go type symbol. +func (l *Loader) IsGoType(i Sym) bool { + return l.SymAttr(i)&goobj.SymFlagGoType != 0 +} + +// Returns whether this symbol should be included in typelink. +func (l *Loader) IsTypelink(i Sym) bool { + return l.SymAttr(i)&goobj.SymFlagTypelink != 0 +} + +// Returns whether this symbol is an itab symbol. +func (l *Loader) IsItab(i Sym) bool { + if l.IsExternal(i) { + return false + } + r, li := l.toLocal(i) + return r.Sym(li).IsItab() +} + +// Return whether this is a trampoline of a deferreturn call. +func (l *Loader) IsDeferReturnTramp(i Sym) bool { + return l.deferReturnTramp[i] +} + +// Set that i is a trampoline of a deferreturn call. +func (l *Loader) SetIsDeferReturnTramp(i Sym, v bool) { + l.deferReturnTramp[i] = v +} + +// growValues grows the slice used to store symbol values. +func (l *Loader) growValues(reqLen int) { + curLen := len(l.values) + if reqLen > curLen { + l.values = append(l.values, make([]int64, reqLen+1-curLen)...) + } +} + +// SymValue returns the value of the i-th symbol. i is global index. +func (l *Loader) SymValue(i Sym) int64 { + return l.values[i] +} + +// SetSymValue sets the value of the i-th symbol. i is global index. +func (l *Loader) SetSymValue(i Sym, val int64) { + l.values[i] = val +} + +// AddToSymValue adds to the value of the i-th symbol. i is the global index. +func (l *Loader) AddToSymValue(i Sym, val int64) { + l.values[i] += val +} + +// Returns the symbol content of the i-th symbol. i is global index. +func (l *Loader) Data(i Sym) []byte { + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp != nil { + return pp.data + } + return nil + } + r, li := l.toLocal(i) + return r.Data(li) +} + +// FreeData clears the symbol data of an external symbol, allowing the memory +// to be freed earlier. No-op for non-external symbols. +// i is global index. +func (l *Loader) FreeData(i Sym) { + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp != nil { + pp.data = nil + } + } +} + +// SymAlign returns the alignment for a symbol. +func (l *Loader) SymAlign(i Sym) int32 { + if int(i) >= len(l.align) { + // align is extended lazily -- it the sym in question is + // outside the range of the existing slice, then we assume its + // alignment has not yet been set. + return 0 + } + // TODO: would it make sense to return an arch-specific + // alignment depending on section type? E.g. STEXT => 32, + // SDATA => 1, etc? + abits := l.align[i] + if abits == 0 { + return 0 + } + return int32(1 << (abits - 1)) +} + +// SetSymAlign sets the alignment for a symbol. +func (l *Loader) SetSymAlign(i Sym, align int32) { + // Reject nonsense alignments. + if align < 0 || align&(align-1) != 0 { + panic("bad alignment value") + } + if int(i) >= len(l.align) { + l.align = append(l.align, make([]uint8, l.NSym()-len(l.align))...) + } + if align == 0 { + l.align[i] = 0 + } + l.align[i] = uint8(bits.Len32(uint32(align))) +} + +// SymValue returns the section of the i-th symbol. i is global index. +func (l *Loader) SymSect(i Sym) *sym.Section { + if int(i) >= len(l.symSects) { + // symSects is extended lazily -- it the sym in question is + // outside the range of the existing slice, then we assume its + // section has not yet been set. + return nil + } + return l.sects[l.symSects[i]] +} + +// SetSymSect sets the section of the i-th symbol. i is global index. +func (l *Loader) SetSymSect(i Sym, sect *sym.Section) { + if int(i) >= len(l.symSects) { + l.symSects = append(l.symSects, make([]uint16, l.NSym()-len(l.symSects))...) + } + l.symSects[i] = sect.Index +} + +// growSects grows the slice used to store symbol sections. +func (l *Loader) growSects(reqLen int) { + curLen := len(l.symSects) + if reqLen > curLen { + l.symSects = append(l.symSects, make([]uint16, reqLen+1-curLen)...) + } +} + +// NewSection creates a new (output) section. +func (l *Loader) NewSection() *sym.Section { + sect := new(sym.Section) + idx := len(l.sects) + if idx != int(uint16(idx)) { + panic("too many sections created") + } + sect.Index = uint16(idx) + l.sects = append(l.sects, sect) + return sect +} + +// SymDynImplib returns the "dynimplib" attribute for the specified +// symbol, making up a portion of the info for a symbol specified +// on a "cgo_import_dynamic" compiler directive. +func (l *Loader) SymDynimplib(i Sym) string { + return l.dynimplib[i] +} + +// SetSymDynimplib sets the "dynimplib" attribute for a symbol. +func (l *Loader) SetSymDynimplib(i Sym, value string) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetDynimplib") + } + if value == "" { + delete(l.dynimplib, i) + } else { + l.dynimplib[i] = value + } +} + +// SymDynimpvers returns the "dynimpvers" attribute for the specified +// symbol, making up a portion of the info for a symbol specified +// on a "cgo_import_dynamic" compiler directive. +func (l *Loader) SymDynimpvers(i Sym) string { + return l.dynimpvers[i] +} + +// SetSymDynimpvers sets the "dynimpvers" attribute for a symbol. +func (l *Loader) SetSymDynimpvers(i Sym, value string) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetDynimpvers") + } + if value == "" { + delete(l.dynimpvers, i) + } else { + l.dynimpvers[i] = value + } +} + +// SymExtname returns the "extname" value for the specified +// symbol. +func (l *Loader) SymExtname(i Sym) string { + if s, ok := l.extname[i]; ok { + return s + } + return l.SymName(i) +} + +// SetSymExtname sets the "extname" attribute for a symbol. +func (l *Loader) SetSymExtname(i Sym, value string) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetExtname") + } + if value == "" { + delete(l.extname, i) + } else { + l.extname[i] = value + } +} + +// SymElfType returns the previously recorded ELF type for a symbol +// (used only for symbols read from shared libraries by ldshlibsyms). +// It is not set for symbols defined by the packages being linked or +// by symbols read by ldelf (and so is left as elf.STT_NOTYPE). +func (l *Loader) SymElfType(i Sym) elf.SymType { + if et, ok := l.elfType[i]; ok { + return et + } + return elf.STT_NOTYPE +} + +// SetSymElfType sets the elf type attribute for a symbol. +func (l *Loader) SetSymElfType(i Sym, et elf.SymType) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetSymElfType") + } + if et == elf.STT_NOTYPE { + delete(l.elfType, i) + } else { + l.elfType[i] = et + } +} + +// SymElfSym returns the ELF symbol index for a given loader +// symbol, assigned during ELF symtab generation. +func (l *Loader) SymElfSym(i Sym) int32 { + return l.elfSym[i] +} + +// SetSymElfSym sets the elf symbol index for a symbol. +func (l *Loader) SetSymElfSym(i Sym, es int32) { + if i == 0 { + panic("bad sym index") + } + if es == 0 { + delete(l.elfSym, i) + } else { + l.elfSym[i] = es + } +} + +// SymLocalElfSym returns the "local" ELF symbol index for a given loader +// symbol, assigned during ELF symtab generation. +func (l *Loader) SymLocalElfSym(i Sym) int32 { + return l.localElfSym[i] +} + +// SetSymLocalElfSym sets the "local" elf symbol index for a symbol. +func (l *Loader) SetSymLocalElfSym(i Sym, es int32) { + if i == 0 { + panic("bad sym index") + } + if es == 0 { + delete(l.localElfSym, i) + } else { + l.localElfSym[i] = es + } +} + +// SymPlt returns the plt value for pe symbols. +func (l *Loader) SymPlt(s Sym) int32 { + if v, ok := l.plt[s]; ok { + return v + } + return -1 +} + +// SetPlt sets the plt value for pe symbols. +func (l *Loader) SetPlt(i Sym, v int32) { + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol for SetPlt") + } + if v == -1 { + delete(l.plt, i) + } else { + l.plt[i] = v + } +} + +// SymGot returns the got value for pe symbols. +func (l *Loader) SymGot(s Sym) int32 { + if v, ok := l.got[s]; ok { + return v + } + return -1 +} + +// SetGot sets the got value for pe symbols. +func (l *Loader) SetGot(i Sym, v int32) { + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol for SetGot") + } + if v == -1 { + delete(l.got, i) + } else { + l.got[i] = v + } +} + +// SymDynid returns the "dynid" property for the specified symbol. +func (l *Loader) SymDynid(i Sym) int32 { + if s, ok := l.dynid[i]; ok { + return s + } + return -1 +} + +// SetSymDynid sets the "dynid" property for a symbol. +func (l *Loader) SetSymDynid(i Sym, val int32) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetSymDynid") + } + if val == -1 { + delete(l.dynid, i) + } else { + l.dynid[i] = val + } +} + +// DynIdSyms returns the set of symbols for which dynID is set to an +// interesting (non-default) value. This is expected to be a fairly +// small set. +func (l *Loader) DynidSyms() []Sym { + sl := make([]Sym, 0, len(l.dynid)) + for s := range l.dynid { + sl = append(sl, s) + } + sort.Slice(sl, func(i, j int) bool { return sl[i] < sl[j] }) + return sl +} + +// SymGoType returns the 'Gotype' property for a given symbol (set by +// the Go compiler for variable symbols). This version relies on +// reading aux symbols for the target sym -- it could be that a faster +// approach would be to check for gotype during preload and copy the +// results in to a map (might want to try this at some point and see +// if it helps speed things up). +func (l *Loader) SymGoType(i Sym) Sym { + var r *oReader + var auxs []goobj.Aux + if l.IsExternal(i) { + pp := l.getPayload(i) + r = l.objs[pp.objidx].r + auxs = pp.auxs + } else { + var li uint32 + r, li = l.toLocal(i) + auxs = r.Auxs(li) + } + for j := range auxs { + a := &auxs[j] + switch a.Type() { + case goobj.AuxGotype: + return l.resolve(r, a.Sym()) + } + } + return 0 +} + +// SymUnit returns the compilation unit for a given symbol (which will +// typically be nil for external or linker-manufactured symbols). +func (l *Loader) SymUnit(i Sym) *sym.CompilationUnit { + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp.objidx != 0 { + r := l.objs[pp.objidx].r + return r.unit + } + return nil + } + r, _ := l.toLocal(i) + return r.unit +} + +// SymPkg returns the package where the symbol came from (for +// regular compiler-generated Go symbols), but in the case of +// building with "-linkshared" (when a symbol is read from a +// shared library), will hold the library name. +// NOTE: this correspondes to sym.Symbol.File field. +func (l *Loader) SymPkg(i Sym) string { + if f, ok := l.symPkg[i]; ok { + return f + } + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp.objidx != 0 { + r := l.objs[pp.objidx].r + return r.unit.Lib.Pkg + } + return "" + } + r, _ := l.toLocal(i) + return r.unit.Lib.Pkg +} + +// SetSymPkg sets the package/library for a symbol. This is +// needed mainly for external symbols, specifically those imported +// from shared libraries. +func (l *Loader) SetSymPkg(i Sym, pkg string) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetSymPkg") + } + l.symPkg[i] = pkg +} + +// SymLocalentry returns the "local entry" value for the specified +// symbol. +func (l *Loader) SymLocalentry(i Sym) uint8 { + return l.localentry[i] +} + +// SetSymLocalentry sets the "local entry" attribute for a symbol. +func (l *Loader) SetSymLocalentry(i Sym, value uint8) { + // reject bad symbols + if i >= Sym(len(l.objSyms)) || i == 0 { + panic("bad symbol index in SetSymLocalentry") + } + if value == 0 { + delete(l.localentry, i) + } else { + l.localentry[i] = value + } +} + +// Returns the number of aux symbols given a global index. +func (l *Loader) NAux(i Sym) int { + if l.IsExternal(i) { + return 0 + } + r, li := l.toLocal(i) + return r.NAux(li) +} + +// Returns the "handle" to the j-th aux symbol of the i-th symbol. +func (l *Loader) Aux(i Sym, j int) Aux { + if l.IsExternal(i) { + return Aux{} + } + r, li := l.toLocal(i) + if j >= r.NAux(li) { + return Aux{} + } + return Aux{r.Aux(li, j), r, l} +} + +// GetFuncDwarfAuxSyms collects and returns the auxiliary DWARF +// symbols associated with a given function symbol. Prior to the +// introduction of the loader, this was done purely using name +// lookups, e.f. for function with name XYZ we would then look up +// go.info.XYZ, etc. +func (l *Loader) GetFuncDwarfAuxSyms(fnSymIdx Sym) (auxDwarfInfo, auxDwarfLoc, auxDwarfRanges, auxDwarfLines Sym) { + if l.SymType(fnSymIdx) != sym.STEXT { + log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncDwarfAuxSyms", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String()) + } + if l.IsExternal(fnSymIdx) { + // Current expectation is that any external function will + // not have auxsyms. + return + } + r, li := l.toLocal(fnSymIdx) + auxs := r.Auxs(li) + for i := range auxs { + a := &auxs[i] + switch a.Type() { + case goobj.AuxDwarfInfo: + auxDwarfInfo = l.resolve(r, a.Sym()) + if l.SymType(auxDwarfInfo) != sym.SDWARFFCN { + panic("aux dwarf info sym with wrong type") + } + case goobj.AuxDwarfLoc: + auxDwarfLoc = l.resolve(r, a.Sym()) + if l.SymType(auxDwarfLoc) != sym.SDWARFLOC { + panic("aux dwarf loc sym with wrong type") + } + case goobj.AuxDwarfRanges: + auxDwarfRanges = l.resolve(r, a.Sym()) + if l.SymType(auxDwarfRanges) != sym.SDWARFRANGE { + panic("aux dwarf ranges sym with wrong type") + } + case goobj.AuxDwarfLines: + auxDwarfLines = l.resolve(r, a.Sym()) + if l.SymType(auxDwarfLines) != sym.SDWARFLINES { + panic("aux dwarf lines sym with wrong type") + } + } + } + return +} + +// AddInteriorSym sets up 'interior' as an interior symbol of +// container/payload symbol 'container'. An interior symbol does not +// itself have data, but gives a name to a subrange of the data in its +// container symbol. The container itself may or may not have a name. +// This method is intended primarily for use in the host object +// loaders, to capture the semantics of symbols and sections in an +// object file. When reading a host object file, we'll typically +// encounter a static section symbol (ex: ".text") containing content +// for a collection of functions, then a series of ELF (or macho, etc) +// symbol table entries each of which points into a sub-section +// (offset and length) of its corresponding container symbol. Within +// the go linker we create a loader.Sym for the container (which is +// expected to have the actual content/payload) and then a set of +// interior loader.Sym's that point into a portion of the container. +func (l *Loader) AddInteriorSym(container Sym, interior Sym) { + // Container symbols are expected to have content/data. + // NB: this restriction may turn out to be too strict (it's possible + // to imagine a zero-sized container with an interior symbol pointing + // into it); it's ok to relax or remove it if we counter an + // oddball host object that triggers this. + if l.SymSize(container) == 0 && len(l.Data(container)) == 0 { + panic("unexpected empty container symbol") + } + // The interior symbols for a container are not expected to have + // content/data or relocations. + if len(l.Data(interior)) != 0 { + panic("unexpected non-empty interior symbol") + } + // Interior symbol is expected to be in the symbol table. + if l.AttrNotInSymbolTable(interior) { + panic("interior symbol must be in symtab") + } + // Only a single level of containment is allowed. + if l.OuterSym(container) != 0 { + panic("outer has outer itself") + } + // Interior sym should not already have a sibling. + if l.SubSym(interior) != 0 { + panic("sub set for subsym") + } + // Interior sym should not already point at a container. + if l.OuterSym(interior) != 0 { + panic("outer already set for subsym") + } + l.sub[interior] = l.sub[container] + l.sub[container] = interior + l.outer[interior] = container +} + +// OuterSym gets the outer symbol for host object loaded symbols. +func (l *Loader) OuterSym(i Sym) Sym { + // FIXME: add check for isExternal? + return l.outer[i] +} + +// SubSym gets the subsymbol for host object loaded symbols. +func (l *Loader) SubSym(i Sym) Sym { + // NB: note -- no check for l.isExternal(), since I am pretty sure + // that later phases in the linker set subsym for "type." syms + return l.sub[i] +} + +// SetCarrierSym declares that 'c' is the carrier or container symbol +// for 's'. Carrier symbols are used in the linker to as a container +// for a collection of sub-symbols where the content of the +// sub-symbols is effectively concatenated to form the content of the +// carrier. The carrier is given a name in the output symbol table +// while the sub-symbol names are not. For example, the Go compiler +// emits named string symbols (type SGOSTRING) when compiling a +// package; after being deduplicated, these symbols are collected into +// a single unit by assigning them a new carrier symbol named +// "go.string.*" (which appears in the final symbol table for the +// output load module). +func (l *Loader) SetCarrierSym(s Sym, c Sym) { + if c == 0 { + panic("invalid carrier in SetCarrierSym") + } + if s == 0 { + panic("invalid sub-symbol in SetCarrierSym") + } + // Carrier symbols are not expected to have content/data. It is + // ok for them to have non-zero size (to allow for use of generator + // symbols). + if len(l.Data(c)) != 0 { + panic("unexpected non-empty carrier symbol") + } + l.outer[s] = c + // relocsym's foldSubSymbolOffset requires that we only + // have a single level of containment-- enforce here. + if l.outer[c] != 0 { + panic("invalid nested carrier sym") + } +} + +// Initialize Reachable bitmap and its siblings for running deadcode pass. +func (l *Loader) InitReachable() { + l.growAttrBitmaps(l.NSym() + 1) +} + +type symWithVal struct { + s Sym + v int64 +} +type bySymValue []symWithVal + +func (s bySymValue) Len() int { return len(s) } +func (s bySymValue) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s bySymValue) Less(i, j int) bool { return s[i].v < s[j].v } + +// SortSub walks through the sub-symbols for 's' and sorts them +// in place by increasing value. Return value is the new +// sub symbol for the specified outer symbol. +func (l *Loader) SortSub(s Sym) Sym { + + if s == 0 || l.sub[s] == 0 { + return s + } + + // Sort symbols using a slice first. Use a stable sort on the off + // chance that there's more than once symbol with the same value, + // so as to preserve reproducible builds. + sl := []symWithVal{} + for ss := l.sub[s]; ss != 0; ss = l.sub[ss] { + sl = append(sl, symWithVal{s: ss, v: l.SymValue(ss)}) + } + sort.Stable(bySymValue(sl)) + + // Then apply any changes needed to the sub map. + ns := Sym(0) + for i := len(sl) - 1; i >= 0; i-- { + s := sl[i].s + l.sub[s] = ns + ns = s + } + + // Update sub for outer symbol, then return + l.sub[s] = sl[0].s + return sl[0].s +} + +// SortSyms sorts a list of symbols by their value. +func (l *Loader) SortSyms(ss []Sym) { + sort.SliceStable(ss, func(i, j int) bool { return l.SymValue(ss[i]) < l.SymValue(ss[j]) }) +} + +// Insure that reachable bitmap and its siblings have enough size. +func (l *Loader) growAttrBitmaps(reqLen int) { + if reqLen > l.attrReachable.Len() { + // These are indexed by global symbol + l.attrReachable = growBitmap(reqLen, l.attrReachable) + l.attrOnList = growBitmap(reqLen, l.attrOnList) + l.attrLocal = growBitmap(reqLen, l.attrLocal) + l.attrNotInSymbolTable = growBitmap(reqLen, l.attrNotInSymbolTable) + l.attrUsedInIface = growBitmap(reqLen, l.attrUsedInIface) + } + l.growExtAttrBitmaps() +} + +func (l *Loader) growExtAttrBitmaps() { + // These are indexed by external symbol index (e.g. l.extIndex(i)) + extReqLen := len(l.payloads) + if extReqLen > l.attrVisibilityHidden.Len() { + l.attrVisibilityHidden = growBitmap(extReqLen, l.attrVisibilityHidden) + l.attrDuplicateOK = growBitmap(extReqLen, l.attrDuplicateOK) + l.attrShared = growBitmap(extReqLen, l.attrShared) + l.attrExternal = growBitmap(extReqLen, l.attrExternal) + } +} + +func (relocs *Relocs) Count() int { return len(relocs.rs) } + +// At returns the j-th reloc for a global symbol. +func (relocs *Relocs) At(j int) Reloc { + if relocs.l.isExtReader(relocs.r) { + pp := relocs.l.payloads[relocs.li] + return Reloc{&relocs.rs[j], relocs.r, relocs.l, pp.reltypes[j]} + } + return Reloc{&relocs.rs[j], relocs.r, relocs.l, 0} +} + +// Relocs returns a Relocs object for the given global sym. +func (l *Loader) Relocs(i Sym) Relocs { + r, li := l.toLocal(i) + if r == nil { + panic(fmt.Sprintf("trying to get oreader for invalid sym %d\n\n", i)) + } + return l.relocs(r, li) +} + +// Relocs returns a Relocs object given a local sym index and reader. +func (l *Loader) relocs(r *oReader, li uint32) Relocs { + var rs []goobj.Reloc + if l.isExtReader(r) { + pp := l.payloads[li] + rs = pp.relocs + } else { + rs = r.Relocs(li) + } + return Relocs{ + rs: rs, + li: li, + r: r, + l: l, + } +} + +// FuncInfo provides hooks to access goobj.FuncInfo in the objects. +type FuncInfo struct { + l *Loader + r *oReader + data []byte + auxs []goobj.Aux + lengths goobj.FuncInfoLengths +} + +func (fi *FuncInfo) Valid() bool { return fi.r != nil } + +func (fi *FuncInfo) Args() int { + return int((*goobj.FuncInfo)(nil).ReadArgs(fi.data)) +} + +func (fi *FuncInfo) Locals() int { + return int((*goobj.FuncInfo)(nil).ReadLocals(fi.data)) +} + +func (fi *FuncInfo) FuncID() objabi.FuncID { + return objabi.FuncID((*goobj.FuncInfo)(nil).ReadFuncID(fi.data)) +} + +func (fi *FuncInfo) Pcsp() Sym { + sym := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data) + return fi.l.resolve(fi.r, sym) +} + +func (fi *FuncInfo) Pcfile() Sym { + sym := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data) + return fi.l.resolve(fi.r, sym) +} + +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 +// below related to pcdata, funcdataoff, files, and inltree nodes. +func (fi *FuncInfo) Preload() { + fi.lengths = (*goobj.FuncInfo)(nil).ReadFuncInfoLengths(fi.data) +} + +func (fi *FuncInfo) Pcdata() []Sym { + 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]) + } + return ret +} + +func (fi *FuncInfo) NumFuncdataoff() uint32 { + if !fi.lengths.Initialized { + panic("need to call Preload first") + } + return fi.lengths.NumFuncdataoff +} + +func (fi *FuncInfo) Funcdataoff(k int) int64 { + if !fi.lengths.Initialized { + panic("need to call Preload first") + } + return (*goobj.FuncInfo)(nil).ReadFuncdataoff(fi.data, fi.lengths.FuncdataoffOff, uint32(k)) +} + +func (fi *FuncInfo) Funcdata(syms []Sym) []Sym { + if !fi.lengths.Initialized { + panic("need to call Preload first") + } + if int(fi.lengths.NumFuncdataoff) > cap(syms) { + syms = make([]Sym, 0, fi.lengths.NumFuncdataoff) + } else { + syms = syms[:0] + } + for j := range fi.auxs { + a := &fi.auxs[j] + if a.Type() == goobj.AuxFuncdata { + syms = append(syms, fi.l.resolve(fi.r, a.Sym())) + } + } + return syms +} + +func (fi *FuncInfo) NumFile() uint32 { + if !fi.lengths.Initialized { + panic("need to call Preload first") + } + return fi.lengths.NumFile +} + +func (fi *FuncInfo) File(k int) goobj.CUFileIndex { + if !fi.lengths.Initialized { + panic("need to call Preload first") + } + return (*goobj.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k)) +} + +type InlTreeNode struct { + Parent int32 + File goobj.CUFileIndex + Line int32 + Func Sym + ParentPC int32 +} + +func (fi *FuncInfo) NumInlTree() uint32 { + if !fi.lengths.Initialized { + panic("need to call Preload first") + } + return fi.lengths.NumInlTree +} + +func (fi *FuncInfo) InlTree(k int) InlTreeNode { + if !fi.lengths.Initialized { + panic("need to call Preload first") + } + node := (*goobj.FuncInfo)(nil).ReadInlTree(fi.data, fi.lengths.InlTreeOff, uint32(k)) + return InlTreeNode{ + Parent: node.Parent, + File: node.File, + Line: node.Line, + Func: fi.l.resolve(fi.r, node.Func), + ParentPC: node.ParentPC, + } +} + +func (l *Loader) FuncInfo(i Sym) FuncInfo { + var r *oReader + var auxs []goobj.Aux + if l.IsExternal(i) { + pp := l.getPayload(i) + if pp.objidx == 0 { + return FuncInfo{} + } + r = l.objs[pp.objidx].r + auxs = pp.auxs + } else { + var li uint32 + r, li = l.toLocal(i) + auxs = r.Auxs(li) + } + for j := range auxs { + a := &auxs[j] + if a.Type() == goobj.AuxFuncInfo { + b := r.Data(a.Sym().SymIdx) + return FuncInfo{l, r, b, auxs, goobj.FuncInfoLengths{}} + } + } + return FuncInfo{} +} + +// Preload a package: adds autolib. +// Does not add defined package or non-packaged symbols to the symbol table. +// These are done in LoadSyms. +// Does not read symbol data. +// Returns the fingerprint of the object. +func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64) goobj.FingerprintType { + roObject, readonly, err := f.Slice(uint64(length)) // TODO: no need to map blocks that are for tools only (e.g. RefName) + if err != nil { + log.Fatal("cannot read object file:", err) + } + r := goobj.NewReaderFromBytes(roObject, readonly) + if r == nil { + if len(roObject) >= 8 && bytes.Equal(roObject[:8], []byte("\x00go114ld")) { + log.Fatalf("found object file %s in old format", f.File().Name()) + } + panic("cannot read object file") + } + pkgprefix := objabi.PathToPrefix(lib.Pkg) + "." + ndef := r.NSym() + nhashed64def := r.NHashed64def() + nhasheddef := r.NHasheddef() + or := &oReader{ + Reader: r, + unit: unit, + version: localSymVersion, + flags: r.Flags(), + pkgprefix: pkgprefix, + syms: make([]Sym, ndef+nhashed64def+nhasheddef+r.NNonpkgdef()+r.NNonpkgref()), + ndef: ndef, + nhasheddef: nhasheddef, + nhashed64def: nhashed64def, + objidx: uint32(len(l.objs)), + } + + // Autolib + lib.Autolib = append(lib.Autolib, r.Autolib()...) + + // DWARF file table + nfile := r.NFile() + unit.FileTable = make([]string, nfile) + for i := range unit.FileTable { + unit.FileTable[i] = r.File(i) + } + + l.addObj(lib.Pkg, or) + + // The caller expects us consuming all the data + f.MustSeek(length, os.SEEK_CUR) + + return r.Fingerprint() +} + +// Holds the loader along with temporary states for loading symbols. +type loadState struct { + l *Loader + hashed64Syms map[uint64]symAndSize // short hashed (content-addressable) symbols, keyed by content hash + hashedSyms map[goobj.HashType]symAndSize // hashed (content-addressable) symbols, keyed by content hash +} + +// Preload symbols of given kind from an object. +func (st *loadState) preloadSyms(r *oReader, kind int) { + l := st.l + var start, end uint32 + switch kind { + case pkgDef: + start = 0 + end = uint32(r.ndef) + case hashed64Def: + start = uint32(r.ndef) + end = uint32(r.ndef + r.nhashed64def) + case hashedDef: + start = uint32(r.ndef + r.nhashed64def) + end = uint32(r.ndef + r.nhashed64def + r.nhasheddef) + if l.hasUnknownPkgPath { + // The content hash depends on symbol name expansion. If any package is + // built without fully expanded names, the content hash is unreliable. + // Treat them as named symbols. + // This is rare. + // (We don't need to do this for hashed64Def case, as there the hash + // function is simply the identity function, which doesn't depend on + // name expansion.) + kind = nonPkgDef + } + case nonPkgDef: + start = uint32(r.ndef + r.nhashed64def + r.nhasheddef) + end = uint32(r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef()) + default: + panic("preloadSyms: bad kind") + } + l.growAttrBitmaps(len(l.objSyms) + int(end-start)) + needNameExpansion := r.NeedNameExpansion() + loadingRuntimePkg := r.unit.Lib.Pkg == "runtime" + for i := start; i < end; i++ { + osym := r.Sym(i) + var name string + var v int + if kind != hashed64Def && kind != hashedDef { // we don't need the name, etc. for hashed symbols + name = osym.Name(r.Reader) + if needNameExpansion { + name = strings.Replace(name, "\"\".", r.pkgprefix, -1) + } + v = abiToVer(osym.ABI(), r.version) + } + gi := st.addSym(name, v, r, i, kind, osym) + r.syms[i] = gi + if osym.TopFrame() { + l.SetAttrTopFrame(gi, true) + } + if osym.Local() { + l.SetAttrLocal(gi, true) + } + if osym.UsedInIface() { + l.SetAttrUsedInIface(gi, true) + } + if strings.HasPrefix(name, "runtime.") || + (loadingRuntimePkg && strings.HasPrefix(name, "type.")) { + if bi := goobj.BuiltinIdx(name, v); bi != -1 { + // This is a definition of a builtin symbol. Record where it is. + l.builtinSyms[bi] = gi + } + } + if a := int32(osym.Align()); a != 0 && a > l.SymAlign(gi) { + l.SetSymAlign(gi, a) + } + } +} + +// Add syms, hashed (content-addressable) symbols, non-package symbols, and +// references to external symbols (which are always named). +func (l *Loader) LoadSyms(arch *sys.Arch) { + // Allocate space for symbols, making a guess as to how much space we need. + // This function was determined empirically by looking at the cmd/compile on + // Darwin, and picking factors for hashed and hashed64 syms. + var symSize, hashedSize, hashed64Size int + for _, o := range l.objs[goObjStart:] { + symSize += o.r.ndef + o.r.nhasheddef/2 + o.r.nhashed64def/2 + o.r.NNonpkgdef() + hashedSize += o.r.nhasheddef / 2 + hashed64Size += o.r.nhashed64def / 2 + } + // Index 0 is invalid for symbols. + l.objSyms = make([]objSym, 1, symSize) + + l.npkgsyms = l.NSym() + st := loadState{ + l: l, + hashed64Syms: make(map[uint64]symAndSize, hashed64Size), + hashedSyms: make(map[goobj.HashType]symAndSize, hashedSize), + } + + for _, o := range l.objs[goObjStart:] { + st.preloadSyms(o.r, pkgDef) + } + for _, o := range l.objs[goObjStart:] { + st.preloadSyms(o.r, hashed64Def) + st.preloadSyms(o.r, hashedDef) + st.preloadSyms(o.r, nonPkgDef) + } + l.nhashedsyms = len(st.hashed64Syms) + len(st.hashedSyms) + for _, o := range l.objs[goObjStart:] { + loadObjRefs(l, o.r, arch) + } + l.values = make([]int64, l.NSym(), l.NSym()+1000) // +1000 make some room for external symbols +} + +func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) { + // load non-package refs + ndef := uint32(r.NAlldef()) + needNameExpansion := r.NeedNameExpansion() + for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ { + osym := r.Sym(ndef + i) + name := osym.Name(r.Reader) + if needNameExpansion { + name = strings.Replace(name, "\"\".", r.pkgprefix, -1) + } + v := abiToVer(osym.ABI(), r.version) + r.syms[ndef+i] = l.LookupOrCreateSym(name, v) + gi := r.syms[ndef+i] + if osym.Local() { + l.SetAttrLocal(gi, true) + } + if osym.UsedInIface() { + l.SetAttrUsedInIface(gi, true) + } + } + + // referenced packages + npkg := r.NPkg() + r.pkg = make([]uint32, npkg) + for i := 1; i < npkg; i++ { // PkgIdx 0 is a dummy invalid package + pkg := r.Pkg(i) + objidx, ok := l.objByPkg[pkg] + if !ok { + log.Fatalf("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) + gi := l.resolve(r, rf.Sym()) + if rf.Flag2()&goobj.SymFlagUsedInIface != 0 { + l.SetAttrUsedInIface(gi, true) + } + } +} + +func abiToVer(abi uint16, localSymVersion int) int { + var v int + if abi == goobj.SymABIstatic { + // Static + v = localSymVersion + } else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 { + // Note that data symbols are "ABI0", which maps to version 0. + v = abiver + } else { + log.Fatalf("invalid symbol ABI: %d", abi) + } + return v +} + +// ResolveABIAlias given a symbol returns the ABI alias target of that +// symbol. If the sym in question is not an alias, the sym itself is +// returned. +func (l *Loader) ResolveABIAlias(s Sym) Sym { + if s == 0 { + return 0 + } + if l.SymType(s) != sym.SABIALIAS { + return s + } + relocs := l.Relocs(s) + target := relocs.At(0).Sym() + if l.SymType(target) == sym.SABIALIAS { + panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", l.SymName(s), l.SymName(target))) + } + return target +} + +// TopLevelSym tests a symbol (by name and kind) to determine whether +// the symbol first class sym (participating in the link) or is an +// anonymous aux or sub-symbol containing some sub-part or payload of +// another symbol. +func (l *Loader) TopLevelSym(s Sym) bool { + return topLevelSym(l.RawSymName(s), l.SymType(s)) +} + +// topLevelSym tests a symbol name and kind to determine whether +// the symbol first class sym (participating in the link) or is an +// anonymous aux or sub-symbol containing some sub-part or payload of +// another symbol. +func topLevelSym(sname string, skind sym.SymKind) bool { + if sname != "" { + return true + } + switch skind { + case sym.SDWARFFCN, sym.SDWARFABSFCN, sym.SDWARFTYPE, sym.SDWARFCONST, sym.SDWARFCUINFO, sym.SDWARFRANGE, sym.SDWARFLOC, sym.SDWARFLINES, sym.SGOFUNC: + return true + default: + return false + } +} + +// cloneToExternal takes the existing object file symbol (symIdx) +// and creates a new external symbol payload that is a clone with +// respect to name, version, type, relocations, etc. The idea here +// is that if the linker decides it wants to update the contents of +// a symbol originally discovered as part of an object file, it's +// easier to do this if we make the updates to an external symbol +// payload. +func (l *Loader) cloneToExternal(symIdx Sym) { + if l.IsExternal(symIdx) { + panic("sym is already external, no need for clone") + } + + // Read the particulars from object. + r, li := l.toLocal(symIdx) + osym := r.Sym(li) + sname := osym.Name(r.Reader) + if r.NeedNameExpansion() { + sname = strings.Replace(sname, "\"\".", r.pkgprefix, -1) + } + sver := abiToVer(osym.ABI(), r.version) + skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())] + + // Create new symbol, update version and kind. + pi := l.newPayload(sname, sver) + pp := l.payloads[pi] + pp.kind = skind + pp.ver = sver + pp.size = int64(osym.Siz()) + pp.objidx = r.objidx + + // If this is a def, then copy the guts. We expect this case + // to be very rare (one case it may come up is with -X). + if li < uint32(r.NAlldef()) { + + // Copy relocations + relocs := l.Relocs(symIdx) + pp.relocs = make([]goobj.Reloc, relocs.Count()) + pp.reltypes = make([]objabi.RelocType, relocs.Count()) + for i := range pp.relocs { + // Copy the relocs slice. + // Convert local reference to global reference. + rel := relocs.At(i) + pp.relocs[i].Set(rel.Off(), rel.Siz(), 0, rel.Add(), goobj.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())}) + pp.reltypes[i] = rel.Type() + } + + // Copy data + pp.data = r.Data(li) + } + + // If we're overriding a data symbol, collect the associated + // Gotype, so as to propagate it to the new symbol. + auxs := r.Auxs(li) + pp.auxs = auxs + + // Install new payload to global index space. + // (This needs to happen at the end, as the accessors above + // need to access the old symbol content.) + l.objSyms[symIdx] = objSym{l.extReader.objidx, uint32(pi)} + l.extReader.syms = append(l.extReader.syms, symIdx) +} + +// Copy the payload of symbol src to dst. Both src and dst must be external +// symbols. +// The intended use case is that when building/linking against a shared library, +// where we do symbol name mangling, the Go object file may have reference to +// the original symbol name whereas the shared library provides a symbol with +// the mangled name. When we do mangling, we copy payload of mangled to original. +func (l *Loader) CopySym(src, dst Sym) { + if !l.IsExternal(dst) { + panic("dst is not external") //l.newExtSym(l.SymName(dst), l.SymVersion(dst)) + } + if !l.IsExternal(src) { + panic("src is not external") //l.cloneToExternal(src) + } + l.payloads[l.extIndex(dst)] = l.payloads[l.extIndex(src)] + l.SetSymPkg(dst, l.SymPkg(src)) + // TODO: other attributes? +} + +// CopyAttributes copies over all of the attributes of symbol 'src' to +// symbol 'dst'. +func (l *Loader) CopyAttributes(src Sym, dst Sym) { + l.SetAttrReachable(dst, l.AttrReachable(src)) + l.SetAttrOnList(dst, l.AttrOnList(src)) + l.SetAttrLocal(dst, l.AttrLocal(src)) + l.SetAttrNotInSymbolTable(dst, l.AttrNotInSymbolTable(src)) + if l.IsExternal(dst) { + l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src)) + l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src)) + l.SetAttrShared(dst, l.AttrShared(src)) + l.SetAttrExternal(dst, l.AttrExternal(src)) + } else { + // Some attributes are modifiable only for external symbols. + // In such cases, don't try to transfer over the attribute + // from the source even if there is a clash. This comes up + // when copying attributes from a dupOK ABI wrapper symbol to + // the real target symbol (which may not be marked dupOK). + } + l.SetAttrTopFrame(dst, l.AttrTopFrame(src)) + l.SetAttrSpecial(dst, l.AttrSpecial(src)) + l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src)) + l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src)) + l.SetAttrReadOnly(dst, l.AttrReadOnly(src)) +} + +// CreateExtSym creates a new external symbol with the specified name +// without adding it to any lookup tables, returning a Sym index for it. +func (l *Loader) CreateExtSym(name string, ver int) Sym { + return l.newExtSym(name, ver) +} + +// CreateStaticSym creates a new static symbol with the specified name +// without adding it to any lookup tables, returning a Sym index for it. +func (l *Loader) CreateStaticSym(name string) Sym { + // Assign a new unique negative version -- this is to mark the + // symbol so that it is not included in the name lookup table. + l.anonVersion-- + return l.newExtSym(name, l.anonVersion) +} + +func (l *Loader) FreeSym(i Sym) { + if l.IsExternal(i) { + pp := l.getPayload(i) + *pp = extSymPayload{} + } +} + +// relocId is essentially a <S,R> tuple identifying the Rth +// relocation of symbol S. +type relocId struct { + sym Sym + ridx int +} + +// SetRelocVariant sets the 'variant' property of a relocation on +// some specific symbol. +func (l *Loader) SetRelocVariant(s Sym, ri int, v sym.RelocVariant) { + // sanity check + if relocs := l.Relocs(s); ri >= relocs.Count() { + panic("invalid relocation ID") + } + if l.relocVariant == nil { + l.relocVariant = make(map[relocId]sym.RelocVariant) + } + if v != 0 { + l.relocVariant[relocId{s, ri}] = v + } else { + delete(l.relocVariant, relocId{s, ri}) + } +} + +// RelocVariant returns the 'variant' property of a relocation on +// some specific symbol. +func (l *Loader) RelocVariant(s Sym, ri int) sym.RelocVariant { + return l.relocVariant[relocId{s, ri}] +} + +// UndefinedRelocTargets iterates through the global symbol index +// space, looking for symbols with relocations targeting undefined +// references. The linker's loadlib method uses this to determine if +// there are unresolved references to functions in system libraries +// (for example, libgcc.a), presumably due to CGO code. Return +// value is a list of loader.Sym's corresponding to the undefined +// cross-refs. The "limit" param controls the maximum number of +// results returned; if "limit" is -1, then all undefs are returned. +func (l *Loader) UndefinedRelocTargets(limit int) []Sym { + result := []Sym{} + for si := Sym(1); si < Sym(len(l.objSyms)); si++ { + relocs := l.Relocs(si) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + rs := r.Sym() + if rs != 0 && l.SymType(rs) == sym.SXREF && l.RawSymName(rs) != ".got" { + result = append(result, rs) + if limit != -1 && len(result) >= limit { + break + } + } + } + } + return result +} + +// AssignTextSymbolOrder populates the Textp slices within each +// library and compilation unit, insuring that packages are laid down +// in dependency order (internal first, then everything else). Return value +// is a slice of all text syms. +func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, extsyms []Sym) []Sym { + + // Library Textp lists should be empty at this point. + for _, lib := range libs { + if len(lib.Textp) != 0 { + panic("expected empty Textp slice for library") + } + if len(lib.DupTextSyms) != 0 { + panic("expected empty DupTextSyms slice for library") + } + } + + // Used to record which dupok symbol we've assigned to a unit. + // Can't use the onlist attribute here because it will need to + // clear for the later assignment of the sym.Symbol to a unit. + // NB: we can convert to using onList once we no longer have to + // call the regular addToTextp. + assignedToUnit := MakeBitmap(l.NSym() + 1) + + // Start off textp with reachable external syms. + textp := []Sym{} + for _, sym := range extsyms { + if !l.attrReachable.Has(sym) { + continue + } + textp = append(textp, sym) + } + + // Walk through all text symbols from Go object files and append + // them to their corresponding library's textp list. + for _, o := range l.objs[goObjStart:] { + r := o.r + lib := r.unit.Lib + for i, n := uint32(0), uint32(r.NAlldef()); i < n; i++ { + gi := l.toGlobal(r, i) + if !l.attrReachable.Has(gi) { + continue + } + osym := r.Sym(i) + st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())] + if st != sym.STEXT { + continue + } + dupok := osym.Dupok() + if r2, i2 := l.toLocal(gi); r2 != r || i2 != i { + // A dupok text symbol is resolved to another package. + // We still need to record its presence in the current + // package, as the trampoline pass expects packages + // are laid out in dependency order. + lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi)) + continue // symbol in different object + } + if dupok { + lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi)) + continue + } + + lib.Textp = append(lib.Textp, sym.LoaderSym(gi)) + } + } + + // Now assemble global textp, and assign text symbols to units. + for _, doInternal := range [2]bool{true, false} { + for idx, lib := range libs { + if intlibs[idx] != doInternal { + continue + } + lists := [2][]sym.LoaderSym{lib.Textp, lib.DupTextSyms} + for i, list := range lists { + for _, s := range list { + sym := Sym(s) + if l.attrReachable.Has(sym) && !assignedToUnit.Has(sym) { + textp = append(textp, sym) + unit := l.SymUnit(sym) + if unit != nil { + unit.Textp = append(unit.Textp, s) + assignedToUnit.Set(sym) + } + // Dupok symbols may be defined in multiple packages; the + // associated package for a dupok sym is chosen sort of + // arbitrarily (the first containing package that the linker + // loads). Canonicalizes its Pkg to the package with which + // it will be laid down in text. + if i == 1 /* DupTextSyms2 */ && l.SymPkg(sym) != lib.Pkg { + l.SetSymPkg(sym, lib.Pkg) + } + } + } + } + lib.Textp = nil + lib.DupTextSyms = nil + } + } + + return textp +} + +// ErrorReporter is a helper class for reporting errors. +type ErrorReporter struct { + ldr *Loader + AfterErrorAction func() +} + +// Errorf method logs an error message. +// +// After each error, the error actions function will be invoked; this +// will either terminate the link immediately (if -h option given) +// or it will keep a count and exit if more than 20 errors have been printed. +// +// Logging an error means that on exit cmd/link will delete any +// output file and return a non-zero error code. +// +func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) { + if s != 0 && reporter.ldr.SymName(s) != "" { + format = reporter.ldr.SymName(s) + ": " + format + } else { + format = fmt.Sprintf("sym %d: %s", s, format) + } + format += "\n" + fmt.Fprintf(os.Stderr, format, args...) + reporter.AfterErrorAction() +} + +// GetErrorReporter returns the loader's associated error reporter. +func (l *Loader) GetErrorReporter() *ErrorReporter { + return l.errorReporter +} + +// Errorf method logs an error message. See ErrorReporter.Errorf for details. +func (l *Loader) Errorf(s Sym, format string, args ...interface{}) { + l.errorReporter.Errorf(s, format, args...) +} + +// Symbol statistics. +func (l *Loader) Stat() string { + s := fmt.Sprintf("%d symbols, %d reachable\n", l.NSym(), l.NReachableSym()) + s += fmt.Sprintf("\t%d package symbols, %d hashed symbols, %d non-package symbols, %d external symbols\n", + l.npkgsyms, l.nhashedsyms, int(l.extStart)-l.npkgsyms-l.nhashedsyms, l.NSym()-int(l.extStart)) + return s +} + +// For debugging. +func (l *Loader) Dump() { + fmt.Println("objs") + for _, obj := range l.objs[goObjStart:] { + if obj.r != nil { + fmt.Println(obj.i, obj.r.unit.Lib) + } + } + fmt.Println("extStart:", l.extStart) + fmt.Println("Nsyms:", len(l.objSyms)) + fmt.Println("syms") + for i := Sym(1); i < Sym(len(l.objSyms)); i++ { + pi := "" + if l.IsExternal(i) { + pi = fmt.Sprintf("<ext %d>", l.extIndex(i)) + } + sect := "" + if l.SymSect(i) != nil { + sect = l.SymSect(i).Name + } + fmt.Printf("%v %v %v %v %x %v\n", i, l.SymName(i), l.SymType(i), pi, l.SymValue(i), sect) + } + fmt.Println("symsByName") + for name, i := range l.symsByName[0] { + fmt.Println(i, name, 0) + } + for name, i := range l.symsByName[1] { + fmt.Println(i, name, 1) + } + fmt.Println("payloads:") + for i := range l.payloads { + pp := l.payloads[i] + fmt.Println(i, pp.name, pp.ver, pp.kind) + } +} diff --git a/src/cmd/link/internal/loader/loader_test.go b/src/cmd/link/internal/loader/loader_test.go new file mode 100644 index 0000000..1371c2a --- /dev/null +++ b/src/cmd/link/internal/loader/loader_test.go @@ -0,0 +1,454 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loader + +import ( + "bytes" + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/sym" + "fmt" + "testing" +) + +// dummyAddSym adds the named symbol to the loader as if it had been +// read from a Go object file. Note that it allocates a global +// index without creating an associated object reader, so one can't +// do anything interesting with this symbol (such as look at its +// data or relocations). +func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym { + idx := uint32(len(ldr.objSyms)) + st := loadState{l: ldr} + return st.addSym(name, 0, or, idx, nonPkgDef, &goobj.Sym{}) +} + +func mkLoader() *Loader { + edummy := func(str string, off int) {} + er := ErrorReporter{} + ldr := NewLoader(0, edummy, &er) + er.ldr = ldr + return ldr +} + +func TestAddMaterializedSymbol(t *testing.T) { + ldr := mkLoader() + dummyOreader := oReader{version: -1, syms: make([]Sym, 100)} + or := &dummyOreader + + // Create some syms from a dummy object file symbol to get things going. + ts1 := addDummyObjSym(t, ldr, or, "type.uint8") + ts2 := addDummyObjSym(t, ldr, or, "mumble") + ts3 := addDummyObjSym(t, ldr, or, "type.string") + + // Create some external symbols. + es1 := ldr.LookupOrCreateSym("extnew1", 0) + if es1 == 0 { + t.Fatalf("LookupOrCreateSym failed for extnew1") + } + es1x := ldr.LookupOrCreateSym("extnew1", 0) + if es1x != es1 { + t.Fatalf("LookupOrCreateSym lookup: expected %d got %d for second lookup", es1, es1x) + } + es2 := ldr.LookupOrCreateSym("go.info.type.uint8", 0) + if es2 == 0 { + t.Fatalf("LookupOrCreateSym failed for go.info.type.uint8") + } + // Create a nameless symbol + es3 := ldr.CreateStaticSym("") + if es3 == 0 { + t.Fatalf("CreateStaticSym failed for nameless sym") + } + + // Grab symbol builder pointers + sb1 := ldr.MakeSymbolUpdater(es1) + sb2 := ldr.MakeSymbolUpdater(es2) + sb3 := ldr.MakeSymbolUpdater(es3) + + // Suppose we create some more symbols, which triggers a grow. + // Make sure the symbol builder's payload pointer is valid, + // even across a grow. + for i := 0; i < 9999; i++ { + ldr.CreateStaticSym("dummy") + } + + // Check get/set symbol type + es3typ := sb3.Type() + if es3typ != sym.Sxxx { + t.Errorf("SymType(es3): expected %v, got %v", sym.Sxxx, es3typ) + } + sb3.SetType(sym.SRODATA) + es3typ = sb3.Type() + if es3typ != sym.SRODATA { + t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ) + } + es3typ = ldr.SymType(es3) + if es3typ != sym.SRODATA { + t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ) + } + + // New symbols should not initially be reachable. + if ldr.AttrReachable(es1) || ldr.AttrReachable(es2) || ldr.AttrReachable(es3) { + t.Errorf("newly materialized symbols should not be reachable") + } + + // ... however it should be possible to set/unset their reachability. + ldr.SetAttrReachable(es3, true) + if !ldr.AttrReachable(es3) { + t.Errorf("expected reachable symbol after update") + } + ldr.SetAttrReachable(es3, false) + if ldr.AttrReachable(es3) { + t.Errorf("expected unreachable symbol after update") + } + + // Test expansion of attr bitmaps + for idx := 0; idx < 36; idx++ { + es := ldr.LookupOrCreateSym(fmt.Sprintf("zext%d", idx), 0) + if ldr.AttrOnList(es) { + t.Errorf("expected OnList after creation") + } + ldr.SetAttrOnList(es, true) + if !ldr.AttrOnList(es) { + t.Errorf("expected !OnList after update") + } + if ldr.AttrDuplicateOK(es) { + t.Errorf("expected DupOK after creation") + } + ldr.SetAttrDuplicateOK(es, true) + if !ldr.AttrDuplicateOK(es) { + t.Errorf("expected !DupOK after update") + } + } + + sb1 = ldr.MakeSymbolUpdater(es1) + sb2 = ldr.MakeSymbolUpdater(es2) + + // Get/set a few other attributes + if ldr.AttrVisibilityHidden(es3) { + t.Errorf("expected initially not hidden") + } + ldr.SetAttrVisibilityHidden(es3, true) + if !ldr.AttrVisibilityHidden(es3) { + t.Errorf("expected hidden after update") + } + + // Test get/set symbol value. + toTest := []Sym{ts2, es3} + for i, s := range toTest { + if v := ldr.SymValue(s); v != 0 { + t.Errorf("ldr.Value(%d): expected 0 got %d\n", s, v) + } + nv := int64(i + 101) + ldr.SetSymValue(s, nv) + if v := ldr.SymValue(s); v != nv { + t.Errorf("ldr.SetValue(%d,%d): expected %d got %d\n", s, nv, nv, v) + } + } + + // Check/set alignment + es3al := ldr.SymAlign(es3) + if es3al != 0 { + t.Errorf("SymAlign(es3): expected 0, got %d", es3al) + } + ldr.SetSymAlign(es3, 128) + es3al = ldr.SymAlign(es3) + if es3al != 128 { + t.Errorf("SymAlign(es3): expected 128, got %d", es3al) + } + + // Add some relocations to the new symbols. + r1, _ := sb1.AddRel(objabi.R_ADDR) + r1.SetOff(0) + r1.SetSiz(1) + r1.SetSym(ts1) + r2, _ := sb1.AddRel(objabi.R_CALL) + r2.SetOff(3) + r2.SetSiz(8) + r2.SetSym(ts2) + r3, _ := sb2.AddRel(objabi.R_USETYPE) + r3.SetOff(7) + r3.SetSiz(1) + r3.SetSym(ts3) + + // Add some data to the symbols. + d1 := []byte{1, 2, 3} + d2 := []byte{4, 5, 6, 7} + sb1.AddBytes(d1) + sb2.AddBytes(d2) + + // Now invoke the usual loader interfaces to make sure + // we're getting the right things back for these symbols. + // First relocations... + expRel := [][]Reloc{{r1, r2}, {r3}} + for k, sb := range []*SymbolBuilder{sb1, sb2} { + rsl := sb.Relocs() + exp := expRel[k] + if !sameRelocSlice(&rsl, exp) { + t.Errorf("expected relocs %v, got %v", exp, rsl) + } + } + + // ... then data. + dat := sb2.Data() + if bytes.Compare(dat, d2) != 0 { + t.Errorf("expected es2 data %v, got %v", d2, dat) + } + + // Nameless symbol should still be nameless. + es3name := ldr.RawSymName(es3) + if "" != es3name { + t.Errorf("expected es3 name of '', got '%s'", es3name) + } + + // Read value of materialized symbol. + es1val := sb1.Value() + if 0 != es1val { + t.Errorf("expected es1 value of 0, got %v", es1val) + } + + // Test other misc methods + irm := ldr.IsReflectMethod(es1) + if 0 != es1val { + t.Errorf("expected IsReflectMethod(es1) value of 0, got %v", irm) + } +} + +func sameRelocSlice(s1 *Relocs, s2 []Reloc) bool { + if s1.Count() != len(s2) { + return false + } + for i := 0; i < s1.Count(); i++ { + r1 := s1.At(i) + r2 := &s2[i] + if r1.Sym() != r2.Sym() || + r1.Type() != r2.Type() || + r1.Off() != r2.Off() || + r1.Add() != r2.Add() || + r1.Siz() != r2.Siz() { + return false + } + } + return true +} + +type addFunc func(l *Loader, s Sym, s2 Sym) Sym + +func mkReloc(l *Loader, typ objabi.RelocType, off int32, siz uint8, add int64, sym Sym) Reloc { + r := Reloc{&goobj.Reloc{}, l.extReader, l, typ} + r.SetOff(off) + r.SetSiz(siz) + r.SetAdd(add) + r.SetSym(sym) + return r +} + +func TestAddDataMethods(t *testing.T) { + ldr := mkLoader() + dummyOreader := oReader{version: -1, syms: make([]Sym, 100)} + or := &dummyOreader + + // Populate loader with some symbols. + addDummyObjSym(t, ldr, or, "type.uint8") + ldr.LookupOrCreateSym("hello", 0) + + arch := sys.ArchAMD64 + var testpoints = []struct { + which string + addDataFunc addFunc + expData []byte + expKind sym.SymKind + expRel []Reloc + }{ + { + which: "AddUint8", + addDataFunc: func(l *Loader, s Sym, _ Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddUint8('a') + return s + }, + expData: []byte{'a'}, + expKind: sym.SDATA, + }, + { + which: "AddUintXX", + addDataFunc: func(l *Loader, s Sym, _ Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddUintXX(arch, 25185, 2) + return s + }, + expData: []byte{'a', 'b'}, + expKind: sym.SDATA, + }, + { + which: "SetUint8", + addDataFunc: func(l *Loader, s Sym, _ Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddUint8('a') + sb.AddUint8('b') + sb.SetUint8(arch, 1, 'c') + return s + }, + expData: []byte{'a', 'c'}, + expKind: sym.SDATA, + }, + { + which: "AddString", + addDataFunc: func(l *Loader, s Sym, _ Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.Addstring("hello") + return s + }, + expData: []byte{'h', 'e', 'l', 'l', 'o', 0}, + expKind: sym.SNOPTRDATA, + }, + { + which: "AddAddrPlus", + addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddAddrPlus(arch, s2, 3) + return s + }, + expData: []byte{0, 0, 0, 0, 0, 0, 0, 0}, + expKind: sym.SDATA, + expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 8, 3, 6)}, + }, + { + which: "AddAddrPlus4", + addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddAddrPlus4(arch, s2, 3) + return s + }, + expData: []byte{0, 0, 0, 0}, + expKind: sym.SDATA, + expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 4, 3, 7)}, + }, + { + which: "AddCURelativeAddrPlus", + addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym { + sb := l.MakeSymbolUpdater(s) + sb.AddCURelativeAddrPlus(arch, s2, 7) + return s + }, + expData: []byte{0, 0, 0, 0, 0, 0, 0, 0}, + expKind: sym.SDATA, + expRel: []Reloc{mkReloc(ldr, objabi.R_ADDRCUOFF, 0, 8, 7, 8)}, + }, + } + + var pmi Sym + for k, tp := range testpoints { + name := fmt.Sprintf("new%d", k+1) + mi := ldr.LookupOrCreateSym(name, 0) + if mi == 0 { + t.Fatalf("LookupOrCreateSym failed for '" + name + "'") + } + mi = tp.addDataFunc(ldr, mi, pmi) + if ldr.SymType(mi) != tp.expKind { + t.Errorf("testing Loader.%s: expected kind %s got %s", + tp.which, tp.expKind, ldr.SymType(mi)) + } + if bytes.Compare(ldr.Data(mi), tp.expData) != 0 { + t.Errorf("testing Loader.%s: expected data %v got %v", + tp.which, tp.expData, ldr.Data(mi)) + } + relocs := ldr.Relocs(mi) + if !sameRelocSlice(&relocs, tp.expRel) { + t.Fatalf("testing Loader.%s: got relocslice %+v wanted %+v", + tp.which, relocs, tp.expRel) + } + pmi = mi + } +} + +func TestOuterSub(t *testing.T) { + ldr := mkLoader() + dummyOreader := oReader{version: -1, syms: make([]Sym, 100)} + or := &dummyOreader + + // Populate loader with some symbols. + addDummyObjSym(t, ldr, or, "type.uint8") + es1 := ldr.LookupOrCreateSym("outer", 0) + ldr.MakeSymbolUpdater(es1).SetSize(101) + es2 := ldr.LookupOrCreateSym("sub1", 0) + es3 := ldr.LookupOrCreateSym("sub2", 0) + es4 := ldr.LookupOrCreateSym("sub3", 0) + es5 := ldr.LookupOrCreateSym("sub4", 0) + es6 := ldr.LookupOrCreateSym("sub5", 0) + + // Should not have an outer sym initially + if ldr.OuterSym(es1) != 0 { + t.Errorf("es1 outer sym set ") + } + if ldr.SubSym(es2) != 0 { + t.Errorf("es2 outer sym set ") + } + + // Establish first outer/sub relationship + ldr.AddInteriorSym(es1, es2) + if ldr.OuterSym(es1) != 0 { + t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0) + } + if ldr.OuterSym(es2) != es1 { + t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1) + } + if ldr.SubSym(es1) != es2 { + t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es2) + } + if ldr.SubSym(es2) != 0 { + t.Errorf("ldr.SubSym(es2) got %d wanted %d", ldr.SubSym(es2), 0) + } + + // Establish second outer/sub relationship + ldr.AddInteriorSym(es1, es3) + if ldr.OuterSym(es1) != 0 { + t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0) + } + if ldr.OuterSym(es2) != es1 { + t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1) + } + if ldr.OuterSym(es3) != es1 { + t.Errorf("ldr.OuterSym(es3) got %d wanted %d", ldr.OuterSym(es3), es1) + } + if ldr.SubSym(es1) != es3 { + t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es3) + } + if ldr.SubSym(es3) != es2 { + t.Errorf("ldr.SubSym(es3) got %d wanted %d", ldr.SubSym(es3), es2) + } + + // Some more + ldr.AddInteriorSym(es1, es4) + ldr.AddInteriorSym(es1, es5) + ldr.AddInteriorSym(es1, es6) + + // Set values. + ldr.SetSymValue(es2, 7) + ldr.SetSymValue(es3, 1) + ldr.SetSymValue(es4, 13) + ldr.SetSymValue(es5, 101) + ldr.SetSymValue(es6, 3) + + // Sort + news := ldr.SortSub(es1) + if news != es3 { + t.Errorf("ldr.SortSub leader got %d wanted %d", news, es3) + } + pv := int64(-1) + count := 0 + for ss := ldr.SubSym(es1); ss != 0; ss = ldr.SubSym(ss) { + v := ldr.SymValue(ss) + if v <= pv { + t.Errorf("ldr.SortSub sortfail at %d: val %d >= prev val %d", + ss, v, pv) + } + pv = v + count++ + } + if count != 5 { + t.Errorf("expected %d in sub list got %d", 5, count) + } +} diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go new file mode 100644 index 0000000..5d37da8 --- /dev/null +++ b/src/cmd/link/internal/loader/symbolbuilder.go @@ -0,0 +1,440 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loader + +import ( + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/sym" + "sort" +) + +// SymbolBuilder is a helper designed to help with the construction +// of new symbol contents. +type SymbolBuilder struct { + *extSymPayload // points to payload being updated + symIdx Sym // index of symbol being updated/constructed + l *Loader // loader +} + +// MakeSymbolBuilder creates a symbol builder for use in constructing +// an entirely new symbol. +func (l *Loader) MakeSymbolBuilder(name string) *SymbolBuilder { + // for now assume that any new sym is intended to be static + symIdx := l.CreateStaticSym(name) + sb := &SymbolBuilder{l: l, symIdx: symIdx} + sb.extSymPayload = l.getPayload(symIdx) + return sb +} + +// MakeSymbolUpdater creates a symbol builder helper for an existing +// symbol 'symIdx'. If 'symIdx' is not an external symbol, then create +// a clone of it (copy name, properties, etc) fix things up so that +// the lookup tables and caches point to the new version, not the old +// version. +func (l *Loader) MakeSymbolUpdater(symIdx Sym) *SymbolBuilder { + if symIdx == 0 { + panic("can't update the null symbol") + } + if !l.IsExternal(symIdx) { + // Create a clone with the same name/version/kind etc. + l.cloneToExternal(symIdx) + } + + // Construct updater and return. + sb := &SymbolBuilder{l: l, symIdx: symIdx} + sb.extSymPayload = l.getPayload(symIdx) + return sb +} + +// CreateSymForUpdate creates a symbol with given name and version, +// returns a CreateSymForUpdate for update. If the symbol already +// exists, it will update in-place. +func (l *Loader) CreateSymForUpdate(name string, version int) *SymbolBuilder { + s := l.LookupOrCreateSym(name, version) + l.SetAttrReachable(s, true) + return l.MakeSymbolUpdater(s) +} + +// Getters for properties of the symbol we're working on. + +func (sb *SymbolBuilder) Sym() Sym { return sb.symIdx } +func (sb *SymbolBuilder) Name() string { return sb.name } +func (sb *SymbolBuilder) Version() int { return sb.ver } +func (sb *SymbolBuilder) Type() sym.SymKind { return sb.kind } +func (sb *SymbolBuilder) Size() int64 { return sb.size } +func (sb *SymbolBuilder) Data() []byte { return sb.data } +func (sb *SymbolBuilder) Value() int64 { return sb.l.SymValue(sb.symIdx) } +func (sb *SymbolBuilder) Align() int32 { return sb.l.SymAlign(sb.symIdx) } +func (sb *SymbolBuilder) Localentry() uint8 { return sb.l.SymLocalentry(sb.symIdx) } +func (sb *SymbolBuilder) OnList() bool { return sb.l.AttrOnList(sb.symIdx) } +func (sb *SymbolBuilder) External() bool { return sb.l.AttrExternal(sb.symIdx) } +func (sb *SymbolBuilder) Extname() string { return sb.l.SymExtname(sb.symIdx) } +func (sb *SymbolBuilder) CgoExportDynamic() bool { return sb.l.AttrCgoExportDynamic(sb.symIdx) } +func (sb *SymbolBuilder) Dynimplib() string { return sb.l.SymDynimplib(sb.symIdx) } +func (sb *SymbolBuilder) Dynimpvers() string { return sb.l.SymDynimpvers(sb.symIdx) } +func (sb *SymbolBuilder) SubSym() Sym { return sb.l.SubSym(sb.symIdx) } +func (sb *SymbolBuilder) GoType() Sym { return sb.l.SymGoType(sb.symIdx) } +func (sb *SymbolBuilder) VisibilityHidden() bool { return sb.l.AttrVisibilityHidden(sb.symIdx) } +func (sb *SymbolBuilder) Sect() *sym.Section { return sb.l.SymSect(sb.symIdx) } + +// Setters for symbol properties. + +func (sb *SymbolBuilder) SetType(kind sym.SymKind) { sb.kind = kind } +func (sb *SymbolBuilder) SetSize(size int64) { sb.size = size } +func (sb *SymbolBuilder) SetData(data []byte) { sb.data = data } +func (sb *SymbolBuilder) SetOnList(v bool) { sb.l.SetAttrOnList(sb.symIdx, v) } +func (sb *SymbolBuilder) SetExternal(v bool) { sb.l.SetAttrExternal(sb.symIdx, v) } +func (sb *SymbolBuilder) SetValue(v int64) { sb.l.SetSymValue(sb.symIdx, v) } +func (sb *SymbolBuilder) SetAlign(align int32) { sb.l.SetSymAlign(sb.symIdx, align) } +func (sb *SymbolBuilder) SetLocalentry(value uint8) { sb.l.SetSymLocalentry(sb.symIdx, value) } +func (sb *SymbolBuilder) SetExtname(value string) { sb.l.SetSymExtname(sb.symIdx, value) } +func (sb *SymbolBuilder) SetDynimplib(value string) { sb.l.SetSymDynimplib(sb.symIdx, value) } +func (sb *SymbolBuilder) SetDynimpvers(value string) { sb.l.SetSymDynimpvers(sb.symIdx, value) } +func (sb *SymbolBuilder) SetPlt(value int32) { sb.l.SetPlt(sb.symIdx, value) } +func (sb *SymbolBuilder) SetGot(value int32) { sb.l.SetGot(sb.symIdx, value) } +func (sb *SymbolBuilder) SetSpecial(value bool) { sb.l.SetAttrSpecial(sb.symIdx, value) } +func (sb *SymbolBuilder) SetLocal(value bool) { sb.l.SetAttrLocal(sb.symIdx, value) } +func (sb *SymbolBuilder) SetVisibilityHidden(value bool) { + sb.l.SetAttrVisibilityHidden(sb.symIdx, value) +} +func (sb *SymbolBuilder) SetNotInSymbolTable(value bool) { + sb.l.SetAttrNotInSymbolTable(sb.symIdx, value) +} +func (sb *SymbolBuilder) SetSect(sect *sym.Section) { sb.l.SetSymSect(sb.symIdx, sect) } + +func (sb *SymbolBuilder) AddBytes(data []byte) { + if sb.kind == 0 { + sb.kind = sym.SDATA + } + sb.data = append(sb.data, data...) + sb.size = int64(len(sb.data)) +} + +func (sb *SymbolBuilder) Relocs() Relocs { + return sb.l.Relocs(sb.symIdx) +} + +// ResetRelocs removes all relocations on this symbol. +func (sb *SymbolBuilder) ResetRelocs() { + sb.relocs = sb.relocs[:0] + sb.reltypes = sb.reltypes[:0] +} + +// SetRelocType sets the type of the 'i'-th relocation on this sym to 't' +func (sb *SymbolBuilder) SetRelocType(i int, t objabi.RelocType) { + sb.relocs[i].SetType(0) + sb.reltypes[i] = t +} + +// SetRelocSym sets the target sym of the 'i'-th relocation on this sym to 's' +func (sb *SymbolBuilder) SetRelocSym(i int, tgt Sym) { + sb.relocs[i].SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(tgt)}) +} + +// SetRelocAdd sets the addend of the 'i'-th relocation on this sym to 'a' +func (sb *SymbolBuilder) SetRelocAdd(i int, a int64) { + sb.relocs[i].SetAdd(a) +} + +// Add n relocations, return a handle to the relocations. +func (sb *SymbolBuilder) AddRelocs(n int) Relocs { + sb.relocs = append(sb.relocs, make([]goobj.Reloc, n)...) + sb.reltypes = append(sb.reltypes, make([]objabi.RelocType, n)...) + return sb.l.Relocs(sb.symIdx) +} + +// Add a relocation with given type, return its handle and index +// (to set other fields). +func (sb *SymbolBuilder) AddRel(typ objabi.RelocType) (Reloc, int) { + j := len(sb.relocs) + sb.relocs = append(sb.relocs, goobj.Reloc{}) + sb.reltypes = append(sb.reltypes, typ) + relocs := sb.Relocs() + return relocs.At(j), j +} + +// Sort relocations by offset. +func (sb *SymbolBuilder) SortRelocs() { + sort.Sort((*relocsByOff)(sb.extSymPayload)) +} + +// Implement sort.Interface +type relocsByOff extSymPayload + +func (p *relocsByOff) Len() int { return len(p.relocs) } +func (p *relocsByOff) Less(i, j int) bool { return p.relocs[i].Off() < p.relocs[j].Off() } +func (p *relocsByOff) Swap(i, j int) { + p.relocs[i], p.relocs[j] = p.relocs[j], p.relocs[i] + p.reltypes[i], p.reltypes[j] = p.reltypes[j], p.reltypes[i] +} + +func (sb *SymbolBuilder) Reachable() bool { + return sb.l.AttrReachable(sb.symIdx) +} + +func (sb *SymbolBuilder) SetReachable(v bool) { + sb.l.SetAttrReachable(sb.symIdx, v) +} + +func (sb *SymbolBuilder) setReachable() { + sb.SetReachable(true) +} + +func (sb *SymbolBuilder) ReadOnly() bool { + return sb.l.AttrReadOnly(sb.symIdx) +} + +func (sb *SymbolBuilder) SetReadOnly(v bool) { + sb.l.SetAttrReadOnly(sb.symIdx, v) +} + +func (sb *SymbolBuilder) DuplicateOK() bool { + return sb.l.AttrDuplicateOK(sb.symIdx) +} + +func (sb *SymbolBuilder) SetDuplicateOK(v bool) { + sb.l.SetAttrDuplicateOK(sb.symIdx, v) +} + +func (sb *SymbolBuilder) Outer() Sym { + return sb.l.OuterSym(sb.symIdx) +} + +func (sb *SymbolBuilder) Sub() Sym { + return sb.l.SubSym(sb.symIdx) +} + +func (sb *SymbolBuilder) SortSub() { + sb.l.SortSub(sb.symIdx) +} + +func (sb *SymbolBuilder) AddInteriorSym(sub Sym) { + sb.l.AddInteriorSym(sb.symIdx, sub) +} + +func (sb *SymbolBuilder) AddUint8(v uint8) int64 { + off := sb.size + if sb.kind == 0 { + sb.kind = sym.SDATA + } + sb.size++ + sb.data = append(sb.data, v) + return off +} + +func (sb *SymbolBuilder) AddUintXX(arch *sys.Arch, v uint64, wid int) int64 { + off := sb.size + sb.setUintXX(arch, off, v, int64(wid)) + return off +} + +func (sb *SymbolBuilder) setUintXX(arch *sys.Arch, off int64, v uint64, wid int64) int64 { + if sb.kind == 0 { + sb.kind = sym.SDATA + } + if sb.size < off+wid { + sb.size = off + wid + sb.Grow(sb.size) + } + + switch wid { + case 1: + sb.data[off] = uint8(v) + case 2: + arch.ByteOrder.PutUint16(sb.data[off:], uint16(v)) + case 4: + arch.ByteOrder.PutUint32(sb.data[off:], uint32(v)) + case 8: + arch.ByteOrder.PutUint64(sb.data[off:], v) + } + + return off + wid +} + +func (sb *SymbolBuilder) AddUint16(arch *sys.Arch, v uint16) int64 { + return sb.AddUintXX(arch, uint64(v), 2) +} + +func (sb *SymbolBuilder) AddUint32(arch *sys.Arch, v uint32) int64 { + return sb.AddUintXX(arch, uint64(v), 4) +} + +func (sb *SymbolBuilder) AddUint64(arch *sys.Arch, v uint64) int64 { + return sb.AddUintXX(arch, v, 8) +} + +func (sb *SymbolBuilder) AddUint(arch *sys.Arch, v uint64) int64 { + return sb.AddUintXX(arch, v, arch.PtrSize) +} + +func (sb *SymbolBuilder) SetUint8(arch *sys.Arch, r int64, v uint8) int64 { + return sb.setUintXX(arch, r, uint64(v), 1) +} + +func (sb *SymbolBuilder) SetUint16(arch *sys.Arch, r int64, v uint16) int64 { + return sb.setUintXX(arch, r, uint64(v), 2) +} + +func (sb *SymbolBuilder) SetUint32(arch *sys.Arch, r int64, v uint32) int64 { + return sb.setUintXX(arch, r, uint64(v), 4) +} + +func (sb *SymbolBuilder) SetUint(arch *sys.Arch, r int64, v uint64) int64 { + return sb.setUintXX(arch, r, v, int64(arch.PtrSize)) +} + +func (sb *SymbolBuilder) SetUintptr(arch *sys.Arch, r int64, v uintptr) int64 { + return sb.setUintXX(arch, r, uint64(v), int64(arch.PtrSize)) +} + +func (sb *SymbolBuilder) SetAddrPlus(arch *sys.Arch, off int64, tgt Sym, add int64) int64 { + if sb.Type() == 0 { + sb.SetType(sym.SDATA) + } + if off+int64(arch.PtrSize) > sb.size { + sb.size = off + int64(arch.PtrSize) + sb.Grow(sb.size) + } + r, _ := sb.AddRel(objabi.R_ADDR) + r.SetSym(tgt) + r.SetOff(int32(off)) + r.SetSiz(uint8(arch.PtrSize)) + r.SetAdd(add) + return off + int64(r.Siz()) +} + +func (sb *SymbolBuilder) SetAddr(arch *sys.Arch, off int64, tgt Sym) int64 { + return sb.SetAddrPlus(arch, off, tgt, 0) +} + +func (sb *SymbolBuilder) AddStringAt(off int64, str string) int64 { + strLen := int64(len(str)) + if off+strLen+1 > int64(len(sb.data)) { + panic("attempt to write past end of buffer") + } + copy(sb.data[off:off+strLen], str) + sb.data[off+strLen] = 0 + return off + strLen + 1 +} + +func (sb *SymbolBuilder) Addstring(str string) int64 { + if sb.kind == 0 { + sb.kind = sym.SNOPTRDATA + } + r := sb.size + if sb.name == ".shstrtab" { + // FIXME: find a better mechanism for this + sb.l.elfsetstring(str, int(r)) + } + sb.data = append(sb.data, str...) + sb.data = append(sb.data, 0) + sb.size = int64(len(sb.data)) + return r +} + +func (sb *SymbolBuilder) SetBytesAt(off int64, b []byte) int64 { + datLen := int64(len(b)) + if off+datLen > int64(len(sb.data)) { + panic("attempt to write past end of buffer") + } + copy(sb.data[off:off+datLen], b) + return off + datLen +} + +func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 { + if sb.kind == 0 { + sb.kind = sym.SDATA + } + i := sb.size + + sb.size += int64(rsize) + sb.Grow(sb.size) + + r, _ := sb.AddRel(typ) + r.SetSym(tgt) + r.SetOff(int32(i)) + r.SetSiz(uint8(rsize)) + r.SetAdd(add) + + return i + int64(rsize) +} + +// Add a symbol reference (relocation) with given type, addend, and size +// (the most generic form). +func (sb *SymbolBuilder) AddSymRef(arch *sys.Arch, tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 { + return sb.addSymRef(tgt, add, typ, rsize) +} + +func (sb *SymbolBuilder) AddAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 { + return sb.addSymRef(tgt, add, objabi.R_ADDR, arch.PtrSize) +} + +func (sb *SymbolBuilder) AddAddrPlus4(arch *sys.Arch, tgt Sym, add int64) int64 { + return sb.addSymRef(tgt, add, objabi.R_ADDR, 4) +} + +func (sb *SymbolBuilder) AddAddr(arch *sys.Arch, tgt Sym) int64 { + return sb.AddAddrPlus(arch, tgt, 0) +} + +func (sb *SymbolBuilder) AddPCRelPlus(arch *sys.Arch, tgt Sym, add int64) int64 { + return sb.addSymRef(tgt, add, objabi.R_PCREL, 4) +} + +func (sb *SymbolBuilder) AddCURelativeAddrPlus(arch *sys.Arch, tgt Sym, add int64) int64 { + return sb.addSymRef(tgt, add, objabi.R_ADDRCUOFF, arch.PtrSize) +} + +func (sb *SymbolBuilder) AddSize(arch *sys.Arch, tgt Sym) int64 { + return sb.addSymRef(tgt, 0, objabi.R_SIZE, arch.PtrSize) +} + +// GenAddAddrPlusFunc returns a function to be called when capturing +// a function symbol's address. In later stages of the link (when +// address assignment is done) when doing internal linking and +// targeting an executable, we can just emit the address of a function +// directly instead of generating a relocation. Clients can call +// this function (setting 'internalExec' based on build mode and target) +// and then invoke the returned function in roughly the same way that +// loader.*SymbolBuilder.AddAddrPlus would be used. +func GenAddAddrPlusFunc(internalExec bool) func(s *SymbolBuilder, arch *sys.Arch, tgt Sym, add int64) int64 { + if internalExec { + return func(s *SymbolBuilder, arch *sys.Arch, tgt Sym, add int64) int64 { + if v := s.l.SymValue(tgt); v != 0 { + return s.AddUint(arch, uint64(v+add)) + } + return s.AddAddrPlus(arch, tgt, add) + } + } else { + return (*SymbolBuilder).AddAddrPlus + } +} + +func (sb *SymbolBuilder) MakeWritable() { + if sb.ReadOnly() { + sb.data = append([]byte(nil), sb.data...) + sb.l.SetAttrReadOnly(sb.symIdx, false) + } +} + +func (sb *SymbolBuilder) AddUleb(v uint64) { + if v < 128 { // common case: 1 byte + sb.AddUint8(uint8(v)) + return + } + for { + c := uint8(v & 0x7f) + v >>= 7 + if v != 0 { + c |= 0x80 + } + sb.AddUint8(c) + if c&0x80 == 0 { + break + } + } +} diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go new file mode 100644 index 0000000..6d1d9bb --- /dev/null +++ b/src/cmd/link/internal/loadmacho/ldmacho.go @@ -0,0 +1,806 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package loadmacho implements a Mach-O file reader. +package loadmacho + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "encoding/binary" + "fmt" +) + +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// TODO(crawshaw): de-duplicate these symbols with cmd/link/internal/ld +const ( + MACHO_X86_64_RELOC_UNSIGNED = 0 + MACHO_X86_64_RELOC_SIGNED = 1 + MACHO_ARM64_RELOC_ADDEND = 10 +) + +type ldMachoObj struct { + f *bio.Reader + base int64 // off in f where Mach-O begins + length int64 // length of Mach-O + is64 bool + name string + e binary.ByteOrder + cputype uint + subcputype uint + filetype uint32 + flags uint32 + cmd []ldMachoCmd + ncmd uint +} + +type ldMachoCmd struct { + type_ int + off uint32 + size uint32 + seg ldMachoSeg + sym ldMachoSymtab + dsym ldMachoDysymtab +} + +type ldMachoSeg struct { + name string + vmaddr uint64 + vmsize uint64 + fileoff uint32 + filesz uint32 + maxprot uint32 + initprot uint32 + nsect uint32 + flags uint32 + sect []ldMachoSect +} + +type ldMachoSect struct { + name string + segname string + addr uint64 + size uint64 + off uint32 + align uint32 + reloff uint32 + nreloc uint32 + flags uint32 + res1 uint32 + res2 uint32 + sym loader.Sym + rel []ldMachoRel +} + +type ldMachoRel struct { + addr uint32 + symnum uint32 + pcrel uint8 + length uint8 + extrn uint8 + type_ uint8 + scattered uint8 + value uint32 +} + +type ldMachoSymtab struct { + symoff uint32 + nsym uint32 + stroff uint32 + strsize uint32 + str []byte + sym []ldMachoSym +} + +type ldMachoSym struct { + name string + type_ uint8 + sectnum uint8 + desc uint16 + kind int8 + value uint64 + sym loader.Sym +} + +type ldMachoDysymtab struct { + ilocalsym uint32 + nlocalsym uint32 + iextdefsym uint32 + nextdefsym uint32 + iundefsym uint32 + nundefsym uint32 + tocoff uint32 + ntoc uint32 + modtaboff uint32 + nmodtab uint32 + extrefsymoff uint32 + nextrefsyms uint32 + indirectsymoff uint32 + nindirectsyms uint32 + extreloff uint32 + nextrel uint32 + locreloff uint32 + nlocrel uint32 + indir []uint32 +} + +// ldMachoSym.type_ +const ( + N_EXT = 0x01 + N_TYPE = 0x1e + N_STAB = 0xe0 +) + +// ldMachoSym.desc +const ( + N_WEAK_REF = 0x40 + N_WEAK_DEF = 0x80 +) + +const ( + LdMachoCpuVax = 1 + LdMachoCpu68000 = 6 + LdMachoCpu386 = 7 + LdMachoCpuAmd64 = 1<<24 | 7 + LdMachoCpuMips = 8 + LdMachoCpu98000 = 10 + LdMachoCpuHppa = 11 + LdMachoCpuArm = 12 + LdMachoCpuArm64 = 1<<24 | 12 + LdMachoCpu88000 = 13 + LdMachoCpuSparc = 14 + LdMachoCpu860 = 15 + LdMachoCpuAlpha = 16 + LdMachoCpuPower = 18 + LdMachoCmdSegment = 1 + LdMachoCmdSymtab = 2 + LdMachoCmdSymseg = 3 + LdMachoCmdThread = 4 + LdMachoCmdDysymtab = 11 + LdMachoCmdSegment64 = 25 + LdMachoFileObject = 1 + LdMachoFileExecutable = 2 + LdMachoFileFvmlib = 3 + LdMachoFileCore = 4 + LdMachoFilePreload = 5 +) + +func unpackcmd(p []byte, m *ldMachoObj, c *ldMachoCmd, type_ uint, sz uint) int { + e4 := m.e.Uint32 + e8 := m.e.Uint64 + + c.type_ = int(type_) + c.size = uint32(sz) + switch type_ { + default: + return -1 + + case LdMachoCmdSegment: + if sz < 56 { + return -1 + } + c.seg.name = cstring(p[8:24]) + c.seg.vmaddr = uint64(e4(p[24:])) + c.seg.vmsize = uint64(e4(p[28:])) + c.seg.fileoff = e4(p[32:]) + c.seg.filesz = e4(p[36:]) + c.seg.maxprot = e4(p[40:]) + c.seg.initprot = e4(p[44:]) + c.seg.nsect = e4(p[48:]) + c.seg.flags = e4(p[52:]) + c.seg.sect = make([]ldMachoSect, c.seg.nsect) + if uint32(sz) < 56+c.seg.nsect*68 { + return -1 + } + p = p[56:] + var s *ldMachoSect + for i := 0; uint32(i) < c.seg.nsect; i++ { + s = &c.seg.sect[i] + s.name = cstring(p[0:16]) + s.segname = cstring(p[16:32]) + s.addr = uint64(e4(p[32:])) + s.size = uint64(e4(p[36:])) + s.off = e4(p[40:]) + s.align = e4(p[44:]) + s.reloff = e4(p[48:]) + s.nreloc = e4(p[52:]) + s.flags = e4(p[56:]) + s.res1 = e4(p[60:]) + s.res2 = e4(p[64:]) + p = p[68:] + } + + case LdMachoCmdSegment64: + if sz < 72 { + return -1 + } + c.seg.name = cstring(p[8:24]) + c.seg.vmaddr = e8(p[24:]) + c.seg.vmsize = e8(p[32:]) + c.seg.fileoff = uint32(e8(p[40:])) + c.seg.filesz = uint32(e8(p[48:])) + c.seg.maxprot = e4(p[56:]) + c.seg.initprot = e4(p[60:]) + c.seg.nsect = e4(p[64:]) + c.seg.flags = e4(p[68:]) + c.seg.sect = make([]ldMachoSect, c.seg.nsect) + if uint32(sz) < 72+c.seg.nsect*80 { + return -1 + } + p = p[72:] + var s *ldMachoSect + for i := 0; uint32(i) < c.seg.nsect; i++ { + s = &c.seg.sect[i] + s.name = cstring(p[0:16]) + s.segname = cstring(p[16:32]) + s.addr = e8(p[32:]) + s.size = e8(p[40:]) + s.off = e4(p[48:]) + s.align = e4(p[52:]) + s.reloff = e4(p[56:]) + s.nreloc = e4(p[60:]) + s.flags = e4(p[64:]) + s.res1 = e4(p[68:]) + s.res2 = e4(p[72:]) + + // p+76 is reserved + p = p[80:] + } + + case LdMachoCmdSymtab: + if sz < 24 { + return -1 + } + c.sym.symoff = e4(p[8:]) + c.sym.nsym = e4(p[12:]) + c.sym.stroff = e4(p[16:]) + c.sym.strsize = e4(p[20:]) + + case LdMachoCmdDysymtab: + if sz < 80 { + return -1 + } + c.dsym.ilocalsym = e4(p[8:]) + c.dsym.nlocalsym = e4(p[12:]) + c.dsym.iextdefsym = e4(p[16:]) + c.dsym.nextdefsym = e4(p[20:]) + c.dsym.iundefsym = e4(p[24:]) + c.dsym.nundefsym = e4(p[28:]) + c.dsym.tocoff = e4(p[32:]) + c.dsym.ntoc = e4(p[36:]) + c.dsym.modtaboff = e4(p[40:]) + c.dsym.nmodtab = e4(p[44:]) + c.dsym.extrefsymoff = e4(p[48:]) + c.dsym.nextrefsyms = e4(p[52:]) + c.dsym.indirectsymoff = e4(p[56:]) + c.dsym.nindirectsyms = e4(p[60:]) + c.dsym.extreloff = e4(p[64:]) + c.dsym.nextrel = e4(p[68:]) + c.dsym.locreloff = e4(p[72:]) + c.dsym.nlocrel = e4(p[76:]) + } + + return 0 +} + +func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int { + if sect.rel != nil || sect.nreloc == 0 { + return 0 + } + rel := make([]ldMachoRel, sect.nreloc) + m.f.MustSeek(m.base+int64(sect.reloff), 0) + buf, _, err := m.f.Slice(uint64(sect.nreloc * 8)) + if err != nil { + return -1 + } + for i := uint32(0); i < sect.nreloc; i++ { + r := &rel[i] + p := buf[i*8:] + r.addr = m.e.Uint32(p) + + // TODO(rsc): Wrong interpretation for big-endian bitfields? + if r.addr&0x80000000 != 0 { + // scatterbrained relocation + r.scattered = 1 + + v := r.addr >> 24 + r.addr &= 0xFFFFFF + r.type_ = uint8(v & 0xF) + v >>= 4 + r.length = 1 << (v & 3) + v >>= 2 + r.pcrel = uint8(v & 1) + r.value = m.e.Uint32(p[4:]) + } else { + v := m.e.Uint32(p[4:]) + r.symnum = v & 0xFFFFFF + v >>= 24 + r.pcrel = uint8(v & 1) + v >>= 1 + r.length = 1 << (v & 3) + v >>= 2 + r.extrn = uint8(v & 1) + v >>= 1 + r.type_ = uint8(v) + } + } + + sect.rel = rel + return 0 +} + +func macholoaddsym(m *ldMachoObj, d *ldMachoDysymtab) int { + n := int(d.nindirectsyms) + m.f.MustSeek(m.base+int64(d.indirectsymoff), 0) + p, _, err := m.f.Slice(uint64(n * 4)) + if err != nil { + return -1 + } + + d.indir = make([]uint32, n) + for i := 0; i < n; i++ { + d.indir[i] = m.e.Uint32(p[4*i:]) + } + return 0 +} + +func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int { + if symtab.sym != nil { + return 0 + } + + m.f.MustSeek(m.base+int64(symtab.stroff), 0) + strbuf, _, err := m.f.Slice(uint64(symtab.strsize)) + if err != nil { + return -1 + } + + symsize := 12 + if m.is64 { + symsize = 16 + } + n := int(symtab.nsym * uint32(symsize)) + m.f.MustSeek(m.base+int64(symtab.symoff), 0) + symbuf, _, err := m.f.Slice(uint64(n)) + if err != nil { + return -1 + } + sym := make([]ldMachoSym, symtab.nsym) + p := symbuf + for i := uint32(0); i < symtab.nsym; i++ { + s := &sym[i] + v := m.e.Uint32(p) + if v >= symtab.strsize { + return -1 + } + s.name = cstring(strbuf[v:]) + s.type_ = p[4] + s.sectnum = p[5] + s.desc = m.e.Uint16(p[6:]) + if m.is64 { + s.value = m.e.Uint64(p[8:]) + } else { + s.value = uint64(m.e.Uint32(p[8:])) + } + p = p[symsize:] + } + + symtab.str = strbuf + symtab.sym = sym + return 0 +} + +// Load the Mach-O file pn from f. +// Symbols are written into syms, and a slice of the text symbols is returned. +func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) { + errorf := func(str string, args ...interface{}) ([]loader.Sym, error) { + return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...)) + } + + base := f.Offset() + + hdr, _, err := f.Slice(7 * 4) + if err != nil { + return errorf("reading hdr: %v", err) + } + + var e binary.ByteOrder + if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { + e = binary.BigEndian + } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { + e = binary.LittleEndian + } else { + return errorf("bad magic - not mach-o file") + } + + is64 := e.Uint32(hdr[:]) == 0xFEEDFACF + ncmd := e.Uint32(hdr[4*4:]) + cmdsz := e.Uint32(hdr[5*4:]) + if ncmd > 0x10000 || cmdsz >= 0x01000000 { + return errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz) + } + + if is64 { + f.MustSeek(4, 1) // skip reserved word in header + } + + m := &ldMachoObj{ + f: f, + e: e, + cputype: uint(e.Uint32(hdr[1*4:])), + subcputype: uint(e.Uint32(hdr[2*4:])), + filetype: e.Uint32(hdr[3*4:]), + ncmd: uint(ncmd), + flags: e.Uint32(hdr[6*4:]), + is64: is64, + base: base, + length: length, + name: pn, + } + + switch arch.Family { + default: + return errorf("mach-o %s unimplemented", arch.Name) + case sys.AMD64: + if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { + return errorf("mach-o object but not amd64") + } + case sys.ARM64: + if e != binary.LittleEndian || m.cputype != LdMachoCpuArm64 { + return errorf("mach-o object but not arm64") + } + } + + m.cmd = make([]ldMachoCmd, ncmd) + cmdp, _, err := f.Slice(uint64(cmdsz)) + if err != nil { + return errorf("reading cmds: %v", err) + } + + // read and parse load commands + var c *ldMachoCmd + + var symtab *ldMachoSymtab + var dsymtab *ldMachoDysymtab + + off := uint32(len(hdr)) + for i := uint32(0); i < ncmd; i++ { + ty := e.Uint32(cmdp) + sz := e.Uint32(cmdp[4:]) + m.cmd[i].off = off + unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz)) + cmdp = cmdp[sz:] + off += sz + if ty == LdMachoCmdSymtab { + if symtab != nil { + return errorf("multiple symbol tables") + } + + symtab = &m.cmd[i].sym + macholoadsym(m, symtab) + } + + if ty == LdMachoCmdDysymtab { + dsymtab = &m.cmd[i].dsym + macholoaddsym(m, dsymtab) + } + + if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) { + if c != nil { + return errorf("multiple load commands") + } + + c = &m.cmd[i] + } + } + + // load text and data segments into memory. + // they are not as small as the load commands, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + if c == nil { + return errorf("no load command") + } + + if symtab == nil { + // our work is done here - no symbols means nothing can refer to this file + return + } + + if int64(c.seg.fileoff+c.seg.filesz) >= length { + return errorf("load segment out of range") + } + + f.MustSeek(m.base+int64(c.seg.fileoff), 0) + dat, readOnly, err := f.Slice(uint64(c.seg.filesz)) + if err != nil { + return errorf("cannot load object data: %v", err) + } + + for i := uint32(0); i < c.seg.nsect; i++ { + sect := &c.seg.sect[i] + if sect.segname != "__TEXT" && sect.segname != "__DATA" { + continue + } + if sect.name == "__eh_frame" { + continue + } + name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name) + s := l.LookupOrCreateSym(name, localSymVersion) + bld := l.MakeSymbolUpdater(s) + if bld.Type() != 0 { + return errorf("duplicate %s/%s", sect.segname, sect.name) + } + + if sect.flags&0xff == 1 { // S_ZEROFILL + bld.SetData(make([]byte, sect.size)) + } else { + bld.SetReadOnly(readOnly) + bld.SetData(dat[sect.addr-c.seg.vmaddr:][:sect.size]) + } + bld.SetSize(int64(len(bld.Data()))) + + if sect.segname == "__TEXT" { + if sect.name == "__text" { + bld.SetType(sym.STEXT) + } else { + bld.SetType(sym.SRODATA) + } + } else { + if sect.name == "__bss" { + bld.SetType(sym.SNOPTRBSS) + bld.SetData(nil) + } else { + bld.SetType(sym.SNOPTRDATA) + } + } + + sect.sym = s + } + + // enter sub-symbols into symbol table. + // have to guess sizes from next symbol. + for i := uint32(0); i < symtab.nsym; i++ { + machsym := &symtab.sym[i] + if machsym.type_&N_STAB != 0 { + continue + } + + // TODO: check sym->type against outer->type. + name := machsym.name + + if name[0] == '_' && name[1] != '\x00' { + name = name[1:] + } + v := 0 + if machsym.type_&N_EXT == 0 { + v = localSymVersion + } + s := l.LookupOrCreateSym(name, v) + if machsym.type_&N_EXT == 0 { + l.SetAttrDuplicateOK(s, true) + } + if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 { + l.SetAttrDuplicateOK(s, true) + } + machsym.sym = s + if machsym.sectnum == 0 { // undefined + continue + } + if uint32(machsym.sectnum) > c.seg.nsect { + return errorf("reference to invalid section %d", machsym.sectnum) + } + + sect := &c.seg.sect[machsym.sectnum-1] + bld := l.MakeSymbolUpdater(s) + outer := sect.sym + if outer == 0 { + continue // ignore reference to invalid section + } + + if osym := l.OuterSym(s); osym != 0 { + if l.AttrDuplicateOK(s) { + continue + } + return errorf("duplicate symbol reference: %s in both %s and %s", l.SymName(s), l.SymName(osym), l.SymName(sect.sym)) + } + + bld.SetType(l.SymType(outer)) + if l.SymSize(outer) != 0 { // skip empty section (0-sized symbol) + l.AddInteriorSym(outer, s) + } + + bld.SetValue(int64(machsym.value - sect.addr)) + if !l.AttrCgoExportDynamic(s) { + bld.SetDynimplib("") // satisfy dynimport + } + if l.SymType(outer) == sym.STEXT { + if bld.External() && !bld.DuplicateOK() { + return errorf("%v: duplicate symbol definition", s) + } + bld.SetExternal(true) + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i := 0; uint32(i) < c.seg.nsect; i++ { + sect := &c.seg.sect[i] + s := sect.sym + if s == 0 { + continue + } + bld := l.MakeSymbolUpdater(s) + if bld.SubSym() != 0 { + + bld.SortSub() + + // assign sizes, now that we know symbols in sorted order. + for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) { + s1Bld := l.MakeSymbolUpdater(s1) + if sub := l.SubSym(s1); sub != 0 { + s1Bld.SetSize(l.SymValue(sub) - l.SymValue(s1)) + } else { + dlen := int64(len(l.Data(s))) + s1Bld.SetSize(l.SymValue(s) + dlen - l.SymValue(s1)) + } + } + } + + if bld.Type() == sym.STEXT { + if bld.OnList() { + return errorf("symbol %s listed multiple times", bld.Name()) + } + bld.SetOnList(true) + textp = append(textp, s) + for s1 := bld.Sub(); s1 != 0; s1 = l.SubSym(s1) { + if l.AttrOnList(s1) { + return errorf("symbol %s listed multiple times", l.RawSymName(s1)) + } + l.SetAttrOnList(s1, true) + textp = append(textp, s1) + } + } + } + + // load relocations + for i := 0; uint32(i) < c.seg.nsect; i++ { + sect := &c.seg.sect[i] + s := sect.sym + if s == 0 { + continue + } + macholoadrel(m, sect) + if sect.rel == nil { + continue + } + + sb := l.MakeSymbolUpdater(sect.sym) + var rAdd int64 + for j := uint32(0); j < sect.nreloc; j++ { + var ( + rOff int32 + rSize uint8 + rType objabi.RelocType + rSym loader.Sym + ) + rel := §.rel[j] + if rel.scattered != 0 { + // mach-o only uses scattered relocation on 32-bit platforms, + // which are no longer supported. + return errorf("%v: unexpected scattered relocation", s) + } + + if arch.Family == sys.ARM64 && rel.type_ == MACHO_ARM64_RELOC_ADDEND { + // Two relocations. This addend will be applied to the next one. + rAdd = int64(rel.symnum) << 40 >> 40 // convert unsigned 24-bit to signed 24-bit + continue + } + + rSize = rel.length + rType = objabi.MachoRelocOffset + (objabi.RelocType(rel.type_) << 1) + objabi.RelocType(rel.pcrel) + rOff = int32(rel.addr) + + // Handle X86_64_RELOC_SIGNED referencing a section (rel.extrn == 0). + p := l.Data(s) + if arch.Family == sys.AMD64 { + if rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_SIGNED { + // Calculate the addend as the offset into the section. + // + // The rip-relative offset stored in the object file is encoded + // as follows: + // + // movsd 0x00000360(%rip),%xmm0 + // + // To get the absolute address of the value this rip-relative address is pointing + // to, we must add the address of the next instruction to it. This is done by + // taking the address of the relocation and adding 4 to it (since the rip-relative + // offset can at most be 32 bits long). To calculate the offset into the section the + // relocation is referencing, we subtract the vaddr of the start of the referenced + // section found in the original object file. + // + // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] + secaddr := c.seg.sect[rel.symnum-1].addr + rAdd = int64(uint64(int64(int32(e.Uint32(p[rOff:])))+int64(rOff)+4) - secaddr) + } else { + rAdd = int64(int32(e.Uint32(p[rOff:]))) + } + } + + // An unsigned internal relocation has a value offset + // by the section address. + if arch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == MACHO_X86_64_RELOC_UNSIGNED { + secaddr := c.seg.sect[rel.symnum-1].addr + rAdd -= int64(secaddr) + } + + if rel.extrn == 0 { + if rel.symnum < 1 || rel.symnum > c.seg.nsect { + return errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect) + } + + rSym = c.seg.sect[rel.symnum-1].sym + if rSym == 0 { + return errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name) + } + } else { + if rel.symnum >= symtab.nsym { + return errorf("invalid relocation: symbol reference out of range") + } + + rSym = symtab.sym[rel.symnum].sym + } + + r, _ := sb.AddRel(rType) + r.SetOff(rOff) + r.SetSiz(rSize) + r.SetSym(rSym) + r.SetAdd(rAdd) + + rAdd = 0 // clear rAdd for next iteration + } + + sb.SortRelocs() + } + + return textp, nil +} + +func cstring(x []byte) string { + i := bytes.IndexByte(x, '\x00') + if i >= 0 { + x = x[:i] + } + return string(x) +} diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go new file mode 100644 index 0000000..a5c025d --- /dev/null +++ b/src/cmd/link/internal/loadpe/ldpe.go @@ -0,0 +1,536 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package loadpe implements a PE/COFF file reader. +package loadpe + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/pe" + "encoding/binary" + "errors" + "fmt" + "io" + "strings" +) + +const ( + // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 (same with IMAGE_SYM_DTYPE_POINTER and IMAGE_SYM_DTYPE_FUNCTION) + IMAGE_SYM_UNDEFINED = 0 + IMAGE_SYM_ABSOLUTE = -1 + IMAGE_SYM_DEBUG = -2 + IMAGE_SYM_TYPE_NULL = 0 + IMAGE_SYM_TYPE_VOID = 1 + IMAGE_SYM_TYPE_CHAR = 2 + IMAGE_SYM_TYPE_SHORT = 3 + IMAGE_SYM_TYPE_INT = 4 + IMAGE_SYM_TYPE_LONG = 5 + IMAGE_SYM_TYPE_FLOAT = 6 + IMAGE_SYM_TYPE_DOUBLE = 7 + IMAGE_SYM_TYPE_STRUCT = 8 + IMAGE_SYM_TYPE_UNION = 9 + IMAGE_SYM_TYPE_ENUM = 10 + IMAGE_SYM_TYPE_MOE = 11 + IMAGE_SYM_TYPE_BYTE = 12 + IMAGE_SYM_TYPE_WORD = 13 + IMAGE_SYM_TYPE_UINT = 14 + IMAGE_SYM_TYPE_DWORD = 15 + IMAGE_SYM_TYPE_PCODE = 32768 + IMAGE_SYM_DTYPE_NULL = 0 + IMAGE_SYM_DTYPE_POINTER = 0x10 + IMAGE_SYM_DTYPE_FUNCTION = 0x20 + IMAGE_SYM_DTYPE_ARRAY = 0x30 + IMAGE_SYM_CLASS_END_OF_FUNCTION = -1 + IMAGE_SYM_CLASS_NULL = 0 + IMAGE_SYM_CLASS_AUTOMATIC = 1 + IMAGE_SYM_CLASS_EXTERNAL = 2 + IMAGE_SYM_CLASS_STATIC = 3 + IMAGE_SYM_CLASS_REGISTER = 4 + IMAGE_SYM_CLASS_EXTERNAL_DEF = 5 + IMAGE_SYM_CLASS_LABEL = 6 + IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7 + IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8 + IMAGE_SYM_CLASS_ARGUMENT = 9 + IMAGE_SYM_CLASS_STRUCT_TAG = 10 + IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11 + IMAGE_SYM_CLASS_UNION_TAG = 12 + IMAGE_SYM_CLASS_TYPE_DEFINITION = 13 + IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14 + IMAGE_SYM_CLASS_ENUM_TAG = 15 + IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16 + IMAGE_SYM_CLASS_REGISTER_PARAM = 17 + IMAGE_SYM_CLASS_BIT_FIELD = 18 + IMAGE_SYM_CLASS_FAR_EXTERNAL = 68 /* Not in PECOFF v8 spec */ + IMAGE_SYM_CLASS_BLOCK = 100 + IMAGE_SYM_CLASS_FUNCTION = 101 + IMAGE_SYM_CLASS_END_OF_STRUCT = 102 + IMAGE_SYM_CLASS_FILE = 103 + IMAGE_SYM_CLASS_SECTION = 104 + IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105 + IMAGE_SYM_CLASS_CLR_TOKEN = 107 + IMAGE_REL_I386_ABSOLUTE = 0x0000 + IMAGE_REL_I386_DIR16 = 0x0001 + IMAGE_REL_I386_REL16 = 0x0002 + IMAGE_REL_I386_DIR32 = 0x0006 + IMAGE_REL_I386_DIR32NB = 0x0007 + IMAGE_REL_I386_SEG12 = 0x0009 + IMAGE_REL_I386_SECTION = 0x000A + IMAGE_REL_I386_SECREL = 0x000B + IMAGE_REL_I386_TOKEN = 0x000C + IMAGE_REL_I386_SECREL7 = 0x000D + IMAGE_REL_I386_REL32 = 0x0014 + IMAGE_REL_AMD64_ABSOLUTE = 0x0000 + IMAGE_REL_AMD64_ADDR64 = 0x0001 + IMAGE_REL_AMD64_ADDR32 = 0x0002 + IMAGE_REL_AMD64_ADDR32NB = 0x0003 + IMAGE_REL_AMD64_REL32 = 0x0004 + IMAGE_REL_AMD64_REL32_1 = 0x0005 + IMAGE_REL_AMD64_REL32_2 = 0x0006 + IMAGE_REL_AMD64_REL32_3 = 0x0007 + IMAGE_REL_AMD64_REL32_4 = 0x0008 + IMAGE_REL_AMD64_REL32_5 = 0x0009 + IMAGE_REL_AMD64_SECTION = 0x000A + IMAGE_REL_AMD64_SECREL = 0x000B + IMAGE_REL_AMD64_SECREL7 = 0x000C + IMAGE_REL_AMD64_TOKEN = 0x000D + IMAGE_REL_AMD64_SREL32 = 0x000E + IMAGE_REL_AMD64_PAIR = 0x000F + IMAGE_REL_AMD64_SSPAN32 = 0x0010 + IMAGE_REL_ARM_ABSOLUTE = 0x0000 + IMAGE_REL_ARM_ADDR32 = 0x0001 + IMAGE_REL_ARM_ADDR32NB = 0x0002 + IMAGE_REL_ARM_BRANCH24 = 0x0003 + IMAGE_REL_ARM_BRANCH11 = 0x0004 + IMAGE_REL_ARM_SECTION = 0x000E + IMAGE_REL_ARM_SECREL = 0x000F + IMAGE_REL_ARM_MOV32 = 0x0010 + IMAGE_REL_THUMB_MOV32 = 0x0011 + IMAGE_REL_THUMB_BRANCH20 = 0x0012 + IMAGE_REL_THUMB_BRANCH24 = 0x0014 + IMAGE_REL_THUMB_BLX23 = 0x0015 + IMAGE_REL_ARM_PAIR = 0x0016 +) + +// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld, ideally in debug/pe. +const ( + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 +) + +// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf + +// peBiobuf makes bio.Reader look like io.ReaderAt. +type peBiobuf bio.Reader + +func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) { + ret := ((*bio.Reader)(f)).MustSeek(off, 0) + if ret < 0 { + return 0, errors.New("fail to seek") + } + n, err := f.Read(p) + if err != nil { + return 0, err + } + return n, nil +} + +// makeUpdater creates a loader.SymbolBuilder if one hasn't been created previously. +// We use this to lazily make SymbolBuilders as we don't always need a builder, and creating them for all symbols might be an error. +func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loader.SymbolBuilder { + if bld != nil { + return bld + } + bld = l.MakeSymbolUpdater(s) + return bld +} + +// Load loads the PE file pn from input. +// Symbols are written into syms, and a slice of the text symbols is returned. +// If an .rsrc section or set of .rsrc$xx sections is found, its symbols are +// returned as rsrc. +func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc []loader.Sym, err error) { + lookup := func(name string, version int) (*loader.SymbolBuilder, loader.Sym) { + s := l.LookupOrCreateSym(name, version) + sb := l.MakeSymbolUpdater(s) + return sb, s + } + sectsyms := make(map[*pe.Section]loader.Sym) + sectdata := make(map[*pe.Section][]byte) + + // Some input files are archives containing multiple of + // object files, and pe.NewFile seeks to the start of + // input file and get confused. Create section reader + // to stop pe.NewFile looking before current position. + sr := io.NewSectionReader((*peBiobuf)(input), input.Offset(), 1<<63-1) + + // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details) + f, err := pe.NewFile(sr) + if err != nil { + return nil, nil, err + } + defer f.Close() + + // TODO return error if found .cormeta + + // create symbols for mapped sections + for _, sect := range f.Sections { + if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + + if sect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue + } + + name := fmt.Sprintf("%s(%s)", pkg, sect.Name) + bld, s := lookup(name, localSymVersion) + + switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) { + case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata + bld.SetType(sym.SRODATA) + + case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss + bld.SetType(sym.SNOPTRBSS) + + case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data + bld.SetType(sym.SNOPTRDATA) + + case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text + bld.SetType(sym.STEXT) + + default: + return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name) + } + + if bld.Type() != sym.SNOPTRBSS { + data, err := sect.Data() + if err != nil { + return nil, nil, err + } + sectdata[sect] = data + bld.SetData(data) + } + bld.SetSize(int64(sect.Size)) + sectsyms[sect] = s + if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") { + rsrc = append(rsrc, s) + } + } + + // load relocations + for _, rsect := range f.Sections { + if _, found := sectsyms[rsect]; !found { + continue + } + if rsect.NumberOfRelocations == 0 { + continue + } + if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + if rsect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue + } + + splitResources := strings.HasPrefix(rsect.Name, ".rsrc$") + sb := l.MakeSymbolUpdater(sectsyms[rsect]) + for j, r := range rsect.Relocs { + if int(r.SymbolTableIndex) >= len(f.COFFSymbols) { + return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols)) + } + pesym := &f.COFFSymbols[r.SymbolTableIndex] + _, gosym, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion) + if err != nil { + return nil, nil, err + } + if gosym == 0 { + name, err := pesym.FullName(f.StringTable) + if err != nil { + name = string(pesym.Name[:]) + } + return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type) + } + + rSym := gosym + rSize := uint8(4) + rOff := int32(r.VirtualAddress) + var rAdd int64 + var rType objabi.RelocType + switch arch.Family { + default: + return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family) + case sys.I386, sys.AMD64: + switch r.Type { + default: + return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type) + + case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, + IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 + IMAGE_REL_AMD64_ADDR32NB: + rType = objabi.R_PCREL + + rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + + case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32: + rType = objabi.R_ADDR + + // load addend from image + rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + + case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 + rSize = 8 + + rType = objabi.R_ADDR + + // load addend from image + rAdd = int64(binary.LittleEndian.Uint64(sectdata[rsect][rOff:])) + } + + case sys.ARM: + switch r.Type { + default: + return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type) + + case IMAGE_REL_ARM_SECREL: + rType = objabi.R_PCREL + + rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + + case IMAGE_REL_ARM_ADDR32, IMAGE_REL_ARM_ADDR32NB: + rType = objabi.R_ADDR + + rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + + case IMAGE_REL_ARM_BRANCH24: + rType = objabi.R_CALLARM + + rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + } + } + + // ld -r could generate multiple section symbols for the + // same section but with different values, we have to take + // that into account, or in the case of split resources, + // the section and its symbols are split into two sections. + if issect(pesym) || splitResources { + rAdd += int64(pesym.Value) + } + + rel, _ := sb.AddRel(rType) + rel.SetOff(rOff) + rel.SetSiz(rSize) + rel.SetSym(rSym) + rel.SetAdd(rAdd) + } + + sb.SortRelocs() + } + + // enter sub-symbols into symbol table. + for i, numaux := 0, 0; i < len(f.COFFSymbols); i += numaux + 1 { + pesym := &f.COFFSymbols[i] + + numaux = int(pesym.NumberOfAuxSymbols) + + name, err := pesym.FullName(f.StringTable) + if err != nil { + return nil, nil, err + } + if name == "" { + continue + } + if issect(pesym) { + continue + } + if int(pesym.SectionNumber) > len(f.Sections) { + continue + } + if pesym.SectionNumber == IMAGE_SYM_DEBUG { + continue + } + if pesym.SectionNumber == IMAGE_SYM_ABSOLUTE && bytes.Equal(pesym.Name[:], []byte("@feat.00")) { + // Microsoft's linker looks at whether all input objects have an empty + // section called @feat.00. If all of them do, then it enables SEH; + // otherwise it doesn't enable that feature. So, since around the Windows + // XP SP2 era, most tools that make PE objects just tack on that section, + // so that it won't gimp Microsoft's linker logic. Go doesn't support SEH, + // so in theory, none of this really matters to us. But actually, if the + // linker tries to ingest an object with @feat.00 -- which are produced by + // LLVM's resource compiler, for example -- it chokes because of the + // IMAGE_SYM_ABSOLUTE section that it doesn't know how to deal with. Since + // @feat.00 is just a marking anyway, skip IMAGE_SYM_ABSOLUTE sections that + // are called @feat.00. + continue + } + var sect *pe.Section + if pesym.SectionNumber > 0 { + sect = f.Sections[pesym.SectionNumber-1] + if _, found := sectsyms[sect]; !found { + continue + } + } + + bld, s, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion) + if err != nil { + return nil, nil, err + } + + if pesym.SectionNumber == 0 { // extern + if l.SymType(s) == sym.SDYNIMPORT { + bld = makeUpdater(l, bld, s) + bld.SetPlt(-2) // flag for dynimport in PE object files. + } + if l.SymType(s) == sym.SXREF && pesym.Value > 0 { // global data + bld = makeUpdater(l, bld, s) + bld.SetType(sym.SNOPTRDATA) + bld.SetSize(int64(pesym.Value)) + } + + continue + } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) { + sect = f.Sections[pesym.SectionNumber-1] + if _, found := sectsyms[sect]; !found { + return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s) + } + } else { + return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s) + } + + if sect == nil { + return nil, nil, nil + } + + if l.OuterSym(s) != 0 { + if l.AttrDuplicateOK(s) { + continue + } + outerName := l.SymName(l.OuterSym(s)) + sectName := l.SymName(sectsyms[sect]) + return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName) + } + + bld = makeUpdater(l, bld, s) + sectsym := sectsyms[sect] + bld.SetType(l.SymType(sectsym)) + l.AddInteriorSym(sectsym, s) + bld.SetValue(int64(pesym.Value)) + bld.SetSize(4) + if l.SymType(sectsym) == sym.STEXT { + if bld.External() && !bld.DuplicateOK() { + return nil, nil, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s)) + } + bld.SetExternal(true) + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for _, sect := range f.Sections { + s := sectsyms[sect] + if s == 0 { + continue + } + l.SortSub(s) + if l.SymType(s) == sym.STEXT { + for ; s != 0; s = l.SubSym(s) { + if l.AttrOnList(s) { + return nil, nil, fmt.Errorf("symbol %s listed multiple times", l.SymName(s)) + } + l.SetAttrOnList(s, true) + textp = append(textp, s) + } + } + } + + return textp, rsrc, nil +} + +func issect(s *pe.COFFSymbol) bool { + return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.' +} + +func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader.Sym, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]loader.Sym, localSymVersion int) (*loader.SymbolBuilder, loader.Sym, error) { + symname, err := pesym.FullName(f.StringTable) + if err != nil { + return nil, 0, err + } + var name string + if issect(pesym) { + name = l.SymName(sectsyms[f.Sections[pesym.SectionNumber-1]]) + } else { + name = symname + switch arch.Family { + case sys.AMD64: + if name == "__imp___acrt_iob_func" { + // Do not rename __imp___acrt_iob_func into __acrt_iob_func, + // because __imp___acrt_iob_func symbol is real + // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details). + } else { + name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name + } + case sys.I386: + if name == "__imp____acrt_iob_func" { + // Do not rename __imp____acrt_iob_func into ___acrt_iob_func, + // because __imp____acrt_iob_func symbol is real + // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details). + } else { + name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name + } + if name[0] == '_' { + name = name[1:] // _Name => Name + } + } + } + + // remove last @XXX + if i := strings.LastIndex(name, "@"); i >= 0 { + name = name[:i] + } + + var s loader.Sym + var bld *loader.SymbolBuilder + switch pesym.Type { + default: + return nil, 0, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type) + + case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL: + switch pesym.StorageClass { + case IMAGE_SYM_CLASS_EXTERNAL: //global + s = lookup(name, 0) + + case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL: + s = lookup(name, localSymVersion) + bld = makeUpdater(l, bld, s) + bld.SetDuplicateOK(true) + + default: + return nil, 0, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass) + } + } + + if s != 0 && l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) { + bld = makeUpdater(l, bld, s) + bld.SetType(sym.SXREF) + } + if strings.HasPrefix(symname, "__imp_") { + bld = makeUpdater(l, bld, s) + bld.SetGot(-2) // flag for __imp_ + } + + return bld, s, nil +} diff --git a/src/cmd/link/internal/loadxcoff/ldxcoff.go b/src/cmd/link/internal/loadxcoff/ldxcoff.go new file mode 100644 index 0000000..a574421 --- /dev/null +++ b/src/cmd/link/internal/loadxcoff/ldxcoff.go @@ -0,0 +1,228 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package loadxcoff implements a XCOFF file reader. +package loadxcoff + +import ( + "cmd/internal/bio" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "errors" + "fmt" + "internal/xcoff" +) + +// ldSection is an XCOFF section with its symbols. +type ldSection struct { + xcoff.Section + sym loader.Sym +} + +// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating xcoffBiobuf + +// xcoffBiobuf makes bio.Reader look like io.ReaderAt. +type xcoffBiobuf bio.Reader + +func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) { + ret := ((*bio.Reader)(f)).MustSeek(off, 0) + if ret < 0 { + return 0, errors.New("fail to seek") + } + n, err := f.Read(p) + if err != nil { + return 0, err + } + return n, nil +} + +// loads the Xcoff file pn from f. +// Symbols are written into loader, and a slice of the text symbols is returned. +func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) { + errorf := func(str string, args ...interface{}) ([]loader.Sym, error) { + return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...)) + } + + var ldSections []*ldSection + + f, err := xcoff.NewFile((*xcoffBiobuf)(input)) + if err != nil { + return nil, err + } + defer f.Close() + + for _, sect := range f.Sections { + //only text, data and bss section + if sect.Type < xcoff.STYP_TEXT || sect.Type > xcoff.STYP_BSS { + continue + } + lds := new(ldSection) + lds.Section = *sect + name := fmt.Sprintf("%s(%s)", pkg, lds.Name) + symbol := l.LookupOrCreateSym(name, localSymVersion) + s := l.MakeSymbolUpdater(symbol) + + switch lds.Type { + default: + return errorf("unrecognized section type 0x%x", lds.Type) + case xcoff.STYP_TEXT: + s.SetType(sym.STEXT) + case xcoff.STYP_DATA: + s.SetType(sym.SNOPTRDATA) + case xcoff.STYP_BSS: + s.SetType(sym.SNOPTRBSS) + } + + s.SetSize(int64(lds.Size)) + if s.Type() != sym.SNOPTRBSS { + data, err := lds.Section.Data() + if err != nil { + return nil, err + } + s.SetData(data) + } + + lds.sym = symbol + ldSections = append(ldSections, lds) + } + + // sx = symbol from file + // s = symbol for loader + for _, sx := range f.Symbols { + // get symbol type + stype, errmsg := getSymbolType(f, sx) + if errmsg != "" { + return errorf("error reading symbol %s: %s", sx.Name, errmsg) + } + if stype == sym.Sxxx { + continue + } + + s := l.LookupOrCreateSym(sx.Name, 0) + + // Text symbol + if l.SymType(s) == sym.STEXT { + if l.AttrOnList(s) { + return errorf("symbol %s listed multiple times", l.SymName(s)) + } + l.SetAttrOnList(s, true) + textp = append(textp, s) + } + } + + // Read relocations + for _, sect := range ldSections { + // TODO(aix): Dwarf section relocation if needed + if sect.Type != xcoff.STYP_TEXT && sect.Type != xcoff.STYP_DATA { + continue + } + sb := l.MakeSymbolUpdater(sect.sym) + for _, rx := range sect.Relocs { + rSym := l.LookupOrCreateSym(rx.Symbol.Name, 0) + if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress { + return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress) + } + rOff := int32(rx.VirtualAddress) + var rSize uint8 + var rType objabi.RelocType + var rAdd int64 + switch rx.Type { + default: + return errorf("section %s: unknown relocation of type 0x%x", sect.Name, rx.Type) + case xcoff.R_POS: + // Reloc the address of r.Sym + // Length should be 64 + if rx.Length != 64 { + return errorf("section %s: relocation R_POS has length different from 64: %d", sect.Name, rx.Length) + } + rSize = 8 + rType = objabi.R_CONST + rAdd = int64(rx.Symbol.Value) + + case xcoff.R_RBR: + rSize = 4 + rType = objabi.R_CALLPOWER + rAdd = 0 + } + r, _ := sb.AddRel(rType) + r.SetOff(rOff) + r.SetSiz(rSize) + r.SetSym(rSym) + r.SetAdd(rAdd) + } + } + return textp, nil + +} + +// Convert symbol xcoff type to sym.SymKind +// Returns nil if this shouldn't be added into loader (like .file or .dw symbols ) +func getSymbolType(f *xcoff.File, s *xcoff.Symbol) (stype sym.SymKind, err string) { + // .file symbol + if s.SectionNumber == -2 { + if s.StorageClass == xcoff.C_FILE { + return sym.Sxxx, "" + } + return sym.Sxxx, "unrecognised StorageClass for sectionNumber = -2" + } + + // extern symbols + // TODO(aix) + if s.SectionNumber == 0 { + return sym.Sxxx, "" + } + + sectType := f.Sections[s.SectionNumber-1].SectionHeader.Type + switch sectType { + default: + return sym.Sxxx, fmt.Sprintf("getSymbolType for Section type 0x%x not implemented", sectType) + case xcoff.STYP_DWARF, xcoff.STYP_DEBUG: + return sym.Sxxx, "" + case xcoff.STYP_DATA, xcoff.STYP_BSS, xcoff.STYP_TEXT: + } + + switch s.StorageClass { + default: + return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x not implemented", s.StorageClass) + case xcoff.C_HIDEXT, xcoff.C_EXT, xcoff.C_WEAKEXT: + switch s.AuxCSect.StorageMappingClass { + default: + return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x and Storage Map 0x%x not implemented", s.StorageClass, s.AuxCSect.StorageMappingClass) + + // Program Code + case xcoff.XMC_PR: + if sectType == xcoff.STYP_TEXT { + return sym.STEXT, "" + } + return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_PR", sectType, s.StorageClass) + + // Read/Write Data + case xcoff.XMC_RW: + if sectType == xcoff.STYP_DATA { + return sym.SDATA, "" + } + if sectType == xcoff.STYP_BSS { + return sym.SBSS, "" + } + return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_RW", sectType, s.StorageClass) + + // Function descriptor + case xcoff.XMC_DS: + if sectType == xcoff.STYP_DATA { + return sym.SDATA, "" + } + return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass) + + // TOC anchor and TOC entry + case xcoff.XMC_TC0, xcoff.XMC_TE: + if sectType == xcoff.STYP_DATA { + return sym.SXCOFFTOC, "" + } + return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass) + + } + } +} diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go new file mode 100644 index 0000000..17b1b20 --- /dev/null +++ b/src/cmd/link/internal/mips/asm.go @@ -0,0 +1,156 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2016 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" +) + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + return +} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + out.Write32(uint32(sectoff)) + + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + switch r.Type { + default: + return false + case objabi.R_ADDR, objabi.R_DWARFSECREF: + if r.Size != 4 { + return false + } + out.Write32(uint32(elf.R_MIPS_32) | uint32(elfsym)<<8) + case objabi.R_ADDRMIPS: + out.Write32(uint32(elf.R_MIPS_LO16) | uint32(elfsym)<<8) + case objabi.R_ADDRMIPSU: + out.Write32(uint32(elf.R_MIPS_HI16) | uint32(elfsym)<<8) + case objabi.R_ADDRMIPSTLS: + out.Write32(uint32(elf.R_MIPS_TLS_TPREL_LO16) | uint32(elfsym)<<8) + case objabi.R_CALLMIPS, objabi.R_JMPMIPS: + out.Write32(uint32(elf.R_MIPS_26) | uint32(elfsym)<<8) + } + + return true +} + +func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { + return +} + +func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { + return false +} + +func applyrel(arch *sys.Arch, ldr *loader.Loader, rt objabi.RelocType, off int32, s loader.Sym, val int64, t int64) int64 { + o := uint32(val) + switch rt { + case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSTLS: + return int64(o&0xffff0000 | uint32(t)&0xffff) + case objabi.R_ADDRMIPSU: + return int64(o&0xffff0000 | uint32((t+(1<<15))>>16)&0xffff) + case objabi.R_CALLMIPS, objabi.R_JMPMIPS: + return int64(o&0xfc000000 | uint32(t>>2)&^0xfc000000) + default: + return val + } +} + +func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) { + rs := r.Sym() + rs = ldr.ResolveABIAlias(rs) + if target.IsExternal() { + switch r.Type() { + default: + return val, 0, false + + case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU: + // set up addend for eventual relocation via outer symbol. + _, off := ld.FoldSubSymbolOffset(ldr, rs) + xadd := r.Add() + off + return applyrel(target.Arch, ldr, r.Type(), r.Off(), s, val, xadd), 1, true + + case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS: + return applyrel(target.Arch, ldr, r.Type(), r.Off(), s, val, r.Add()), 1, true + } + } + + const isOk = true + const noExtReloc = 0 + switch rt := r.Type(); rt { + case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU: + t := ldr.SymValue(rs) + r.Add() + return applyrel(target.Arch, ldr, rt, r.Off(), s, val, t), noExtReloc, isOk + case objabi.R_CALLMIPS, objabi.R_JMPMIPS: + t := ldr.SymValue(rs) + r.Add() + + if t&3 != 0 { + ldr.Errorf(s, "direct call is not aligned: %s %x", ldr.SymName(rs), t) + } + + // check if target address is in the same 256 MB region as the next instruction + if (ldr.SymValue(s)+int64(r.Off())+4)&0xf0000000 != (t & 0xf0000000) { + ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t) + } + + return applyrel(target.Arch, ldr, rt, r.Off(), s, val, t), noExtReloc, isOk + case objabi.R_ADDRMIPSTLS: + // thread pointer is at 0x7000 offset from the start of TLS data area + t := ldr.SymValue(rs) + r.Add() - 0x7000 + if t < -32768 || t >= 32678 { + ldr.Errorf(s, "TLS offset out of range %d", t) + } + return applyrel(target.Arch, ldr, rt, r.Off(), s, val, t), noExtReloc, isOk + } + + return val, 0, false +} + +func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 { + return -1 +} + +func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) { + switch r.Type() { + case objabi.R_ADDRMIPS, objabi.R_ADDRMIPSU: + return ld.ExtrelocViaOuterSym(ldr, r, s), true + + case objabi.R_ADDRMIPSTLS, objabi.R_CALLMIPS, objabi.R_JMPMIPS: + return ld.ExtrelocSimple(ldr, r), true + } + return loader.ExtReloc{}, false +} diff --git a/src/cmd/link/internal/mips/l.go b/src/cmd/link/internal/mips/l.go new file mode 100644 index 0000000..affc48c --- /dev/null +++ b/src/cmd/link/internal/mips/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2016 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2016 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + MaxAlign = 32 // max data alignment + MinAlign = 1 // min data alignment + FuncAlign = 4 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + DWARFREGSP = 29 + DWARFREGLR = 31 +) diff --git a/src/cmd/link/internal/mips/obj.go b/src/cmd/link/internal/mips/obj.go new file mode 100644 index 0000000..f20597c --- /dev/null +++ b/src/cmd/link/internal/mips/obj.go @@ -0,0 +1,88 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2016 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchMIPS + if objabi.GOARCH == "mipsle" { + arch = sys.ArchMIPSLE + } + + theArch := ld.Arch{ + Funcalign: FuncAlign, + Maxalign: MaxAlign, + Minalign: MinAlign, + Dwarfregsp: DWARFREGSP, + Dwarfreglr: DWARFREGLR, + + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Extreloc: extreloc, + Elfreloc1: elfreloc1, + ElfrelocSize: 8, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + + Linuxdynld: "/lib/ld.so.1", + + Freebsddynld: "XXX", + Openbsddynld: "XXX", + Netbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + case objabi.Hlinux: /* mips elf */ + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + } +} diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go new file mode 100644 index 0000000..4789b41 --- /dev/null +++ b/src/cmd/link/internal/mips64/asm.go @@ -0,0 +1,157 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" +) + +func gentext(ctxt *ld.Link, ldr *loader.Loader) {} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + + // mips64 ELF relocation (endian neutral) + // offset uint64 + // sym uint32 + // ssym uint8 + // type3 uint8 + // type2 uint8 + // type uint8 + // addend int64 + + out.Write64(uint64(sectoff)) + + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + out.Write32(uint32(elfsym)) + out.Write8(0) + out.Write8(0) + out.Write8(0) + switch r.Type { + default: + return false + case objabi.R_ADDR, objabi.R_DWARFSECREF: + switch r.Size { + case 4: + out.Write8(uint8(elf.R_MIPS_32)) + case 8: + out.Write8(uint8(elf.R_MIPS_64)) + default: + return false + } + case objabi.R_ADDRMIPS: + out.Write8(uint8(elf.R_MIPS_LO16)) + case objabi.R_ADDRMIPSU: + out.Write8(uint8(elf.R_MIPS_HI16)) + case objabi.R_ADDRMIPSTLS: + out.Write8(uint8(elf.R_MIPS_TLS_TPREL_LO16)) + case objabi.R_CALLMIPS, + objabi.R_JMPMIPS: + out.Write8(uint8(elf.R_MIPS_26)) + } + out.Write64(uint64(r.Xadd)) + + return true +} + +func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) { + return +} + +func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { + return false +} + +func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) { + if target.IsExternal() { + switch r.Type() { + default: + return val, 0, false + + case objabi.R_ADDRMIPS, + objabi.R_ADDRMIPSU, + objabi.R_ADDRMIPSTLS, + objabi.R_CALLMIPS, + objabi.R_JMPMIPS: + return val, 1, true + } + } + + const isOk = true + const noExtReloc = 0 + rs := r.Sym() + rs = ldr.ResolveABIAlias(rs) + switch r.Type() { + case objabi.R_ADDRMIPS, + objabi.R_ADDRMIPSU: + t := ldr.SymValue(rs) + r.Add() + if r.Type() == objabi.R_ADDRMIPS { + return int64(val&0xffff0000 | t&0xffff), noExtReloc, isOk + } + return int64(val&0xffff0000 | ((t+1<<15)>>16)&0xffff), noExtReloc, isOk + case objabi.R_ADDRMIPSTLS: + // thread pointer is at 0x7000 offset from the start of TLS data area + t := ldr.SymValue(rs) + r.Add() - 0x7000 + if t < -32768 || t >= 32678 { + ldr.Errorf(s, "TLS offset out of range %d", t) + } + return int64(val&0xffff0000 | t&0xffff), noExtReloc, isOk + case objabi.R_CALLMIPS, + objabi.R_JMPMIPS: + // Low 26 bits = (S + A) >> 2 + t := ldr.SymValue(rs) + r.Add() + return int64(val&0xfc000000 | (t>>2)&^0xfc000000), noExtReloc, isOk + } + + return val, 0, false +} + +func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 { + return -1 +} + +func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) { + switch r.Type() { + case objabi.R_ADDRMIPS, + objabi.R_ADDRMIPSU: + return ld.ExtrelocViaOuterSym(ldr, r, s), true + + case objabi.R_ADDRMIPSTLS, + objabi.R_CALLMIPS, + objabi.R_JMPMIPS: + return ld.ExtrelocSimple(ldr, r), true + } + return loader.ExtReloc{}, false +} diff --git a/src/cmd/link/internal/mips64/l.go b/src/cmd/link/internal/mips64/l.go new file mode 100644 index 0000000..837af0e --- /dev/null +++ b/src/cmd/link/internal/mips64/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips64 + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 8 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 29 + dwarfRegLR = 31 +) diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go new file mode 100644 index 0000000..01d89a2 --- /dev/null +++ b/src/cmd/link/internal/mips64/obj.go @@ -0,0 +1,98 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mips64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchMIPS64 + if objabi.GOARCH == "mips64le" { + arch = sys.ArchMIPS64LE + } + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Extreloc: extreloc, + Elfreloc1: elfreloc1, + ElfrelocSize: 24, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + + Linuxdynld: "/lib64/ld64.so.1", + Freebsddynld: "XXX", + Openbsddynld: "/usr/libexec/ld.so", + Netbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 16*1024 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 16 * 1024 + } + + case objabi.Hlinux, /* mips64 elf */ + objabi.Hopenbsd: + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + } +} diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go new file mode 100644 index 0000000..539afac --- /dev/null +++ b/src/cmd/link/internal/ppc64/asm.go @@ -0,0 +1,1075 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "encoding/binary" + "fmt" + "log" + "strings" +) + +func genplt(ctxt *ld.Link, ldr *loader.Loader) { + // The ppc64 ABI PLT has similar concepts to other + // architectures, but is laid out quite differently. When we + // see an R_PPC64_REL24 relocation to a dynamic symbol + // (indicating that the call needs to go through the PLT), we + // generate up to three stubs and reserve a PLT slot. + // + // 1) The call site will be bl x; nop (where the relocation + // applies to the bl). We rewrite this to bl x_stub; ld + // r2,24(r1). The ld is necessary because x_stub will save + // r2 (the TOC pointer) at 24(r1) (the "TOC save slot"). + // + // 2) We reserve space for a pointer in the .plt section (once + // per referenced dynamic function). .plt is a data + // section filled solely by the dynamic linker (more like + // .plt.got on other architectures). Initially, the + // dynamic linker will fill each slot with a pointer to the + // corresponding x@plt entry point. + // + // 3) We generate the "call stub" x_stub (once per dynamic + // function/object file pair). This saves the TOC in the + // TOC save slot, reads the function pointer from x's .plt + // slot and calls it like any other global entry point + // (including setting r12 to the function address). + // + // 4) We generate the "symbol resolver stub" x@plt (once per + // dynamic function). This is solely a branch to the glink + // resolver stub. + // + // 5) We generate the glink resolver stub (only once). This + // computes which symbol resolver stub we came through and + // invokes the dynamic resolver via a pointer provided by + // the dynamic linker. This will patch up the .plt slot to + // point directly at the function so future calls go + // straight from the call stub to the real function, and + // then call the function. + + // NOTE: It's possible we could make ppc64 closer to other + // architectures: ppc64's .plt is like .plt.got on other + // platforms and ppc64's .glink is like .plt on other + // platforms. + + // Find all R_PPC64_REL24 relocations that reference dynamic + // imports. Reserve PLT entries for these symbols and + // generate call stubs. The call stubs need to live in .text, + // which is why we need to do this pass this early. + // + // This assumes "case 1" from the ABI, where the caller needs + // us to save and restore the TOC pointer. + var stubs []loader.Sym + for _, s := range ctxt.Textp { + relocs := ldr.Relocs(s) + for i := 0; i < relocs.Count(); i++ { + r := relocs.At(i) + if r.Type() != objabi.ElfRelocOffset+objabi.RelocType(elf.R_PPC64_REL24) || ldr.SymType(r.Sym()) != sym.SDYNIMPORT { + continue + } + + // Reserve PLT entry and generate symbol + // resolver + addpltsym(ctxt, ldr, r.Sym()) + + // Generate call stub. Important to note that we're looking + // up the stub using the same version as the parent symbol (s), + // needed so that symtoc() will select the right .TOC. symbol + // when processing the stub. In older versions of the linker + // this was done by setting stub.Outer to the parent, but + // if the stub has the right version initially this is not needed. + n := fmt.Sprintf("%s.%s", ldr.SymName(s), ldr.SymName(r.Sym())) + stub := ldr.CreateSymForUpdate(n, ldr.SymVersion(s)) + if stub.Size() == 0 { + stubs = append(stubs, stub.Sym()) + gencallstub(ctxt, ldr, 1, stub, r.Sym()) + } + + // Update the relocation to use the call stub + r.SetSym(stub.Sym()) + + // make sure the data is writeable + if ldr.AttrReadOnly(s) { + panic("can't write to read-only sym data") + } + + // Restore TOC after bl. The compiler put a + // nop here for us to overwrite. + sp := ldr.Data(s) + const o1 = 0xe8410018 // ld r2,24(r1) + ctxt.Arch.ByteOrder.PutUint32(sp[r.Off()+4:], o1) + } + } + // Put call stubs at the beginning (instead of the end). + // So when resolving the relocations to calls to the stubs, + // the addresses are known and trampolines can be inserted + // when necessary. + ctxt.Textp = append(stubs, ctxt.Textp...) +} + +func genaddmoduledata(ctxt *ld.Link, ldr *loader.Loader) { + initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt) + if initfunc == nil { + return + } + + o := func(op uint32) { + initfunc.AddUint32(ctxt.Arch, op) + } + + // addis r2, r12, .TOC.-func@ha + toc := ctxt.DotTOC[0] + rel1, _ := initfunc.AddRel(objabi.R_ADDRPOWER_PCREL) + rel1.SetOff(0) + rel1.SetSiz(8) + rel1.SetSym(toc) + o(0x3c4c0000) + // addi r2, r2, .TOC.-func@l + o(0x38420000) + // mflr r31 + o(0x7c0802a6) + // stdu r31, -32(r1) + o(0xf801ffe1) + // addis r3, r2, local.moduledata@got@ha + var tgt loader.Sym + if s := ldr.Lookup("local.moduledata", 0); s != 0 { + tgt = s + } else if s := ldr.Lookup("local.pluginmoduledata", 0); s != 0 { + tgt = s + } else { + tgt = ldr.LookupOrCreateSym("runtime.firstmoduledata", 0) + } + rel2, _ := initfunc.AddRel(objabi.R_ADDRPOWER_GOT) + rel2.SetOff(int32(initfunc.Size())) + rel2.SetSiz(8) + rel2.SetSym(tgt) + o(0x3c620000) + // ld r3, local.moduledata@got@l(r3) + o(0xe8630000) + // bl runtime.addmoduledata + rel3, _ := initfunc.AddRel(objabi.R_CALLPOWER) + rel3.SetOff(int32(initfunc.Size())) + rel3.SetSiz(4) + rel3.SetSym(addmoduledata) + o(0x48000001) + // nop + o(0x60000000) + // ld r31, 0(r1) + o(0xe8010000) + // mtlr r31 + o(0x7c0803a6) + // addi r1,r1,32 + o(0x38210020) + // blr + o(0x4e800020) +} + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + if ctxt.DynlinkingGo() { + genaddmoduledata(ctxt, ldr) + } + + if ctxt.LinkMode == ld.LinkInternal { + genplt(ctxt, ldr) + } +} + +// Construct a call stub in stub that calls symbol targ via its PLT +// entry. +func gencallstub(ctxt *ld.Link, ldr *loader.Loader, abicase int, stub *loader.SymbolBuilder, targ loader.Sym) { + if abicase != 1 { + // If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC + // relocations, we'll need to implement cases 2 and 3. + log.Fatalf("gencallstub only implements case 1 calls") + } + + plt := ctxt.PLT + + stub.SetType(sym.STEXT) + + // Save TOC pointer in TOC save slot + stub.AddUint32(ctxt.Arch, 0xf8410018) // std r2,24(r1) + + // Load the function pointer from the PLT. + rel, ri1 := stub.AddRel(objabi.R_POWER_TOC) + rel.SetOff(int32(stub.Size())) + rel.SetSiz(2) + rel.SetAdd(int64(ldr.SymPlt(targ))) + rel.SetSym(plt) + if ctxt.Arch.ByteOrder == binary.BigEndian { + rel.SetOff(rel.Off() + int32(rel.Siz())) + } + ldr.SetRelocVariant(stub.Sym(), int(ri1), sym.RV_POWER_HA) + stub.AddUint32(ctxt.Arch, 0x3d820000) // addis r12,r2,targ@plt@toc@ha + + rel2, ri2 := stub.AddRel(objabi.R_POWER_TOC) + rel2.SetOff(int32(stub.Size())) + rel2.SetSiz(2) + rel2.SetAdd(int64(ldr.SymPlt(targ))) + rel2.SetSym(plt) + if ctxt.Arch.ByteOrder == binary.BigEndian { + rel2.SetOff(rel2.Off() + int32(rel2.Siz())) + } + ldr.SetRelocVariant(stub.Sym(), int(ri2), sym.RV_POWER_LO) + stub.AddUint32(ctxt.Arch, 0xe98c0000) // ld r12,targ@plt@toc@l(r12) + + // Jump to the loaded pointer + stub.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12 + stub.AddUint32(ctxt.Arch, 0x4e800420) // bctr +} + +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + if target.IsElf() { + return addelfdynrel(target, ldr, syms, s, r, rIdx) + } else if target.IsAIX() { + return ld.Xcoffadddynrel(target, ldr, syms, s, r, rIdx) + } + return false +} + +func addelfdynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch r.Type() { + default: + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL24): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CALLPOWER) + + // This is a local call, so the caller isn't setting + // up r12 and r2 is the same for the caller and + // callee. Hence, we need to go to the local entry + // point. (If we don't do this, the callee will try + // to use r12 to compute r2.) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymLocalentry(targ))*4) + + if targType == sym.SDYNIMPORT { + // Should have been handled in elfsetupplt + ldr.Errorf(s, "unexpected R_PPC64_REL24 for dyn import") + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC_REL32): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_PPC_REL32 for dyn import") + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_ADDR64): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + if targType == sym.SDYNIMPORT { + // These happen in .toc sections + ld.Adddynsym(ldr, target, syms, targ) + + rela := ldr.MakeSymbolUpdater(syms.Rela) + rela.AddAddrPlus(target.Arch, s, int64(r.Off())) + rela.AddUint64(target.Arch, elf.R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_PPC64_ADDR64))) + rela.AddUint64(target.Arch, uint64(r.Add())) + su.SetRelocType(rIdx, objabi.ElfRelocOffset) // ignore during relocsym + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO|sym.RV_CHECK_OVERFLOW) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HA): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_HI): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HI|sym.RV_CHECK_OVERFLOW) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_DS): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS|sym.RV_CHECK_OVERFLOW) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_TOC16_LO_DS): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_POWER_TOC) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_DS) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_LO): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_LO) + su.SetRelocAdd(rIdx, r.Add()+2) // Compensate for relocation size of 2 + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HI): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HI|sym.RV_CHECK_OVERFLOW) + su.SetRelocAdd(rIdx, r.Add()+2) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_PPC64_REL16_HA): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_POWER_HA|sym.RV_CHECK_OVERFLOW) + su.SetRelocAdd(rIdx, r.Add()+2) + return true + } + + // Handle references to ELF symbols from our own object files. + if targType != sym.SDYNIMPORT { + return true + } + + // TODO(austin): Translate our relocations to ELF + + return false +} + +func xcoffreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool { + rs := r.Xsym + + emitReloc := func(v uint16, off uint64) { + out.Write64(uint64(sectoff) + off) + out.Write32(uint32(ldr.SymDynid(rs))) + out.Write16(v) + } + + var v uint16 + switch r.Type { + default: + return false + case objabi.R_ADDR, objabi.R_DWARFSECREF: + v = ld.XCOFF_R_POS + if r.Size == 4 { + v |= 0x1F << 8 + } else { + v |= 0x3F << 8 + } + emitReloc(v, 0) + case objabi.R_ADDRPOWER_TOCREL: + case objabi.R_ADDRPOWER_TOCREL_DS: + emitReloc(ld.XCOFF_R_TOCU|(0x0F<<8), 2) + emitReloc(ld.XCOFF_R_TOCL|(0x0F<<8), 6) + case objabi.R_POWER_TLS_LE: + emitReloc(ld.XCOFF_R_TLS_LE|0x0F<<8, 2) + case objabi.R_CALLPOWER: + if r.Size != 4 { + return false + } + emitReloc(ld.XCOFF_R_RBR|0x19<<8, 0) + case objabi.R_XCOFFREF: + emitReloc(ld.XCOFF_R_REF|0x3F<<8, 0) + } + return true + +} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + // Beware that bit0~bit15 start from the third byte of a instruction in Big-Endian machines. + rt := r.Type + if rt == objabi.R_ADDR || rt == objabi.R_POWER_TLS || rt == objabi.R_CALLPOWER { + } else { + if ctxt.Arch.ByteOrder == binary.BigEndian { + sectoff += 2 + } + } + out.Write64(uint64(sectoff)) + + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + switch rt { + default: + return false + case objabi.R_ADDR, objabi.R_DWARFSECREF: + switch r.Size { + case 4: + out.Write64(uint64(elf.R_PPC64_ADDR32) | uint64(elfsym)<<32) + case 8: + out.Write64(uint64(elf.R_PPC64_ADDR64) | uint64(elfsym)<<32) + default: + return false + } + case objabi.R_POWER_TLS: + out.Write64(uint64(elf.R_PPC64_TLS) | uint64(elfsym)<<32) + case objabi.R_POWER_TLS_LE: + out.Write64(uint64(elf.R_PPC64_TPREL16) | uint64(elfsym)<<32) + case objabi.R_POWER_TLS_IE: + out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_HA) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_PPC64_GOT_TPREL16_LO_DS) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER: + out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_PPC64_ADDR16_LO) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER_DS: + out.Write64(uint64(elf.R_PPC64_ADDR16_HA) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_PPC64_ADDR16_LO_DS) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER_GOT: + out.Write64(uint64(elf.R_PPC64_GOT16_HA) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_PPC64_GOT16_LO_DS) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER_PCREL: + out.Write64(uint64(elf.R_PPC64_REL16_HA) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_PPC64_REL16_LO) | uint64(elfsym)<<32) + r.Xadd += 4 + case objabi.R_ADDRPOWER_TOCREL: + out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_PPC64_TOC16_LO) | uint64(elfsym)<<32) + case objabi.R_ADDRPOWER_TOCREL_DS: + out.Write64(uint64(elf.R_PPC64_TOC16_HA) | uint64(elfsym)<<32) + out.Write64(uint64(r.Xadd)) + out.Write64(uint64(sectoff + 4)) + out.Write64(uint64(elf.R_PPC64_TOC16_LO_DS) | uint64(elfsym)<<32) + case objabi.R_CALLPOWER: + if r.Size != 4 { + return false + } + out.Write64(uint64(elf.R_PPC64_REL24) | uint64(elfsym)<<32) + + } + out.Write64(uint64(r.Xadd)) + + return true +} + +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { + // The dynamic linker stores the address of the + // dynamic resolver and the DSO identifier in the two + // doublewords at the beginning of the .plt section + // before the PLT array. Reserve space for these. + plt.SetSize(16) + } +} + +func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { + return false +} + +// Return the value of .TOC. for symbol s +func symtoc(ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) int64 { + v := ldr.SymVersion(s) + if out := ldr.OuterSym(s); out != 0 { + v = ldr.SymVersion(out) + } + + toc := syms.DotTOC[v] + if toc == 0 { + ldr.Errorf(s, "TOC-relative relocation in object without .TOC.") + return 0 + } + + return ldr.SymValue(toc) +} + +// archreloctoc relocates a TOC relative symbol. +// If the symbol pointed by this TOC relative symbol is in .data or .bss, the +// default load instruction can be changed to an addi instruction and the +// symbol address can be used directly. +// This code is for AIX only. +func archreloctoc(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) int64 { + rs := ldr.ResolveABIAlias(r.Sym()) + if target.IsLinux() { + ldr.Errorf(s, "archrelocaddr called for %s relocation\n", ldr.SymName(rs)) + } + var o1, o2 uint32 + + o1 = uint32(val >> 32) + o2 = uint32(val) + + if !strings.HasPrefix(ldr.SymName(rs), "TOC.") { + ldr.Errorf(s, "archreloctoc called for a symbol without TOC anchor") + } + var t int64 + useAddi := false + relocs := ldr.Relocs(rs) + tarSym := ldr.ResolveABIAlias(relocs.At(0).Sym()) + + if target.IsInternal() && tarSym != 0 && ldr.AttrReachable(tarSym) && ldr.SymSect(tarSym).Seg == &ld.Segdata { + t = ldr.SymValue(tarSym) + r.Add() - ldr.SymValue(syms.TOC) + // change ld to addi in the second instruction + o2 = (o2 & 0x03FF0000) | 0xE<<26 + useAddi = true + } else { + t = ldr.SymValue(rs) + r.Add() - ldr.SymValue(syms.TOC) + } + + if t != int64(int32(t)) { + ldr.Errorf(s, "TOC relocation for %s is too big to relocate %s: 0x%x", ldr.SymName(s), rs, t) + } + + if t&0x8000 != 0 { + t += 0x10000 + } + + o1 |= uint32((t >> 16) & 0xFFFF) + + switch r.Type() { + case objabi.R_ADDRPOWER_TOCREL_DS: + if useAddi { + o2 |= uint32(t) & 0xFFFF + } else { + if t&3 != 0 { + ldr.Errorf(s, "bad DS reloc for %s: %d", ldr.SymName(s), ldr.SymValue(rs)) + } + o2 |= uint32(t) & 0xFFFC + } + default: + return -1 + } + + return int64(o1)<<32 | int64(o2) +} + +// archrelocaddr relocates a symbol address. +// This code is for AIX only. +func archrelocaddr(ldr *loader.Loader, target *ld.Target, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) int64 { + rs := ldr.ResolveABIAlias(r.Sym()) + if target.IsAIX() { + ldr.Errorf(s, "archrelocaddr called for %s relocation\n", ldr.SymName(rs)) + } + var o1, o2 uint32 + if target.IsBigEndian() { + o1 = uint32(val >> 32) + o2 = uint32(val) + } else { + o1 = uint32(val) + o2 = uint32(val >> 32) + } + + // We are spreading a 31-bit address across two instructions, putting the + // high (adjusted) part in the low 16 bits of the first instruction and the + // low part in the low 16 bits of the second instruction, or, in the DS case, + // bits 15-2 (inclusive) of the address into bits 15-2 of the second + // instruction (it is an error in this case if the low 2 bits of the address + // are non-zero). + + t := ldr.SymAddr(rs) + r.Add() + if t < 0 || t >= 1<<31 { + ldr.Errorf(s, "relocation for %s is too big (>=2G): 0x%x", ldr.SymName(s), ldr.SymValue(rs)) + } + if t&0x8000 != 0 { + t += 0x10000 + } + + switch r.Type() { + case objabi.R_ADDRPOWER: + o1 |= (uint32(t) >> 16) & 0xffff + o2 |= uint32(t) & 0xffff + case objabi.R_ADDRPOWER_DS: + o1 |= (uint32(t) >> 16) & 0xffff + if t&3 != 0 { + ldr.Errorf(s, "bad DS reloc for %s: %d", ldr.SymName(s), ldr.SymValue(rs)) + } + o2 |= uint32(t) & 0xfffc + default: + return -1 + } + + if target.IsBigEndian() { + return int64(o1)<<32 | int64(o2) + } + return int64(o2)<<32 | int64(o1) +} + +// Determine if the code was compiled so that the TOC register R2 is initialized and maintained +func r2Valid(ctxt *ld.Link) bool { + switch ctxt.BuildMode { + case ld.BuildModeCArchive, ld.BuildModeCShared, ld.BuildModePIE, ld.BuildModeShared, ld.BuildModePlugin: + return true + } + // -linkshared option + return ctxt.IsSharedGoLink() +} + +// resolve direct jump relocation r in s, and add trampoline if necessary +func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) { + + // Trampolines are created if the branch offset is too large and the linker cannot insert a call stub to handle it. + // For internal linking, trampolines are always created for long calls. + // For external linking, the linker can insert a call stub to handle a long call, but depends on having the TOC address in + // r2. For those build modes with external linking where the TOC address is not maintained in r2, trampolines must be created. + if ctxt.IsExternal() && r2Valid(ctxt) { + // No trampolines needed since r2 contains the TOC + return + } + + relocs := ldr.Relocs(s) + r := relocs.At(ri) + var t int64 + // ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet + // laid out. Conservatively use a trampoline. This should be rare, as we lay out packages + // in dependency order. + if ldr.SymValue(rs) != 0 { + t = ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off())) + } + switch r.Type() { + case objabi.R_CALLPOWER: + + // If branch offset is too far then create a trampoline. + + if (ctxt.IsExternal() && ldr.SymSect(s) != ldr.SymSect(rs)) || (ctxt.IsInternal() && int64(int32(t<<6)>>6) != t) || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && ldr.SymPkg(s) != ldr.SymPkg(rs)) { + var tramp loader.Sym + for i := 0; ; i++ { + + // Using r.Add as part of the name is significant in functions like duffzero where the call + // target is at some offset within the function. Calls to duff+8 and duff+256 must appear as + // distinct trampolines. + + oName := ldr.SymName(rs) + name := oName + if r.Add() == 0 { + name += fmt.Sprintf("-tramp%d", i) + } else { + name += fmt.Sprintf("%+x-tramp%d", r.Add(), i) + } + + // Look up the trampoline in case it already exists + + tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs))) + if oName == "runtime.deferreturn" { + ldr.SetIsDeferReturnTramp(tramp, true) + } + if ldr.SymValue(tramp) == 0 { + break + } + + t = ldr.SymValue(tramp) + r.Add() - (ldr.SymValue(s) + int64(r.Off())) + + // With internal linking, the trampoline can be used if it is not too far. + // With external linking, the trampoline must be in this section for it to be reused. + if (ctxt.IsInternal() && int64(int32(t<<6)>>6) == t) || (ctxt.IsExternal() && ldr.SymSect(s) == ldr.SymSect(tramp)) { + break + } + } + if ldr.SymType(tramp) == 0 { + if r2Valid(ctxt) { + // Should have returned for above cases + ctxt.Errorf(s, "unexpected trampoline for shared or dynamic linking") + } else { + trampb := ldr.MakeSymbolUpdater(tramp) + ctxt.AddTramp(trampb) + gentramp(ctxt, ldr, trampb, rs, r.Add()) + } + } + sb := ldr.MakeSymbolUpdater(s) + relocs := sb.Relocs() + r := relocs.At(ri) + r.SetSym(tramp) + r.SetAdd(0) // This was folded into the trampoline target address + } + default: + ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type())) + } +} + +func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) { + tramp.SetSize(16) // 4 instructions + P := make([]byte, tramp.Size()) + t := ldr.SymValue(target) + offset + var o1, o2 uint32 + + if ctxt.IsAIX() { + // On AIX, the address is retrieved with a TOC symbol. + // For internal linking, the "Linux" way might still be used. + // However, all text symbols are accessed with a TOC symbol as + // text relocations aren't supposed to be possible. + // So, keep using the external linking way to be more AIX friendly. + o1 = uint32(0x3fe20000) // lis r2, toctargetaddr hi + o2 = uint32(0xebff0000) // ld r31, toctargetaddr lo + + toctramp := ldr.CreateSymForUpdate("TOC."+ldr.SymName(tramp.Sym()), 0) + toctramp.SetType(sym.SXCOFFTOC) + toctramp.AddAddrPlus(ctxt.Arch, target, offset) + + r, _ := tramp.AddRel(objabi.R_ADDRPOWER_TOCREL_DS) + r.SetOff(0) + r.SetSiz(8) // generates 2 relocations: HA + LO + r.SetSym(toctramp.Sym()) + } else { + // Used for default build mode for an executable + // Address of the call target is generated using + // relocation and doesn't depend on r2 (TOC). + o1 = uint32(0x3fe00000) // lis r31,targetaddr hi + o2 = uint32(0x3bff0000) // addi r31,targetaddr lo + + // With external linking, the target address must be + // relocated using LO and HA + if ctxt.IsExternal() || ldr.SymValue(target) == 0 { + r, _ := tramp.AddRel(objabi.R_ADDRPOWER) + r.SetOff(0) + r.SetSiz(8) // generates 2 relocations: HA + LO + r.SetSym(target) + r.SetAdd(offset) + } else { + // adjustment needed if lo has sign bit set + // when using addi to compute address + val := uint32((t & 0xffff0000) >> 16) + if t&0x8000 != 0 { + val += 1 + } + o1 |= val // hi part of addr + o2 |= uint32(t & 0xffff) // lo part of addr + } + } + + o3 := uint32(0x7fe903a6) // mtctr r31 + o4 := uint32(0x4e800420) // bctr + ctxt.Arch.ByteOrder.PutUint32(P, o1) + ctxt.Arch.ByteOrder.PutUint32(P[4:], o2) + ctxt.Arch.ByteOrder.PutUint32(P[8:], o3) + ctxt.Arch.ByteOrder.PutUint32(P[12:], o4) + tramp.SetData(P) +} + +func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (relocatedOffset int64, nExtReloc int, ok bool) { + rs := ldr.ResolveABIAlias(r.Sym()) + if target.IsExternal() { + // On AIX, relocations (except TLS ones) must be also done to the + // value with the current addresses. + switch rt := r.Type(); rt { + default: + if !target.IsAIX() { + return val, nExtReloc, false + } + case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE: + // check Outer is nil, Type is TLSBSS? + nExtReloc = 1 + if rt == objabi.R_POWER_TLS_IE { + nExtReloc = 2 // need two ELF relocations, see elfreloc1 + } + return val, nExtReloc, true + case objabi.R_ADDRPOWER, + objabi.R_ADDRPOWER_DS, + objabi.R_ADDRPOWER_TOCREL, + objabi.R_ADDRPOWER_TOCREL_DS, + objabi.R_ADDRPOWER_GOT, + objabi.R_ADDRPOWER_PCREL: + nExtReloc = 2 // need two ELF relocations, see elfreloc1 + if !target.IsAIX() { + return val, nExtReloc, true + } + case objabi.R_CALLPOWER: + nExtReloc = 1 + if !target.IsAIX() { + return val, nExtReloc, true + } + } + } + + switch r.Type() { + case objabi.R_ADDRPOWER_TOCREL, objabi.R_ADDRPOWER_TOCREL_DS: + return archreloctoc(ldr, target, syms, r, s, val), nExtReloc, true + case objabi.R_ADDRPOWER, objabi.R_ADDRPOWER_DS: + return archrelocaddr(ldr, target, syms, r, s, val), nExtReloc, true + case objabi.R_CALLPOWER: + // Bits 6 through 29 = (S + A - P) >> 2 + + t := ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off())) + + if t&3 != 0 { + ldr.Errorf(s, "relocation for %s+%d is not aligned: %d", ldr.SymName(rs), r.Off(), t) + } + // If branch offset is too far then create a trampoline. + + if int64(int32(t<<6)>>6) != t { + ldr.Errorf(s, "direct call too far: %s %x", ldr.SymName(rs), t) + } + return val | int64(uint32(t)&^0xfc000003), nExtReloc, true + case objabi.R_POWER_TOC: // S + A - .TOC. + return ldr.SymValue(rs) + r.Add() - symtoc(ldr, syms, s), nExtReloc, true + + case objabi.R_POWER_TLS_LE: + // The thread pointer points 0x7000 bytes after the start of the + // thread local storage area as documented in section "3.7.2 TLS + // Runtime Handling" of "Power Architecture 64-Bit ELF V2 ABI + // Specification". + v := ldr.SymValue(rs) - 0x7000 + if target.IsAIX() { + // On AIX, the thread pointer points 0x7800 bytes after + // the TLS. + v -= 0x800 + } + if int64(int16(v)) != v { + ldr.Errorf(s, "TLS offset out of range %d", v) + } + return (val &^ 0xffff) | (v & 0xffff), nExtReloc, true + } + + return val, nExtReloc, false +} + +func archrelocvariant(target *ld.Target, ldr *loader.Loader, r loader.Reloc, rv sym.RelocVariant, s loader.Sym, t int64) (relocatedOffset int64) { + rs := ldr.ResolveABIAlias(r.Sym()) + switch rv & sym.RV_TYPE_MASK { + default: + ldr.Errorf(s, "unexpected relocation variant %d", rv) + fallthrough + + case sym.RV_NONE: + return t + + case sym.RV_POWER_LO: + if rv&sym.RV_CHECK_OVERFLOW != 0 { + // Whether to check for signed or unsigned + // overflow depends on the instruction + var o1 uint32 + if target.IsBigEndian() { + o1 = binary.BigEndian.Uint32(ldr.Data(s)[r.Off()-2:]) + } else { + o1 = binary.LittleEndian.Uint32(ldr.Data(s)[r.Off():]) + } + switch o1 >> 26 { + case 24, // ori + 26, // xori + 28: // andi + if t>>16 != 0 { + goto overflow + } + + default: + if int64(int16(t)) != t { + goto overflow + } + } + } + + return int64(int16(t)) + + case sym.RV_POWER_HA: + t += 0x8000 + fallthrough + + // Fallthrough + case sym.RV_POWER_HI: + t >>= 16 + + if rv&sym.RV_CHECK_OVERFLOW != 0 { + // Whether to check for signed or unsigned + // overflow depends on the instruction + var o1 uint32 + if target.IsBigEndian() { + o1 = binary.BigEndian.Uint32(ldr.Data(s)[r.Off()-2:]) + } else { + o1 = binary.LittleEndian.Uint32(ldr.Data(s)[r.Off():]) + } + switch o1 >> 26 { + case 25, // oris + 27, // xoris + 29: // andis + if t>>16 != 0 { + goto overflow + } + + default: + if int64(int16(t)) != t { + goto overflow + } + } + } + + return int64(int16(t)) + + case sym.RV_POWER_DS: + var o1 uint32 + if target.IsBigEndian() { + o1 = uint32(binary.BigEndian.Uint16(ldr.Data(s)[r.Off():])) + } else { + o1 = uint32(binary.LittleEndian.Uint16(ldr.Data(s)[r.Off():])) + } + if t&3 != 0 { + ldr.Errorf(s, "relocation for %s+%d is not aligned: %d", ldr.SymName(rs), r.Off(), t) + } + if (rv&sym.RV_CHECK_OVERFLOW != 0) && int64(int16(t)) != t { + goto overflow + } + return int64(o1)&0x3 | int64(int16(t)) + } + +overflow: + ldr.Errorf(s, "relocation for %s+%d is too big: %d", ldr.SymName(rs), r.Off(), t) + return t +} + +func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) { + switch r.Type() { + case objabi.R_POWER_TLS, objabi.R_POWER_TLS_LE, objabi.R_POWER_TLS_IE, objabi.R_CALLPOWER: + return ld.ExtrelocSimple(ldr, r), true + case objabi.R_ADDRPOWER, + objabi.R_ADDRPOWER_DS, + objabi.R_ADDRPOWER_TOCREL, + objabi.R_ADDRPOWER_TOCREL_DS, + objabi.R_ADDRPOWER_GOT, + objabi.R_ADDRPOWER_PCREL: + return ld.ExtrelocViaOuterSym(ldr, r, s), true + } + return loader.ExtReloc{}, false +} + +func addpltsym(ctxt *ld.Link, ldr *loader.Loader, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { + return + } + + ld.Adddynsym(ldr, &ctxt.Target, &ctxt.ArchSyms, s) + + if ctxt.IsELF { + plt := ldr.MakeSymbolUpdater(ctxt.PLT) + rela := ldr.MakeSymbolUpdater(ctxt.RelaPLT) + if plt.Size() == 0 { + panic("plt is not set up") + } + + // Create the glink resolver if necessary + glink := ensureglinkresolver(ctxt, ldr) + + // Write symbol resolver stub (just a branch to the + // glink resolver stub) + rel, _ := glink.AddRel(objabi.R_CALLPOWER) + rel.SetOff(int32(glink.Size())) + rel.SetSiz(4) + rel.SetSym(glink.Sym()) + glink.AddUint32(ctxt.Arch, 0x48000000) // b .glink + + // In the ppc64 ABI, the dynamic linker is responsible + // for writing the entire PLT. We just need to + // reserve 8 bytes for each PLT entry and generate a + // JMP_SLOT dynamic relocation for it. + // + // TODO(austin): ABI v1 is different + ldr.SetPlt(s, int32(plt.Size())) + + plt.Grow(plt.Size() + 8) + plt.SetSize(plt.Size() + 8) + + rela.AddAddrPlus(ctxt.Arch, plt.Sym(), int64(ldr.SymPlt(s))) + rela.AddUint64(ctxt.Arch, elf.R_INFO(uint32(ldr.SymDynid(s)), uint32(elf.R_PPC64_JMP_SLOT))) + rela.AddUint64(ctxt.Arch, 0) + } else { + ctxt.Errorf(s, "addpltsym: unsupported binary format") + } +} + +// Generate the glink resolver stub if necessary and return the .glink section +func ensureglinkresolver(ctxt *ld.Link, ldr *loader.Loader) *loader.SymbolBuilder { + glink := ldr.CreateSymForUpdate(".glink", 0) + if glink.Size() != 0 { + return glink + } + + // This is essentially the resolver from the ppc64 ELF ABI. + // At entry, r12 holds the address of the symbol resolver stub + // for the target routine and the argument registers hold the + // arguments for the target routine. + // + // This stub is PIC, so first get the PC of label 1 into r11. + // Other things will be relative to this. + glink.AddUint32(ctxt.Arch, 0x7c0802a6) // mflr r0 + glink.AddUint32(ctxt.Arch, 0x429f0005) // bcl 20,31,1f + glink.AddUint32(ctxt.Arch, 0x7d6802a6) // 1: mflr r11 + glink.AddUint32(ctxt.Arch, 0x7c0803a6) // mtlf r0 + + // Compute the .plt array index from the entry point address. + // Because this is PIC, everything is relative to label 1b (in + // r11): + // r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4 + glink.AddUint32(ctxt.Arch, 0x3800ffd0) // li r0,-(res_0-1b)=-48 + glink.AddUint32(ctxt.Arch, 0x7c006214) // add r0,r0,r12 + glink.AddUint32(ctxt.Arch, 0x7c0b0050) // sub r0,r0,r11 + glink.AddUint32(ctxt.Arch, 0x7800f082) // srdi r0,r0,2 + + // r11 = address of the first byte of the PLT + glink.AddSymRef(ctxt.Arch, ctxt.PLT, 0, objabi.R_ADDRPOWER, 8) + + glink.AddUint32(ctxt.Arch, 0x3d600000) // addis r11,0,.plt@ha + glink.AddUint32(ctxt.Arch, 0x396b0000) // addi r11,r11,.plt@l + + // Load r12 = dynamic resolver address and r11 = DSO + // identifier from the first two doublewords of the PLT. + glink.AddUint32(ctxt.Arch, 0xe98b0000) // ld r12,0(r11) + glink.AddUint32(ctxt.Arch, 0xe96b0008) // ld r11,8(r11) + + // Jump to the dynamic resolver + glink.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12 + glink.AddUint32(ctxt.Arch, 0x4e800420) // bctr + + // The symbol resolvers must immediately follow. + // res_0: + + // Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes + // before the first symbol resolver stub. + du := ldr.MakeSymbolUpdater(ctxt.Dynamic) + ld.Elfwritedynentsymplus(ctxt, du, elf.DT_PPC64_GLINK, glink.Sym(), glink.Size()-32) + + return glink +} diff --git a/src/cmd/link/internal/ppc64/l.go b/src/cmd/link/internal/ppc64/l.go new file mode 100644 index 0000000..e8d3b68 --- /dev/null +++ b/src/cmd/link/internal/ppc64/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 16 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 1 + dwarfRegLR = 65 +) diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go new file mode 100644 index 0000000..ef4393f --- /dev/null +++ b/src/cmd/link/internal/ppc64/obj.go @@ -0,0 +1,107 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchPPC64 + if objabi.GOARCH == "ppc64le" { + arch = sys.ArchPPC64LE + } + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + WriteTextBlocks: true, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Extreloc: extreloc, + Elfreloc1: elfreloc1, + ElfrelocSize: 24, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Trampoline: trampoline, + Machoreloc1: machoreloc1, + Xcoffreloc1: xcoffreloc1, + + // TODO(austin): ABI v1 uses /usr/lib/ld.so.1, + Linuxdynld: "/lib64/ld64.so.1", + + Freebsddynld: "XXX", + Openbsddynld: "XXX", + Netbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4128 + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hlinux: /* ppc64 elf */ + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + + case objabi.Haix: + ld.Xcoffinit(ctxt) + } +} diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go new file mode 100644 index 0000000..c18e054 --- /dev/null +++ b/src/cmd/link/internal/riscv64/asm.go @@ -0,0 +1,244 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package riscv64 + +import ( + "cmd/internal/obj/riscv" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "fmt" + "log" + "sort" +) + +// fakeLabelName matches the RISCV_FAKE_LABEL_NAME from binutils. +const fakeLabelName = ".L0 " + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { +} + +func genSymsLate(ctxt *ld.Link, ldr *loader.Loader) { + if ctxt.LinkMode != ld.LinkExternal { + return + } + + // Generate a local text symbol for each relocation target, as the + // R_RISCV_PCREL_LO12_* relocations generated by elfreloc1 need it. + if ctxt.Textp == nil { + log.Fatal("genSymsLate called before Textp has been assigned") + } + var hi20Syms []loader.Sym + for _, s := range ctxt.Textp { + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.Type() != objabi.R_RISCV_PCREL_ITYPE && r.Type() != objabi.R_RISCV_PCREL_STYPE && + r.Type() != objabi.R_RISCV_TLS_IE_ITYPE && r.Type() != objabi.R_RISCV_TLS_IE_STYPE { + continue + } + if r.Off() == 0 && ldr.SymType(s) == sym.STEXT { + // Use the symbol for the function instead of creating + // an overlapping symbol. + continue + } + + // TODO(jsing): Consider generating ELF symbols without needing + // loader symbols, in order to reduce memory consumption. This + // would require changes to genelfsym so that it called + // putelfsym and putelfsyment as appropriate. + sb := ldr.MakeSymbolBuilder(fakeLabelName) + sb.SetType(sym.STEXT) + sb.SetValue(ldr.SymValue(s) + int64(r.Off())) + sb.SetLocal(true) + sb.SetReachable(true) + sb.SetVisibilityHidden(true) + sb.SetSect(ldr.SymSect(s)) + if outer := ldr.OuterSym(s); outer != 0 { + ldr.AddInteriorSym(outer, sb.Sym()) + } + hi20Syms = append(hi20Syms, sb.Sym()) + } + } + ctxt.Textp = append(ctxt.Textp, hi20Syms...) + ldr.SortSyms(ctxt.Textp) +} + +func findHI20Symbol(ctxt *ld.Link, ldr *loader.Loader, val int64) loader.Sym { + idx := sort.Search(len(ctxt.Textp), func(i int) bool { return ldr.SymValue(ctxt.Textp[i]) >= val }) + if idx >= len(ctxt.Textp) { + return 0 + } + if s := ctxt.Textp[idx]; ldr.SymValue(s) == val && ldr.SymType(s) == sym.STEXT { + return s + } + return 0 +} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + switch r.Type { + case objabi.R_ADDR, objabi.R_DWARFSECREF: + out.Write64(uint64(sectoff)) + switch r.Size { + case 4: + out.Write64(uint64(elf.R_RISCV_32) | uint64(elfsym)<<32) + case 8: + out.Write64(uint64(elf.R_RISCV_64) | uint64(elfsym)<<32) + default: + ld.Errorf(nil, "unknown size %d for %v relocation", r.Size, r.Type) + return false + } + out.Write64(uint64(r.Xadd)) + + case objabi.R_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("elfsetupplt") +} + +func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { + log.Fatalf("machoreloc1 not implemented") + return false +} + +func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) { + 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 + + // Generate AUIPC and second instruction immediates. + low, high, err := riscv.Split32BitImmediate(off) + if err != nil { + ldr.Errorf(s, "R_RISCV_PCREL_ relocation does not fit in 32-bits: %d", off) + } + + auipcImm, err := riscv.EncodeUImmediate(high) + if err != nil { + ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ AUIPC relocation offset for %s: %v", ldr.SymName(rs), err) + } + + var secondImm, secondImmMask int64 + switch r.Type() { + case objabi.R_RISCV_PCREL_ITYPE: + secondImmMask = riscv.ITypeImmMask + secondImm, err = riscv.EncodeIImmediate(low) + if err != nil { + ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ITYPE I-type instruction relocation offset for %s: %v", ldr.SymName(rs), err) + } + case objabi.R_RISCV_PCREL_STYPE: + secondImmMask = riscv.STypeImmMask + secondImm, err = riscv.EncodeSImmediate(low) + if err != nil { + ldr.Errorf(s, "cannot encode R_RISCV_PCREL_STYPE S-type instruction relocation offset for %s: %v", ldr.SymName(rs), err) + } + default: + panic(fmt.Sprintf("Unknown relocation type: %v", r.Type())) + } + + auipc := int64(uint32(val)) + second := int64(uint32(val >> 32)) + + auipc = (auipc &^ riscv.UTypeImmMask) | int64(uint32(auipcImm)) + second = (second &^ secondImmMask) | int64(uint32(secondImm)) + + return second<<32 | auipc, 0, true + } + + return val, 0, false +} + +func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 { + log.Fatalf("archrelocvariant") + return -1 +} + +func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) { + switch r.Type() { + case objabi.R_RISCV_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/l.go b/src/cmd/link/internal/riscv64/l.go new file mode 100644 index 0000000..a302657 --- /dev/null +++ b/src/cmd/link/internal/riscv64/l.go @@ -0,0 +1,14 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package riscv64 + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 + funcAlign = 8 + + dwarfRegLR = 1 + dwarfRegSP = 2 +) diff --git a/src/cmd/link/internal/riscv64/obj.go b/src/cmd/link/internal/riscv64/obj.go new file mode 100644 index 0000000..917324d --- /dev/null +++ b/src/cmd/link/internal/riscv64/obj.go @@ -0,0 +1,60 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package riscv64 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchRISCV64 + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Extreloc: extreloc, + Elfreloc1: elfreloc1, + ElfrelocSize: 24, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + GenSymsLate: genSymsLate, + Machoreloc1: machoreloc1, + + Linuxdynld: "/lib/ld.so.1", + + Freebsddynld: "XXX", + Netbsddynld: "XXX", + Openbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + case objabi.Hlinux: + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + } +} diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go new file mode 100644 index 0000000..78d2cc8 --- /dev/null +++ b/src/cmd/link/internal/s390x/asm.go @@ -0,0 +1,455 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package s390x + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" +) + +// gentext generates assembly to append the local moduledata to the global +// moduledata linked list at initialization time. This is only done if the runtime +// is in a different module. +// +// <go.link.addmoduledata>: +// larl %r2, <local.moduledata> +// jg <runtime.addmoduledata@plt> +// undef +// +// The job of appending the moduledata is delegated to runtime.addmoduledata. +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt) + if initfunc == nil { + return + } + + // larl %r2, <local.moduledata> + initfunc.AddUint8(0xc0) + initfunc.AddUint8(0x20) + initfunc.AddSymRef(ctxt.Arch, ctxt.Moduledata, 6, objabi.R_PCREL, 4) + r1 := initfunc.Relocs() + ldr.SetRelocVariant(initfunc.Sym(), r1.Count()-1, sym.RV_390_DBL) + + // jg <runtime.addmoduledata[@plt]> + initfunc.AddUint8(0xc0) + initfunc.AddUint8(0xf4) + initfunc.AddSymRef(ctxt.Arch, addmoduledata, 6, objabi.R_CALL, 4) + r2 := initfunc.Relocs() + ldr.SetRelocVariant(initfunc.Sym(), r2.Count()-1, sym.RV_390_DBL) + + // undef (for debugging) + initfunc.AddUint32(ctxt.Arch, 0) +} + +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch r.Type() { + default: + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d", r.Type()) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_12), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT12): + ldr.Errorf(s, "s390x 12-bit relocations have not been implemented (relocation type %d)", r.Type()-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_8), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_16), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_64): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_390_nn relocation for dynamic symbol %s", ldr.SymName(targ)) + } + + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC64): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_390_PCnn relocation for dynamic symbol %s", ldr.SymName(targ)) + } + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT16), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOT64): + ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT16DBL), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32DBL): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + r.SetSym(syms.PLT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PLT64): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + r.SetSym(syms.PLT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_COPY): + ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GLOB_DAT): + ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_JMP_SLOT): + ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_RELATIVE): + ldr.Errorf(s, "unimplemented S390x relocation: %v", r.Type()-objabi.ElfRelocOffset) + return false + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTOFF): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_390_GOTOFF relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_GOTOFF) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPC): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + r.SetSym(syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC16DBL), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_PC32DBL): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_390_PCnnDBL relocation for dynamic symbol %s", ldr.SymName(targ)) + } + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTPCDBL): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL) + r.SetSym(syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(r.Siz())) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_390_GOTENT): + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_390_GLOB_DAT)) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + ldr.SetRelocVariant(s, rIdx, sym.RV_390_DBL) + r.SetSym(syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))+int64(r.Siz())) + return true + } + // Handle references to ELF symbols from our own object files. + if targType != sym.SDYNIMPORT { + return true + } + + return false +} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + out.Write64(uint64(sectoff)) + + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + siz := r.Size + switch r.Type { + default: + return false + case objabi.R_TLS_LE: + switch siz { + default: + return false + case 4: + // WARNING - silently ignored by linker in ELF64 + out.Write64(uint64(elf.R_390_TLS_LE32) | uint64(elfsym)<<32) + case 8: + // WARNING - silently ignored by linker in ELF32 + out.Write64(uint64(elf.R_390_TLS_LE64) | uint64(elfsym)<<32) + } + case objabi.R_TLS_IE: + switch siz { + default: + return false + case 4: + out.Write64(uint64(elf.R_390_TLS_IEENT) | uint64(elfsym)<<32) + } + case objabi.R_ADDR, objabi.R_DWARFSECREF: + switch siz { + default: + return false + case 4: + out.Write64(uint64(elf.R_390_32) | uint64(elfsym)<<32) + case 8: + out.Write64(uint64(elf.R_390_64) | uint64(elfsym)<<32) + } + case objabi.R_GOTPCREL: + if siz == 4 { + out.Write64(uint64(elf.R_390_GOTENT) | uint64(elfsym)<<32) + } else { + return false + } + case objabi.R_PCREL, objabi.R_PCRELDBL, objabi.R_CALL: + elfrel := elf.R_390_NONE + rVariant := ldr.RelocVariant(s, ri) + isdbl := rVariant&sym.RV_TYPE_MASK == sym.RV_390_DBL + // TODO(mundaym): all DBL style relocations should be + // signalled using the variant - see issue 14218. + switch r.Type { + case objabi.R_PCRELDBL, objabi.R_CALL: + isdbl = true + } + if ldr.SymType(r.Xsym) == sym.SDYNIMPORT && (ldr.SymElfType(r.Xsym) == elf.STT_FUNC || r.Type == objabi.R_CALL) { + if isdbl { + switch siz { + case 2: + elfrel = elf.R_390_PLT16DBL + case 4: + elfrel = elf.R_390_PLT32DBL + } + } else { + switch siz { + case 4: + elfrel = elf.R_390_PLT32 + case 8: + elfrel = elf.R_390_PLT64 + } + } + } else { + if isdbl { + switch siz { + case 2: + elfrel = elf.R_390_PC16DBL + case 4: + elfrel = elf.R_390_PC32DBL + } + } else { + switch siz { + case 2: + elfrel = elf.R_390_PC16 + case 4: + elfrel = elf.R_390_PC32 + case 8: + elfrel = elf.R_390_PC64 + } + } + } + if elfrel == elf.R_390_NONE { + return false // unsupported size/dbl combination + } + out.Write64(uint64(elfrel) | uint64(elfsym)<<32) + } + + out.Write64(uint64(r.Xadd)) + return true +} + +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { + // stg %r1,56(%r15) + plt.AddUint8(0xe3) + plt.AddUint8(0x10) + plt.AddUint8(0xf0) + plt.AddUint8(0x38) + plt.AddUint8(0x00) + plt.AddUint8(0x24) + // larl %r1,_GLOBAL_OFFSET_TABLE_ + plt.AddUint8(0xc0) + plt.AddUint8(0x10) + plt.AddSymRef(ctxt.Arch, got.Sym(), 6, objabi.R_PCRELDBL, 4) + // mvc 48(8,%r15),8(%r1) + plt.AddUint8(0xd2) + plt.AddUint8(0x07) + plt.AddUint8(0xf0) + plt.AddUint8(0x30) + plt.AddUint8(0x10) + plt.AddUint8(0x08) + // lg %r1,16(%r1) + plt.AddUint8(0xe3) + plt.AddUint8(0x10) + plt.AddUint8(0x10) + plt.AddUint8(0x10) + plt.AddUint8(0x00) + plt.AddUint8(0x04) + // br %r1 + plt.AddUint8(0x07) + plt.AddUint8(0xf1) + // nopr %r0 + plt.AddUint8(0x07) + plt.AddUint8(0x00) + // nopr %r0 + plt.AddUint8(0x07) + plt.AddUint8(0x00) + // nopr %r0 + plt.AddUint8(0x07) + plt.AddUint8(0x00) + + // assume got->size == 0 too + got.AddAddrPlus(ctxt.Arch, dynamic, 0) + + got.AddUint64(ctxt.Arch, 0) + got.AddUint64(ctxt.Arch, 0) + } +} + +func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { + return false +} + +func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) { + return val, 0, false +} + +func archrelocvariant(target *ld.Target, ldr *loader.Loader, r loader.Reloc, rv sym.RelocVariant, s loader.Sym, t int64) int64 { + switch rv & sym.RV_TYPE_MASK { + default: + ldr.Errorf(s, "unexpected relocation variant %d", rv) + return t + + case sym.RV_NONE: + return t + + case sym.RV_390_DBL: + if t&1 != 0 { + ldr.Errorf(s, "%s+%v is not 2-byte aligned", ldr.SymName(r.Sym()), ldr.SymValue(r.Sym())) + } + return t >> 1 + } +} + +func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { + return + } + + ld.Adddynsym(ldr, target, syms, s) + + if target.IsElf() { + plt := ldr.MakeSymbolUpdater(syms.PLT) + got := ldr.MakeSymbolUpdater(syms.GOT) + rela := ldr.MakeSymbolUpdater(syms.RelaPLT) + if plt.Size() == 0 { + panic("plt is not set up") + } + // larl %r1,_GLOBAL_OFFSET_TABLE_+index + + plt.AddUint8(0xc0) + plt.AddUint8(0x10) + plt.AddPCRelPlus(target.Arch, got.Sym(), got.Size()+6) + pltrelocs := plt.Relocs() + ldr.SetRelocVariant(plt.Sym(), pltrelocs.Count()-1, sym.RV_390_DBL) + + // add to got: pointer to current pos in plt + got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size()+8) // weird but correct + // lg %r1,0(%r1) + plt.AddUint8(0xe3) + plt.AddUint8(0x10) + plt.AddUint8(0x10) + plt.AddUint8(0x00) + plt.AddUint8(0x00) + plt.AddUint8(0x04) + // br %r1 + plt.AddUint8(0x07) + plt.AddUint8(0xf1) + // basr %r1,%r0 + plt.AddUint8(0x0d) + plt.AddUint8(0x10) + // lgf %r1,12(%r1) + plt.AddUint8(0xe3) + plt.AddUint8(0x10) + plt.AddUint8(0x10) + plt.AddUint8(0x0c) + plt.AddUint8(0x00) + plt.AddUint8(0x14) + // jg .plt + plt.AddUint8(0xc0) + plt.AddUint8(0xf4) + + plt.AddUint32(target.Arch, uint32(-((plt.Size() - 2) >> 1))) // roll-your-own relocation + //.plt index + plt.AddUint32(target.Arch, uint32(rela.Size())) // rela size before current entry + + // rela + rela.AddAddrPlus(target.Arch, got.Sym(), got.Size()-8) + + sDynid := ldr.SymDynid(s) + rela.AddUint64(target.Arch, elf.R_INFO(uint32(sDynid), uint32(elf.R_390_JMP_SLOT))) + rela.AddUint64(target.Arch, 0) + + ldr.SetPlt(s, int32(plt.Size()-32)) + + } else { + ldr.Errorf(s, "addpltsym: unsupported binary format") + } +} diff --git a/src/cmd/link/internal/s390x/l.go b/src/cmd/link/internal/s390x/l.go new file mode 100644 index 0000000..f040587 --- /dev/null +++ b/src/cmd/link/internal/s390x/l.go @@ -0,0 +1,74 @@ +// Inferno utils/5l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package s390x + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + maxAlign = 32 // max data alignment + minAlign = 2 // min data alignment + funcAlign = 16 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 15 + dwarfRegLR = 14 +) diff --git a/src/cmd/link/internal/s390x/obj.go b/src/cmd/link/internal/s390x/obj.go new file mode 100644 index 0000000..8acc1d4 --- /dev/null +++ b/src/cmd/link/internal/s390x/obj.go @@ -0,0 +1,87 @@ +// Inferno utils/5l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package s390x + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.ArchS390X + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Elfreloc1: elfreloc1, + ElfrelocSize: 24, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + + Linuxdynld: "/lib64/ld64.so.1", + + // not relevant for s390x + Freebsddynld: "XXX", + Openbsddynld: "XXX", + Netbsddynld: "XXX", + Dragonflydynld: "XXX", + Solarisdynld: "XXX", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hlinux: // s390x ELF + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x10000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 0x10000 + } + } +} diff --git a/src/cmd/link/internal/sym/compilation_unit.go b/src/cmd/link/internal/sym/compilation_unit.go new file mode 100644 index 0000000..5d7206d --- /dev/null +++ b/src/cmd/link/internal/sym/compilation_unit.go @@ -0,0 +1,35 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +import "cmd/internal/dwarf" + +// LoaderSym holds a loader.Sym value. We can't refer to this +// type from the sym package since loader imports sym. +type LoaderSym int + +// A CompilationUnit represents a set of source files that are compiled +// together. Since all Go sources in a Go package are compiled together, +// there's one CompilationUnit per package that represents all Go sources in +// that package, plus one for each assembly file. +// +// Equivalently, there's one CompilationUnit per object file in each Library +// loaded by the linker. +// +// These are used for both DWARF and pclntab generation. +type CompilationUnit struct { + Pkg string // The package name, eg ("fmt", or "runtime") + Lib *Library // Our library + PclnIndex int // Index of this CU in pclntab + PCs []dwarf.Range // PC ranges, relative to Textp[0] + DWInfo *dwarf.DWDie // CU root DIE + FileTable []string // The file table used in this compilation unit. + + Consts LoaderSym // Package constants DIEs + FuncDIEs []LoaderSym // Function DIE subtrees + AbsFnDIEs []LoaderSym // Abstract function DIE subtrees + RangeSyms []LoaderSym // Symbols for debug_range + Textp []LoaderSym // Text symbols in this CU +} diff --git a/src/cmd/link/internal/sym/library.go b/src/cmd/link/internal/sym/library.go new file mode 100644 index 0000000..876b5ff --- /dev/null +++ b/src/cmd/link/internal/sym/library.go @@ -0,0 +1,27 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +import "cmd/internal/goobj" + +type Library struct { + Objref string + Srcref string + File string + Pkg string + Shlib string + Fingerprint goobj.FingerprintType + Autolib []goobj.ImportedPkg + Imports []*Library + Main bool + Units []*CompilationUnit + + Textp []LoaderSym // text syms defined in this library + DupTextSyms []LoaderSym // dupok text syms defined in this library +} + +func (l Library) String() string { + return l.Pkg +} diff --git a/src/cmd/link/internal/sym/reloc.go b/src/cmd/link/internal/sym/reloc.go new file mode 100644 index 0000000..a543233 --- /dev/null +++ b/src/cmd/link/internal/sym/reloc.go @@ -0,0 +1,74 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "debug/elf" +) + +// RelocVariant is a linker-internal variation on a relocation. +type RelocVariant uint8 + +const ( + RV_NONE RelocVariant = iota + RV_POWER_LO + RV_POWER_HI + RV_POWER_HA + RV_POWER_DS + + // RV_390_DBL is a s390x-specific relocation variant that indicates that + // the value to be placed into the relocatable field should first be + // divided by 2. + RV_390_DBL + + RV_CHECK_OVERFLOW RelocVariant = 1 << 7 + RV_TYPE_MASK RelocVariant = RV_CHECK_OVERFLOW - 1 +) + +func RelocName(arch *sys.Arch, r objabi.RelocType) string { + // We didn't have some relocation types at Go1.4. + // Uncomment code when we include those in bootstrap code. + + switch { + case r >= objabi.MachoRelocOffset: // Mach-O + // nr := (r - objabi.MachoRelocOffset)>>1 + // switch ctxt.Arch.Family { + // case sys.AMD64: + // return macho.RelocTypeX86_64(nr).String() + // case sys.ARM: + // return macho.RelocTypeARM(nr).String() + // case sys.ARM64: + // return macho.RelocTypeARM64(nr).String() + // case sys.I386: + // return macho.RelocTypeGeneric(nr).String() + // default: + // panic("unreachable") + // } + case r >= objabi.ElfRelocOffset: // ELF + nr := r - objabi.ElfRelocOffset + switch arch.Family { + case sys.AMD64: + return elf.R_X86_64(nr).String() + case sys.ARM: + return elf.R_ARM(nr).String() + case sys.ARM64: + return elf.R_AARCH64(nr).String() + case sys.I386: + return elf.R_386(nr).String() + case sys.MIPS, sys.MIPS64: + return elf.R_MIPS(nr).String() + case sys.PPC64: + return elf.R_PPC64(nr).String() + case sys.S390X: + return elf.R_390(nr).String() + default: + panic("unreachable") + } + } + + return r.String() +} diff --git a/src/cmd/link/internal/sym/segment.go b/src/cmd/link/internal/sym/segment.go new file mode 100644 index 0000000..97853b9 --- /dev/null +++ b/src/cmd/link/internal/sym/segment.go @@ -0,0 +1,66 @@ +// Inferno utils/8l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package sym + +// Terrible but standard terminology. +// A segment describes a block of file to load into memory. +// A section further describes the pieces of that block for +// use in debuggers and such. + +type Segment struct { + Rwx uint8 // permission as usual unix bits (5 = r-x etc) + Vaddr uint64 // virtual address + Length uint64 // length in memory + Fileoff uint64 // file offset + Filelen uint64 // length on disk + Sections []*Section +} + +type Section struct { + Rwx uint8 + Extnum int16 + Align int32 + Name string + Vaddr uint64 + Length uint64 + Seg *Segment + Elfsect interface{} // an *ld.ElfShdr + Reloff uint64 + Rellen uint64 + // Relcount is the number of *host* relocations applied to this section + // (when external linking). + // Incremented atomically on multiple goroutines. + // Note: this may differ from number of Go relocations, as one Go relocation + // may turn into multiple host relocations. + Relcount uint32 + Sym LoaderSym // symbol for the section, if any + Index uint16 // each section has a unique index, used internally +} diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go new file mode 100644 index 0000000..70cf36a --- /dev/null +++ b/src/cmd/link/internal/sym/symbol.go @@ -0,0 +1,35 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sym + +import ( + "cmd/internal/obj" +) + +const ( + SymVerABI0 = 0 + SymVerABIInternal = 1 + SymVerStatic = 10 // Minimum version used by static (file-local) syms +) + +func ABIToVersion(abi obj.ABI) int { + switch abi { + case obj.ABI0: + return SymVerABI0 + case obj.ABIInternal: + return SymVerABIInternal + } + return -1 +} + +func VersionToABI(v int) (obj.ABI, bool) { + switch v { + case SymVerABI0: + return obj.ABI0, true + case SymVerABIInternal: + return obj.ABIInternal, true + } + return ^obj.ABI(0), false +} diff --git a/src/cmd/link/internal/sym/symkind.go b/src/cmd/link/internal/sym/symkind.go new file mode 100644 index 0000000..c176d5e --- /dev/null +++ b/src/cmd/link/internal/sym/symkind.go @@ -0,0 +1,178 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package sym + +// A SymKind describes the kind of memory represented by a symbol. +type SymKind uint8 + +// Defined SymKind values. +// +// TODO(rsc): Give idiomatic Go names. +//go:generate stringer -type=SymKind +const ( + Sxxx SymKind = iota + STEXT + SELFRXSECT + SMACHOPLT + + // Read-only sections. + STYPE + SSTRING + SGOSTRING + SGOFUNC + SGCBITS + SRODATA + SFUNCTAB + + SELFROSECT + + // Read-only sections with relocations. + // + // Types STYPE-SFUNCTAB above are written to the .rodata section by default. + // When linking a shared object, some conceptually "read only" types need to + // be written to by relocations and putting them in a section called + // ".rodata" interacts poorly with the system linkers. The GNU linkers + // support this situation by arranging for sections of the name + // ".data.rel.ro.XXX" to be mprotected read only by the dynamic linker after + // relocations have applied, so when the Go linker is creating a shared + // object it checks all objects of the above types and bumps any object that + // has a relocation to it to the corresponding type below, which are then + // written to sections with appropriate magic names. + STYPERELRO + SSTRINGRELRO + SGOSTRINGRELRO + SGOFUNCRELRO + SGCBITSRELRO + SRODATARELRO + SFUNCTABRELRO + + // Part of .data.rel.ro if it exists, otherwise part of .rodata. + STYPELINK + SITABLINK + SSYMTAB + SPCLNTAB + + // Writable sections. + SFirstWritable + SBUILDINFO + SELFSECT + SMACHO + SMACHOGOT + SWINDOWS + SELFGOT + SNOPTRDATA + SINITARR + SDATA + SXCOFFTOC + SBSS + SNOPTRBSS + SLIBFUZZER_EXTRA_COUNTER + STLSBSS + SXREF + SMACHOSYMSTR + SMACHOSYMTAB + SMACHOINDIRECTPLT + SMACHOINDIRECTGOT + SFILEPATH + SDYNIMPORT + SHOSTOBJ + SUNDEFEXT // Undefined symbol for resolution by external linker + + // Sections for debugging information + SDWARFSECT + // DWARF symbol types + SDWARFCUINFO + SDWARFCONST + SDWARFFCN + SDWARFABSFCN + SDWARFTYPE + SDWARFVAR + SDWARFRANGE + SDWARFLOC + SDWARFLINES + + // ABI aliases (these never appear in the output) + SABIALIAS +) + +// AbiSymKindToSymKind maps values read from object files (which are +// of type cmd/internal/objabi.SymKind) to values of type SymKind. +var AbiSymKindToSymKind = [...]SymKind{ + Sxxx, + STEXT, + SRODATA, + SNOPTRDATA, + SDATA, + SBSS, + SNOPTRBSS, + STLSBSS, + SDWARFCUINFO, + SDWARFCONST, + SDWARFFCN, + SDWARFABSFCN, + SDWARFTYPE, + SDWARFVAR, + SDWARFRANGE, + SDWARFLOC, + SDWARFLINES, + SABIALIAS, + SLIBFUZZER_EXTRA_COUNTER, +} + +// ReadOnly are the symbol kinds that form read-only sections. In some +// cases, if they will require relocations, they are transformed into +// rel-ro sections using relROMap. +var ReadOnly = []SymKind{ + STYPE, + SSTRING, + SGOSTRING, + SGOFUNC, + SGCBITS, + SRODATA, + SFUNCTAB, +} + +// RelROMap describes the transformation of read-only symbols to rel-ro +// symbols. +var RelROMap = map[SymKind]SymKind{ + STYPE: STYPERELRO, + SSTRING: SSTRINGRELRO, + SGOSTRING: SGOSTRINGRELRO, + SGOFUNC: SGOFUNCRELRO, + SGCBITS: SGCBITSRELRO, + SRODATA: SRODATARELRO, + SFUNCTAB: SFUNCTABRELRO, +} + +// IsData returns true if the type is a data type. +func (t SymKind) IsData() bool { + return t == SDATA || t == SNOPTRDATA || t == SBSS || t == SNOPTRBSS +} diff --git a/src/cmd/link/internal/sym/symkind_string.go b/src/cmd/link/internal/sym/symkind_string.go new file mode 100644 index 0000000..34cb314 --- /dev/null +++ b/src/cmd/link/internal/sym/symkind_string.go @@ -0,0 +1,80 @@ +// Code generated by "stringer -type=SymKind"; DO NOT EDIT. + +package sym + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Sxxx-0] + _ = x[STEXT-1] + _ = x[SELFRXSECT-2] + _ = x[SMACHOPLT-3] + _ = x[STYPE-4] + _ = x[SSTRING-5] + _ = x[SGOSTRING-6] + _ = x[SGOFUNC-7] + _ = x[SGCBITS-8] + _ = x[SRODATA-9] + _ = x[SFUNCTAB-10] + _ = x[SELFROSECT-11] + _ = x[STYPERELRO-12] + _ = x[SSTRINGRELRO-13] + _ = x[SGOSTRINGRELRO-14] + _ = x[SGOFUNCRELRO-15] + _ = x[SGCBITSRELRO-16] + _ = x[SRODATARELRO-17] + _ = x[SFUNCTABRELRO-18] + _ = x[STYPELINK-19] + _ = x[SITABLINK-20] + _ = x[SSYMTAB-21] + _ = x[SPCLNTAB-22] + _ = x[SFirstWritable-23] + _ = x[SBUILDINFO-24] + _ = x[SELFSECT-25] + _ = x[SMACHO-26] + _ = x[SMACHOGOT-27] + _ = x[SWINDOWS-28] + _ = x[SELFGOT-29] + _ = x[SNOPTRDATA-30] + _ = x[SINITARR-31] + _ = x[SDATA-32] + _ = x[SXCOFFTOC-33] + _ = x[SBSS-34] + _ = x[SNOPTRBSS-35] + _ = x[SLIBFUZZER_EXTRA_COUNTER-36] + _ = x[STLSBSS-37] + _ = x[SXREF-38] + _ = x[SMACHOSYMSTR-39] + _ = x[SMACHOSYMTAB-40] + _ = x[SMACHOINDIRECTPLT-41] + _ = x[SMACHOINDIRECTGOT-42] + _ = x[SFILEPATH-43] + _ = x[SDYNIMPORT-44] + _ = x[SHOSTOBJ-45] + _ = x[SUNDEFEXT-46] + _ = x[SDWARFSECT-47] + _ = x[SDWARFCUINFO-48] + _ = x[SDWARFCONST-49] + _ = x[SDWARFFCN-50] + _ = x[SDWARFABSFCN-51] + _ = x[SDWARFTYPE-52] + _ = x[SDWARFVAR-53] + _ = x[SDWARFRANGE-54] + _ = x[SDWARFLOC-55] + _ = x[SDWARFLINES-56] + _ = x[SABIALIAS-57] +} + +const _SymKind_name = "SxxxSTEXTSELFRXSECTSMACHOPLTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_EXTRA_COUNTERSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSABIALIAS" + +var _SymKind_index = [...]uint16{0, 4, 9, 19, 28, 33, 40, 49, 56, 63, 70, 78, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 337, 344, 349, 361, 373, 390, 407, 416, 426, 434, 443, 453, 465, 476, 485, 497, 507, 516, 527, 536, 547, 556} + +func (i SymKind) String() string { + if i >= SymKind(len(_SymKind_index)-1) { + return "SymKind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _SymKind_name[_SymKind_index[i]:_SymKind_index[i+1]] +} diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go new file mode 100644 index 0000000..31851fb --- /dev/null +++ b/src/cmd/link/internal/wasm/asm.go @@ -0,0 +1,605 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package wasm + +import ( + "bytes" + "cmd/internal/objabi" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "io" + "regexp" +) + +const ( + I32 = 0x7F + I64 = 0x7E + F32 = 0x7D + F64 = 0x7C +) + +const ( + sectionCustom = 0 + sectionType = 1 + sectionImport = 2 + sectionFunction = 3 + sectionTable = 4 + sectionMemory = 5 + sectionGlobal = 6 + sectionExport = 7 + sectionStart = 8 + sectionElement = 9 + sectionCode = 10 + sectionData = 11 +) + +// funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly +const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { +} + +type wasmFunc struct { + Name string + Type uint32 + Code []byte +} + +type wasmFuncType struct { + Params []byte + Results []byte +} + +var wasmFuncTypes = map[string]*wasmFuncType{ + "_rt0_wasm_js": {Params: []byte{}}, // + "wasm_export_run": {Params: []byte{I32, I32}}, // argc, argv + "wasm_export_resume": {Params: []byte{}}, // + "wasm_export_getsp": {Results: []byte{I32}}, // sp + "wasm_pc_f_loop": {Params: []byte{}}, // + "runtime.wasmMove": {Params: []byte{I32, I32, I32}}, // dst, src, len + "runtime.wasmZero": {Params: []byte{I32, I32}}, // ptr, len + "runtime.wasmDiv": {Params: []byte{I64, I64}, Results: []byte{I64}}, // x, y -> x/y + "runtime.wasmTruncS": {Params: []byte{F64}, Results: []byte{I64}}, // x -> int(x) + "runtime.wasmTruncU": {Params: []byte{F64}, Results: []byte{I64}}, // x -> uint(x) + "runtime.gcWriteBarrier": {Params: []byte{I64, I64}}, // ptr, val + "cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1 + "memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1 + "memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0 + "memchr": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // s, c, len -> index +} + +func assignAddress(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) { + // WebAssembly functions do not live in the same address space as the linear memory. + // Instead, WebAssembly automatically assigns indices. Imported functions (section "import") + // have indices 0 to n. They are followed by native functions (sections "function" and "code") + // with indices n+1 and following. + // + // The following rules describe how wasm handles function indices and addresses: + // PC_F = funcValueOffset + WebAssembly function index (not including the imports) + // s.Value = PC = PC_F<<16 + PC_B + // + // The funcValueOffset is necessary to avoid conflicts with expectations + // that the Go runtime has about function addresses. + // The field "s.Value" corresponds to the concept of PC at runtime. + // However, there is no PC register, only PC_F and PC_B. PC_F denotes the function, + // PC_B the resume point inside of that function. The entry of the function has PC_B = 0. + ldr.SetSymSect(s, sect) + ldr.SetSymValue(s, int64(funcValueOffset+va/ld.MINFUNC)<<16) // va starts at zero + va += uint64(ld.MINFUNC) + return sect, n, va +} + +type wasmDataSect struct { + sect *sym.Section + data []byte +} + +var dataSects []wasmDataSect + +func asmb(ctxt *ld.Link, ldr *loader.Loader) { + sections := []*sym.Section{ + ldr.SymSect(ldr.Lookup("runtime.rodata", 0)), + ldr.SymSect(ldr.Lookup("runtime.typelink", 0)), + ldr.SymSect(ldr.Lookup("runtime.itablink", 0)), + ldr.SymSect(ldr.Lookup("runtime.symtab", 0)), + ldr.SymSect(ldr.Lookup("runtime.pclntab", 0)), + ldr.SymSect(ldr.Lookup("runtime.noptrdata", 0)), + ldr.SymSect(ldr.Lookup("runtime.data", 0)), + } + + dataSects = make([]wasmDataSect, len(sections)) + for i, sect := range sections { + data := ld.DatblkBytes(ctxt, int64(sect.Vaddr), int64(sect.Length)) + dataSects[i] = wasmDataSect{sect, data} + } +} + +// asmb writes the final WebAssembly module binary. +// Spec: https://webassembly.github.io/spec/core/binary/modules.html +func asmb2(ctxt *ld.Link, ldr *loader.Loader) { + types := []*wasmFuncType{ + // For normal Go functions, the single parameter is PC_B, + // the return value is + // 0 if the function returned normally or + // 1 if the stack needs to be unwound. + {Params: []byte{I32}, Results: []byte{I32}}, + } + + // collect host imports (functions that get imported from the WebAssembly host, usually JavaScript) + hostImports := []*wasmFunc{ + { + Name: "debug", + Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), + }, + } + hostImportMap := make(map[loader.Sym]int64) + for _, fn := range ctxt.Textp { + relocs := ldr.Relocs(fn) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.Type() == objabi.R_WASMIMPORT { + hostImportMap[r.Sym()] = int64(len(hostImports)) + hostImports = append(hostImports, &wasmFunc{ + Name: ldr.SymName(r.Sym()), + Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), + }) + } + } + } + + // collect functions with WebAssembly body + var buildid []byte + fns := make([]*wasmFunc, len(ctxt.Textp)) + for i, fn := range ctxt.Textp { + wfn := new(bytes.Buffer) + if ldr.SymName(fn) == "go.buildid" { + writeUleb128(wfn, 0) // number of sets of locals + writeI32Const(wfn, 0) + wfn.WriteByte(0x0b) // end + buildid = ldr.Data(fn) + } else { + // Relocations have variable length, handle them here. + relocs := ldr.Relocs(fn) + P := ldr.Data(fn) + off := int32(0) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.Siz() == 0 { + continue // skip marker relocations + } + wfn.Write(P[off:r.Off()]) + off = r.Off() + rs := ldr.ResolveABIAlias(r.Sym()) + switch r.Type() { + case objabi.R_ADDR: + writeSleb128(wfn, ldr.SymValue(rs)+r.Add()) + case objabi.R_CALL: + writeSleb128(wfn, int64(len(hostImports))+ldr.SymValue(rs)>>16-funcValueOffset) + case objabi.R_WASMIMPORT: + writeSleb128(wfn, hostImportMap[rs]) + default: + ldr.Errorf(fn, "bad reloc type %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type())) + continue + } + } + wfn.Write(P[off:]) + } + + typ := uint32(0) + if sig, ok := wasmFuncTypes[ldr.SymName(fn)]; ok { + typ = lookupType(sig, &types) + } + + name := nameRegexp.ReplaceAllString(ldr.SymName(fn), "_") + fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()} + } + + ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic + ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version + + // Add any buildid early in the binary: + if len(buildid) != 0 { + writeBuildID(ctxt, buildid) + } + + writeTypeSec(ctxt, types) + writeImportSec(ctxt, hostImports) + writeFunctionSec(ctxt, fns) + writeTableSec(ctxt, fns) + writeMemorySec(ctxt, ldr) + writeGlobalSec(ctxt) + writeExportSec(ctxt, ldr, len(hostImports)) + writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns))) + writeCodeSec(ctxt, fns) + writeDataSec(ctxt) + writeProducerSec(ctxt) + if !*ld.FlagS { + writeNameSec(ctxt, len(hostImports), fns) + } +} + +func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 { + for i, t := range *types { + if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) { + return uint32(i) + } + } + *types = append(*types, sig) + return uint32(len(*types) - 1) +} + +func writeSecHeader(ctxt *ld.Link, id uint8) int64 { + ctxt.Out.WriteByte(id) + sizeOffset := ctxt.Out.Offset() + ctxt.Out.Write(make([]byte, 5)) // placeholder for length + return sizeOffset +} + +func writeSecSize(ctxt *ld.Link, sizeOffset int64) { + endOffset := ctxt.Out.Offset() + ctxt.Out.SeekSet(sizeOffset) + writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5) + ctxt.Out.SeekSet(endOffset) +} + +func writeBuildID(ctxt *ld.Link, buildid []byte) { + sizeOffset := writeSecHeader(ctxt, sectionCustom) + writeName(ctxt.Out, "go.buildid") + ctxt.Out.Write(buildid) + writeSecSize(ctxt, sizeOffset) +} + +// writeTypeSec writes the section that declares all function types +// so they can be referenced by index. +func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) { + sizeOffset := writeSecHeader(ctxt, sectionType) + + writeUleb128(ctxt.Out, uint64(len(types))) + + for _, t := range types { + ctxt.Out.WriteByte(0x60) // functype + writeUleb128(ctxt.Out, uint64(len(t.Params))) + for _, v := range t.Params { + ctxt.Out.WriteByte(byte(v)) + } + writeUleb128(ctxt.Out, uint64(len(t.Results))) + for _, v := range t.Results { + ctxt.Out.WriteByte(byte(v)) + } + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeImportSec writes the section that lists the functions that get +// imported from the WebAssembly host, usually JavaScript. +func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionImport) + + writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports + for _, fn := range hostImports { + writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js + writeName(ctxt.Out, fn.Name) + ctxt.Out.WriteByte(0x00) // func import + writeUleb128(ctxt.Out, uint64(fn.Type)) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeFunctionSec writes the section that declares the types of functions. +// The bodies of these functions will later be provided in the "code" section. +func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionFunction) + + writeUleb128(ctxt.Out, uint64(len(fns))) + for _, fn := range fns { + writeUleb128(ctxt.Out, uint64(fn.Type)) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeTableSec writes the section that declares tables. Currently there is only a single table +// that is used by the CallIndirect operation to dynamically call any function. +// The contents of the table get initialized by the "element" section. +func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionTable) + + numElements := uint64(funcValueOffset + len(fns)) + writeUleb128(ctxt.Out, 1) // number of tables + ctxt.Out.WriteByte(0x70) // type: anyfunc + ctxt.Out.WriteByte(0x00) // no max + writeUleb128(ctxt.Out, numElements) // min + + writeSecSize(ctxt, sizeOffset) +} + +// writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used. +// Linear memory always starts at address zero. More memory can be requested with the GrowMemory instruction. +func writeMemorySec(ctxt *ld.Link, ldr *loader.Loader) { + sizeOffset := writeSecHeader(ctxt, sectionMemory) + + dataSection := ldr.SymSect(ldr.Lookup("runtime.data", 0)) + dataEnd := dataSection.Vaddr + dataSection.Length + var initialSize = dataEnd + 16<<20 // 16MB, enough for runtime init without growing + + const wasmPageSize = 64 << 10 // 64KB + + writeUleb128(ctxt.Out, 1) // number of memories + ctxt.Out.WriteByte(0x00) // no maximum memory size + writeUleb128(ctxt.Out, initialSize/wasmPageSize) // minimum (initial) memory size + + writeSecSize(ctxt, sizeOffset) +} + +// writeGlobalSec writes the section that declares global variables. +func writeGlobalSec(ctxt *ld.Link) { + sizeOffset := writeSecHeader(ctxt, sectionGlobal) + + globalRegs := []byte{ + I32, // 0: SP + I64, // 1: CTXT + I64, // 2: g + I64, // 3: RET0 + I64, // 4: RET1 + I64, // 5: RET2 + I64, // 6: RET3 + I32, // 7: PAUSE + } + + writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals + + for _, typ := range globalRegs { + ctxt.Out.WriteByte(typ) + ctxt.Out.WriteByte(0x01) // var + switch typ { + case I32: + writeI32Const(ctxt.Out, 0) + case I64: + writeI64Const(ctxt.Out, 0) + } + ctxt.Out.WriteByte(0x0b) // end + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeExportSec writes the section that declares exports. +// Exports can be accessed by the WebAssembly host, usually JavaScript. +// The wasm_export_* functions and the linear memory get exported. +func writeExportSec(ctxt *ld.Link, ldr *loader.Loader, lenHostImports int) { + sizeOffset := writeSecHeader(ctxt, sectionExport) + + writeUleb128(ctxt.Out, 4) // number of exports + + for _, name := range []string{"run", "resume", "getsp"} { + s := ldr.Lookup("wasm_export_"+name, 0) + idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset + writeName(ctxt.Out, name) // inst.exports.run/resume/getsp in wasm_exec.js + ctxt.Out.WriteByte(0x00) // func export + writeUleb128(ctxt.Out, uint64(idx)) // funcidx + } + + writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js + ctxt.Out.WriteByte(0x02) // mem export + writeUleb128(ctxt.Out, 0) // memidx + + writeSecSize(ctxt, sizeOffset) +} + +// writeElementSec writes the section that initializes the tables declared by the "table" section. +// The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value) +// maps linearly to the function index (numImports + PC_F). +func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) { + sizeOffset := writeSecHeader(ctxt, sectionElement) + + writeUleb128(ctxt.Out, 1) // number of element segments + + writeUleb128(ctxt.Out, 0) // tableidx + writeI32Const(ctxt.Out, funcValueOffset) + ctxt.Out.WriteByte(0x0b) // end + + writeUleb128(ctxt.Out, numFns) // number of entries + for i := uint64(0); i < numFns; i++ { + writeUleb128(ctxt.Out, numImports+i) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeElementSec writes the section that provides the function bodies for the functions +// declared by the "func" section. +func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionCode) + + writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries + for _, fn := range fns { + writeUleb128(ctxt.Out, uint64(len(fn.Code))) + ctxt.Out.Write(fn.Code) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeDataSec writes the section that provides data that will be used to initialize the linear memory. +func writeDataSec(ctxt *ld.Link) { + sizeOffset := writeSecHeader(ctxt, sectionData) + + type dataSegment struct { + offset int32 + data []byte + } + + // Omit blocks of zeroes and instead emit data segments with offsets skipping the zeroes. + // This reduces the size of the WebAssembly binary. We use 8 bytes as an estimate for the + // overhead of adding a new segment (same as wasm-opt's memory-packing optimization uses). + const segmentOverhead = 8 + + // Generate at most this many segments. A higher number of segments gets rejected by some WebAssembly runtimes. + const maxNumSegments = 100000 + + var segments []*dataSegment + for secIndex, ds := range dataSects { + data := ds.data + offset := int32(ds.sect.Vaddr) + + // skip leading zeroes + for len(data) > 0 && data[0] == 0 { + data = data[1:] + offset++ + } + + for len(data) > 0 { + dataLen := int32(len(data)) + var segmentEnd, zeroEnd int32 + if len(segments)+(len(dataSects)-secIndex) == maxNumSegments { + segmentEnd = dataLen + zeroEnd = dataLen + } else { + for { + // look for beginning of zeroes + for segmentEnd < dataLen && data[segmentEnd] != 0 { + segmentEnd++ + } + // look for end of zeroes + zeroEnd = segmentEnd + for zeroEnd < dataLen && data[zeroEnd] == 0 { + zeroEnd++ + } + // emit segment if omitting zeroes reduces the output size + if zeroEnd-segmentEnd >= segmentOverhead || zeroEnd == dataLen { + break + } + segmentEnd = zeroEnd + } + } + + segments = append(segments, &dataSegment{ + offset: offset, + data: data[:segmentEnd], + }) + data = data[zeroEnd:] + offset += zeroEnd + } + } + + writeUleb128(ctxt.Out, uint64(len(segments))) // number of data entries + for _, seg := range segments { + writeUleb128(ctxt.Out, 0) // memidx + writeI32Const(ctxt.Out, seg.offset) + ctxt.Out.WriteByte(0x0b) // end + writeUleb128(ctxt.Out, uint64(len(seg.data))) + ctxt.Out.Write(seg.data) + } + + writeSecSize(ctxt, sizeOffset) +} + +// writeProducerSec writes an optional section that reports the source language and compiler version. +func writeProducerSec(ctxt *ld.Link) { + sizeOffset := writeSecHeader(ctxt, sectionCustom) + writeName(ctxt.Out, "producers") + + writeUleb128(ctxt.Out, 2) // number of fields + + writeName(ctxt.Out, "language") // field name + writeUleb128(ctxt.Out, 1) // number of values + writeName(ctxt.Out, "Go") // value: name + writeName(ctxt.Out, objabi.Version) // value: version + + writeName(ctxt.Out, "processed-by") // field name + writeUleb128(ctxt.Out, 1) // number of values + writeName(ctxt.Out, "Go cmd/compile") // value: name + writeName(ctxt.Out, objabi.Version) // value: version + + writeSecSize(ctxt, sizeOffset) +} + +var nameRegexp = regexp.MustCompile(`[^\w\.]`) + +// writeNameSec writes an optional section that assigns names to the functions declared by the "func" section. +// The names are only used by WebAssembly stack traces, debuggers and decompilers. +// TODO(neelance): add symbol table of DATA symbols +func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) { + sizeOffset := writeSecHeader(ctxt, sectionCustom) + writeName(ctxt.Out, "name") + + sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names + writeUleb128(ctxt.Out, uint64(len(fns))) + for i, fn := range fns { + writeUleb128(ctxt.Out, uint64(firstFnIndex+i)) + writeName(ctxt.Out, fn.Name) + } + writeSecSize(ctxt, sizeOffset2) + + writeSecSize(ctxt, sizeOffset) +} + +type nameWriter interface { + io.ByteWriter + io.Writer +} + +func writeI32Const(w io.ByteWriter, v int32) { + w.WriteByte(0x41) // i32.const + writeSleb128(w, int64(v)) +} + +func writeI64Const(w io.ByteWriter, v int64) { + w.WriteByte(0x42) // i64.const + writeSleb128(w, v) +} + +func writeName(w nameWriter, name string) { + writeUleb128(w, uint64(len(name))) + w.Write([]byte(name)) +} + +func writeUleb128(w io.ByteWriter, v uint64) { + if v < 128 { + w.WriteByte(uint8(v)) + return + } + more := true + for more { + c := uint8(v & 0x7f) + v >>= 7 + more = v != 0 + if more { + c |= 0x80 + } + w.WriteByte(c) + } +} + +func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) { + for i := 0; i < length; i++ { + c := uint8(v & 0x7f) + v >>= 7 + if i < length-1 { + c |= 0x80 + } + w.WriteByte(c) + } + if v != 0 { + panic("writeUleb128FixedLength: length too small") + } +} + +func writeSleb128(w io.ByteWriter, v int64) { + more := true + for more { + c := uint8(v & 0x7f) + s := uint8(v & 0x40) + v >>= 7 + more = !((v == 0 && s == 0) || (v == -1 && s != 0)) + if more { + c |= 0x80 + } + w.WriteByte(c) + } +} diff --git a/src/cmd/link/internal/wasm/obj.go b/src/cmd/link/internal/wasm/obj.go new file mode 100644 index 0000000..f8090a3 --- /dev/null +++ b/src/cmd/link/internal/wasm/obj.go @@ -0,0 +1,35 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package wasm + +import ( + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + theArch := ld.Arch{ + Funcalign: 16, + Maxalign: 32, + Minalign: 1, + + Archinit: archinit, + AssignAddress: assignAddress, + Asmb: asmb, + Asmb2: asmb2, + Gentext: gentext, + } + + return sys.ArchWasm, theArch +} + +func archinit(ctxt *ld.Link) { + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0 + } +} diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go new file mode 100644 index 0000000..af0ce11 --- /dev/null +++ b/src/cmd/link/internal/x86/asm.go @@ -0,0 +1,492 @@ +// Inferno utils/8l/asm.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "debug/elf" + "log" +) + +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + if ctxt.DynlinkingGo() { + // We need get_pc_thunk. + } else { + switch ctxt.BuildMode { + case ld.BuildModeCArchive: + if !ctxt.IsELF { + return + } + case ld.BuildModePIE, ld.BuildModeCShared, ld.BuildModePlugin: + // We need get_pc_thunk. + default: + return + } + } + + // Generate little thunks that load the PC of the next instruction into a register. + thunks := make([]loader.Sym, 0, 7+len(ctxt.Textp)) + for _, r := range [...]struct { + name string + num uint8 + }{ + {"ax", 0}, + {"cx", 1}, + {"dx", 2}, + {"bx", 3}, + // sp + {"bp", 5}, + {"si", 6}, + {"di", 7}, + } { + thunkfunc := ldr.CreateSymForUpdate("__x86.get_pc_thunk."+r.name, 0) + thunkfunc.SetType(sym.STEXT) + ldr.SetAttrLocal(thunkfunc.Sym(), true) + o := func(op ...uint8) { + for _, op1 := range op { + thunkfunc.AddUint8(op1) + } + } + // 8b 04 24 mov (%esp),%eax + // Destination register is in bits 3-5 of the middle byte, so add that in. + o(0x8b, 0x04+r.num<<3, 0x24) + // c3 ret + o(0xc3) + + thunks = append(thunks, thunkfunc.Sym()) + } + ctxt.Textp = append(thunks, ctxt.Textp...) // keep Textp in dependency order + + initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt) + if initfunc == nil { + return + } + + o := func(op ...uint8) { + for _, op1 := range op { + initfunc.AddUint8(op1) + } + } + + // go.link.addmoduledata: + // 53 push %ebx + // e8 00 00 00 00 call __x86.get_pc_thunk.cx + R_CALL __x86.get_pc_thunk.cx + // 8d 81 00 00 00 00 lea 0x0(%ecx), %eax + R_PCREL ctxt.Moduledata + // 8d 99 00 00 00 00 lea 0x0(%ecx), %ebx + R_GOTPC _GLOBAL_OFFSET_TABLE_ + // e8 00 00 00 00 call runtime.addmoduledata@plt + R_CALL runtime.addmoduledata + // 5b pop %ebx + // c3 ret + + o(0x53) + + o(0xe8) + initfunc.AddSymRef(ctxt.Arch, ldr.Lookup("__x86.get_pc_thunk.cx", 0), 0, objabi.R_CALL, 4) + + o(0x8d, 0x81) + initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 6) + + o(0x8d, 0x99) + gotsym := ldr.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0) + initfunc.AddSymRef(ctxt.Arch, gotsym, 12, objabi.R_PCREL, 4) + o(0xe8) + initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_CALL, 4) + + o(0x5b) + + o(0xc3) +} + +func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch r.Type() { + default: + if r.Type() >= objabi.ElfRelocOffset { + ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) + return false + } + + // Handle relocations found in ELF object files. + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PC32): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", ldr.SymName(targ)) + } + // TODO(mwhudson): the test of VisibilityHidden here probably doesn't make + // sense and should be removed when someone has thought about it properly. + if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) { + ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PLT32): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocAdd(rIdx, r.Add()+4) + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) + } + + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32), + objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32X): + su := ldr.MakeSymbolUpdater(s) + if targType != sym.SDYNIMPORT { + // have symbol + sData := ldr.Data(s) + + if r.Off() >= 2 && sData[r.Off()-2] == 0x8b { + su.MakeWritable() + + // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT. + writeableData := su.Data() + writeableData[r.Off()-2] = 0x8d + su.SetRelocType(rIdx, objabi.R_GOTOFF) + return true + } + + if r.Off() >= 2 && sData[r.Off()-2] == 0xff && sData[r.Off()-1] == 0xb3 { + su.MakeWritable() + // turn PUSHL of GOT entry into PUSHL of symbol itself. + // use unnecessary SS prefix to keep instruction same length. + writeableData := su.Data() + writeableData[r.Off()-2] = 0x36 + writeableData[r.Off()-1] = 0x68 + su.SetRelocType(rIdx, objabi.R_ADDR) + return true + } + + ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) + return false + } + + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT)) + su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym + su.SetRelocSym(rIdx, 0) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTOFF): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_GOTOFF) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC): + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_PCREL) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+4) + return true + + case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_32): + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected R_386_32 relocation for dynamic symbol %s", ldr.SymName(targ)) + } + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + return true + + case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0: + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_ADDR) + if targType == sym.SDYNIMPORT { + ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ)) + } + return true + + case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1: + su := ldr.MakeSymbolUpdater(s) + if targType == sym.SDYNIMPORT { + addpltsym(target, ldr, syms, targ) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) + su.SetRelocType(rIdx, objabi.R_PCREL) + return true + } + + su.SetRelocType(rIdx, objabi.R_PCREL) + return true + + case objabi.MachoRelocOffset + ld.MACHO_FAKE_GOTPCREL: + su := ldr.MakeSymbolUpdater(s) + if targType != sym.SDYNIMPORT { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + sData := ldr.Data(s) + if r.Off() < 2 || sData[r.Off()-2] != 0x8b { + ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) + return false + } + + su.MakeWritable() + writeableData := su.Data() + writeableData[r.Off()-2] = 0x8d + su.SetRelocType(rIdx, objabi.R_PCREL) + return true + } + + ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT)) + su.SetRelocSym(rIdx, syms.GOT) + su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) + su.SetRelocType(rIdx, objabi.R_PCREL) + return true + } + + // Handle references to ELF symbols from our own object files. + if targType != sym.SDYNIMPORT { + return true + } + + // Reread the reloc to incorporate any changes in type above. + relocs := ldr.Relocs(s) + r = relocs.At(rIdx) + + switch r.Type() { + case objabi.R_CALL, + objabi.R_PCREL: + if target.IsExternal() { + // External linker will do this relocation. + return true + } + addpltsym(target, ldr, syms, targ) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocSym(rIdx, syms.PLT) + su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) + return true + + case objabi.R_ADDR: + if ldr.SymType(s) != sym.SDATA { + break + } + if target.IsElf() { + ld.Adddynsym(ldr, target, syms, targ) + rel := ldr.MakeSymbolUpdater(syms.Rel) + rel.AddAddrPlus(target.Arch, s, int64(r.Off())) + rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(targ)), uint32(elf.R_386_32))) + su := ldr.MakeSymbolUpdater(s) + su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym + su.SetRelocSym(rIdx, 0) + return true + } + } + + return false +} + +func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { + out.Write32(uint32(sectoff)) + + elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) + siz := r.Size + switch r.Type { + default: + return false + case objabi.R_ADDR, objabi.R_DWARFSECREF: + if siz == 4 { + out.Write32(uint32(elf.R_386_32) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_GOTPCREL: + if siz == 4 { + out.Write32(uint32(elf.R_386_GOTPC)) + if ldr.SymName(r.Xsym) != "_GLOBAL_OFFSET_TABLE_" { + out.Write32(uint32(sectoff)) + out.Write32(uint32(elf.R_386_GOT32) | uint32(elfsym)<<8) + } + } else { + return false + } + case objabi.R_CALL: + if siz == 4 { + if ldr.SymType(r.Xsym) == sym.SDYNIMPORT { + out.Write32(uint32(elf.R_386_PLT32) | uint32(elfsym)<<8) + } else { + out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8) + } + } else { + return false + } + case objabi.R_PCREL: + if siz == 4 { + out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_TLS_LE: + if siz == 4 { + out.Write32(uint32(elf.R_386_TLS_LE) | uint32(elfsym)<<8) + } else { + return false + } + case objabi.R_TLS_IE: + if siz == 4 { + out.Write32(uint32(elf.R_386_GOTPC)) + out.Write32(uint32(sectoff)) + out.Write32(uint32(elf.R_386_TLS_GOTIE) | uint32(elfsym)<<8) + } else { + return false + } + } + + return true +} + +func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { + return false +} + +func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + rt := r.Type + + if ldr.SymDynid(rs) < 0 { + ldr.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs)) + return false + } + + out.Write32(uint32(sectoff)) + out.Write32(uint32(ldr.SymDynid(rs))) + + switch rt { + default: + return false + + case objabi.R_DWARFSECREF: + v = ld.IMAGE_REL_I386_SECREL + + case objabi.R_ADDR: + v = ld.IMAGE_REL_I386_DIR32 + + case objabi.R_CALL, + objabi.R_PCREL: + v = ld.IMAGE_REL_I386_REL32 + } + + out.Write16(uint16(v)) + + return true +} + +func archreloc(*ld.Target, *loader.Loader, *ld.ArchSyms, loader.Reloc, loader.Sym, int64) (int64, int, bool) { + return -1, 0, false +} + +func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 { + log.Fatalf("unexpected relocation variant") + return -1 +} + +func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { + if plt.Size() == 0 { + // pushl got+4 + plt.AddUint8(0xff) + + plt.AddUint8(0x35) + plt.AddAddrPlus(ctxt.Arch, got.Sym(), 4) + + // jmp *got+8 + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddAddrPlus(ctxt.Arch, got.Sym(), 8) + + // zero pad + plt.AddUint32(ctxt.Arch, 0) + + // assume got->size == 0 too + got.AddAddrPlus(ctxt.Arch, dynamic, 0) + + got.AddUint32(ctxt.Arch, 0) + got.AddUint32(ctxt.Arch, 0) + } +} + +func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { + if ldr.SymPlt(s) >= 0 { + return + } + + ld.Adddynsym(ldr, target, syms, s) + + if target.IsElf() { + plt := ldr.MakeSymbolUpdater(syms.PLT) + got := ldr.MakeSymbolUpdater(syms.GOTPLT) + rel := ldr.MakeSymbolUpdater(syms.RelPLT) + if plt.Size() == 0 { + panic("plt is not set up") + } + + // jmpq *got+size + plt.AddUint8(0xff) + + plt.AddUint8(0x25) + plt.AddAddrPlus(target.Arch, got.Sym(), got.Size()) + + // add to got: pointer to current pos in plt + got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size()) + + // pushl $x + plt.AddUint8(0x68) + + plt.AddUint32(target.Arch, uint32(rel.Size())) + + // jmp .plt + plt.AddUint8(0xe9) + + plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4))) + + // rel + rel.AddAddrPlus(target.Arch, got.Sym(), got.Size()-4) + + sDynid := ldr.SymDynid(s) + rel.AddUint32(target.Arch, elf.R_INFO32(uint32(sDynid), uint32(elf.R_386_JMP_SLOT))) + + ldr.SetPlt(s, int32(plt.Size()-16)) + } else { + ldr.Errorf(s, "addpltsym: unsupported binary format") + } +} diff --git a/src/cmd/link/internal/x86/l.go b/src/cmd/link/internal/x86/l.go new file mode 100644 index 0000000..5875d45 --- /dev/null +++ b/src/cmd/link/internal/x86/l.go @@ -0,0 +1,43 @@ +// Inferno utils/8l/l.h +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +const ( + maxAlign = 32 // max data alignment + minAlign = 1 // min data alignment + funcAlign = 16 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + dwarfRegSP = 4 + dwarfRegLR = 8 +) diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go new file mode 100644 index 0000000..a19437d --- /dev/null +++ b/src/cmd/link/internal/x86/obj.go @@ -0,0 +1,116 @@ +// Inferno utils/8l/obj.c +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/ld" +) + +func Init() (*sys.Arch, ld.Arch) { + arch := sys.Arch386 + + theArch := ld.Arch{ + Funcalign: funcAlign, + Maxalign: maxAlign, + Minalign: minAlign, + Dwarfregsp: dwarfRegSP, + Dwarfreglr: dwarfRegLR, + // 0xCC is INT $3 - breakpoint instruction + CodePad: []byte{0xCC}, + + Plan9Magic: uint32(4*11*11 + 7), + + Adddynrel: adddynrel, + Archinit: archinit, + Archreloc: archreloc, + Archrelocvariant: archrelocvariant, + Elfreloc1: elfreloc1, + ElfrelocSize: 8, + Elfsetupplt: elfsetupplt, + Gentext: gentext, + Machoreloc1: machoreloc1, + PEreloc1: pereloc1, + + Linuxdynld: "/lib/ld-linux.so.2", + Freebsddynld: "/usr/libexec/ld-elf.so.1", + Openbsddynld: "/usr/libexec/ld.so", + Netbsddynld: "/usr/libexec/ld.elf_so", + Solarisdynld: "/lib/ld.so.1", + } + + return arch, theArch +} + +func archinit(ctxt *ld.Link) { + switch ctxt.HeadType { + default: + ld.Exitf("unknown -H option: %v", ctxt.HeadType) + + case objabi.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4096 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hdarwin: /* apple MACH */ + ld.HEADR = ld.INITIAL_MACHO_HEADR + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 4096 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hlinux, /* elf32 executable */ + objabi.Hfreebsd, + objabi.Hnetbsd, + objabi.Hopenbsd: + ld.Elfinit(ctxt) + + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0x08048000 + int64(ld.HEADR) + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 4096 + } + + case objabi.Hwindows: /* PE executable */ + // ld.HEADR, ld.FlagTextAddr, ld.FlagRound are set in ld.Peinit + return + } +} |