cmd: merge branch 'dev.link' into master

In the dev.link branch we continued developing the new object
file format support and the linker improvements described in
https://golang.org/s/better-linker . Since the last merge, more
progress has been made to improve the new linker.

This is a clean merge.

Change-Id: I57c510b651a39354d78478a9a4499f770eef2eb1
This commit is contained in:
Cherry Zhang 2020-04-24 10:30:33 -04:00
commit a02349bc9d
52 changed files with 1811 additions and 1675 deletions

View File

@ -17,6 +17,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
)
@ -150,6 +151,14 @@ func buildGoobj() error {
return nil
}
// Check that a symbol has a given name, accepting both
// new and old objects.
// TODO(go115newobj): remove.
func matchSymName(symname, want string) bool {
return symname == want ||
strings.HasPrefix(symname, want+"#") // new style, with index
}
func TestParseGoobj(t *testing.T) {
path := go1obj
@ -168,7 +177,7 @@ func TestParseGoobj(t *testing.T) {
}
var found bool
for _, s := range p.Syms {
if s.Name == "mypkg.go1" {
if matchSymName(s.Name, "mypkg.go1") {
found = true
break
}
@ -197,10 +206,10 @@ func TestParseArchive(t *testing.T) {
var found1 bool
var found2 bool
for _, s := range p.Syms {
if s.Name == "mypkg.go1" {
if matchSymName(s.Name, "mypkg.go1") {
found1 = true
}
if s.Name == "mypkg.go2" {
if matchSymName(s.Name, "mypkg.go2") {
found2 = true
}
}
@ -233,10 +242,10 @@ func TestParseCGOArchive(t *testing.T) {
var found1 bool
var found2 bool
for _, s := range p.Syms {
if s.Name == "mycgo.go1" {
if matchSymName(s.Name, "mycgo.go1") {
found1 = true
}
if s.Name == "mycgo.go2" {
if matchSymName(s.Name, "mycgo.go2") {
found2 = true
}
}

View File

@ -54,8 +54,10 @@ func (r *objReader) readNew() {
case goobj2.PkgIdxSelf:
i = int(s.SymIdx)
default:
// Symbol from other package, referenced by index.
// We don't know the name. Use index.
pkg := pkglist[p]
return SymID{fmt.Sprintf("%s.<#%d>", pkg, s.SymIdx), 0}
return SymID{fmt.Sprintf("%s.#%d", pkg, s.SymIdx), 0}
}
sym := rr.Sym(i)
return SymID{sym.Name(rr), abiToVer(sym.ABI())}
@ -66,6 +68,7 @@ func (r *objReader) readNew() {
// Symbols
pcdataBase := start + rr.PcdataBase()
n := rr.NSym() + rr.NNonpkgdef() + rr.NNonpkgref()
npkgdef := rr.NSym()
ndef := rr.NSym() + rr.NNonpkgdef()
for i := 0; i < n; i++ {
osym := rr.Sym(i)
@ -76,6 +79,10 @@ func (r *objReader) readNew() {
// prefix for the package in which the object file has been found.
// Expand it.
name := strings.ReplaceAll(osym.Name(rr), `"".`, r.pkgprefix)
if i < npkgdef {
// Indexed symbol. Attach index to the name.
name += fmt.Sprintf("#%d", i)
}
symID := SymID{Name: name, Version: abiToVer(osym.ABI())}
r.p.SymRefs = append(r.p.SymRefs, symID)

View File

@ -10,6 +10,7 @@ var builtins = [...]struct {
{"runtime.panicdivide", 1},
{"runtime.panicshift", 1},
{"runtime.panicmakeslicelen", 1},
{"runtime.panicmakeslicecap", 1},
{"runtime.throwinit", 1},
{"runtime.panicwrap", 1},
{"runtime.gopanic", 1},
@ -180,14 +181,26 @@ var builtins = [...]struct {
{"runtime.msanwrite", 1},
{"runtime.checkptrAlignment", 1},
{"runtime.checkptrArithmetic", 1},
{"runtime.libfuzzerTraceCmp1", 1},
{"runtime.libfuzzerTraceCmp2", 1},
{"runtime.libfuzzerTraceCmp4", 1},
{"runtime.libfuzzerTraceCmp8", 1},
{"runtime.libfuzzerTraceConstCmp1", 1},
{"runtime.libfuzzerTraceConstCmp2", 1},
{"runtime.libfuzzerTraceConstCmp4", 1},
{"runtime.libfuzzerTraceConstCmp8", 1},
{"runtime.x86HasPOPCNT", 0},
{"runtime.x86HasSSE41", 0},
{"runtime.x86HasFMA", 0},
{"runtime.armHasVFPv4", 0},
{"runtime.arm64HasATOMICS", 0},
{"runtime.gcWriteBarrier", 0},
{"runtime.deferproc", 1},
{"runtime.deferprocStack", 1},
{"runtime.deferreturn", 1},
{"runtime.newproc", 1},
{"runtime.panicoverflow", 1},
{"runtime.sigpanic", 1},
{"runtime.gcWriteBarrier", 0},
{"runtime.morestack", 0},
{"runtime.morestackc", 0},
{"runtime.morestack_noctxt", 0},

View File

@ -113,11 +113,18 @@ var extra = [...]struct {
name string
abi int
}{
{"gcWriteBarrier", 0}, // asm function, ABI0
// compiler frontend inserted calls (sysfunc)
{"deferproc", 1},
{"deferprocStack", 1},
{"deferreturn", 1},
{"newproc", 1},
{"panicoverflow", 1},
{"sigpanic", 1},
// compiler backend inserted calls
{"gcWriteBarrier", 0}, // asm function, ABI0
// assembler backend inserted calls
{"morestack", 0}, // asm function, ABI0
{"morestackc", 0}, // asm function, ABI0
{"morestack_noctxt", 0}, // asm function, ABI0

View File

@ -756,7 +756,6 @@ func asmb2(ctxt *ld.Link) {
if ctxt.IsELF {
ctxt.Out.SeekSet(symo)
ld.Asmelfsym(ctxt)
ctxt.Out.Flush()
ctxt.Out.Write(ld.Elfstrdat)
if ctxt.LinkMode == ld.LinkExternal {
@ -766,13 +765,11 @@ func asmb2(ctxt *ld.Link) {
case objabi.Hplan9:
ld.Asmplan9sym(ctxt)
ctxt.Out.Flush()
sym := ctxt.Syms.Lookup("pclntab", 0)
if sym != nil {
ld.Lcsize = int32(len(sym.P))
ctxt.Out.Write(sym.P)
ctxt.Out.Flush()
}
case objabi.Hwindows:
@ -817,8 +814,6 @@ func asmb2(ctxt *ld.Link) {
case objabi.Hwindows:
ld.Asmbpe(ctxt)
}
ctxt.Out.Flush()
}
func tlsIEtoLE(s *sym.Symbol, off, size int) {

View File

@ -314,87 +314,7 @@ func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.S
}
func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
var v uint32
rs := r.Xsym
if r.Type == objabi.R_PCREL {
if rs.Type == sym.SHOSTOBJ {
ld.Errorf(s, "pc-relative relocation of external symbol is not supported")
return false
}
if r.Siz != 4 {
return false
}
// emit a pair of "scattered" relocations that
// resolve to the difference of section addresses of
// the symbol and the instruction
// this value is added to the field being relocated
o1 := uint32(sectoff)
o1 |= 1 << 31 // scattered bit
o1 |= ld.MACHO_ARM_RELOC_SECTDIFF << 24
o1 |= 2 << 28 // size = 4
o2 := uint32(0)
o2 |= 1 << 31 // scattered bit
o2 |= ld.MACHO_ARM_RELOC_PAIR << 24
o2 |= 2 << 28 // size = 4
out.Write32(o1)
out.Write32(uint32(ld.Symaddr(rs)))
out.Write32(o2)
out.Write32(uint32(s.Value + int64(r.Off)))
return true
}
if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALLARM {
if rs.Dynid < 0 {
ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
return false
}
v = uint32(rs.Dynid)
v |= 1 << 27 // external relocation
} else {
v = uint32(rs.Sect.Extnum)
if v == 0 {
ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type)
return false
}
}
switch r.Type {
default:
return false
case objabi.R_ADDR:
v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28
case objabi.R_CALLARM:
v |= 1 << 24 // pc-relative bit
v |= ld.MACHO_ARM_RELOC_BR24 << 28
}
switch r.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
return false
}
func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
@ -816,7 +736,6 @@ func asmb2(ctxt *ld.Link) {
default:
if ctxt.IsELF {
ld.Asmelfsym(ctxt)
ctxt.Out.Flush()
ctxt.Out.Write(ld.Elfstrdat)
if ctxt.LinkMode == ld.LinkExternal {
@ -826,13 +745,11 @@ func asmb2(ctxt *ld.Link) {
case objabi.Hplan9:
ld.Asmplan9sym(ctxt)
ctxt.Out.Flush()
sym := ctxt.Syms.Lookup("pclntab", 0)
if sym != nil {
ld.Lcsize = int32(len(sym.P))
ctxt.Out.Write(sym.P)
ctxt.Out.Flush()
}
case objabi.Hwindows:
@ -863,7 +780,6 @@ func asmb2(ctxt *ld.Link) {
ld.Asmbpe(ctxt)
}
ctxt.Out.Flush()
if *ld.FlagC {
fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)

View File

@ -866,7 +866,6 @@ func asmb2(ctxt *ld.Link) {
default:
if ctxt.IsELF {
ld.Asmelfsym(ctxt)
ctxt.Out.Flush()
ctxt.Out.Write(ld.Elfstrdat)
if ctxt.LinkMode == ld.LinkExternal {
@ -876,13 +875,11 @@ func asmb2(ctxt *ld.Link) {
case objabi.Hplan9:
ld.Asmplan9sym(ctxt)
ctxt.Out.Flush()
sym := ctxt.Syms.Lookup("pclntab", 0)
if sym != nil {
ld.Lcsize = int32(len(sym.P))
ctxt.Out.Write(sym.P)
ctxt.Out.Flush()
}
case objabi.Hdarwin:
@ -915,7 +912,6 @@ func asmb2(ctxt *ld.Link) {
ld.Asmbmacho(ctxt)
}
ctxt.Out.Flush()
if *ld.FlagC {
fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,218 @@
// Copyright 2016 The Go Authors. All rights reserved.
// 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 (
"bytes"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"container/heap"
"fmt"
"unicode"
)
var _ = fmt.Print
type workQueue []loader.Sym
// Implement container/heap.Interface.
func (q *workQueue) Len() int { return len(*q) }
func (q *workQueue) Less(i, j int) bool { return (*q)[i] < (*q)[j] }
func (q *workQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
func (q *workQueue) Push(i interface{}) { *q = append(*q, i.(loader.Sym)) }
func (q *workQueue) Pop() interface{} { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i }
// Functions for deadcode pass to use.
// Deadcode pass should call push/pop, not Push/Pop.
func (q *workQueue) push(i loader.Sym) { heap.Push(q, i) }
func (q *workQueue) pop() loader.Sym { return heap.Pop(q).(loader.Sym) }
func (q *workQueue) empty() bool { return len(*q) == 0 }
type deadcodePass struct {
ctxt *Link
ldr *loader.Loader
wq workQueue
ifaceMethod map[methodsig]bool // methods declared in reached interfaces
markableMethods []methodref2 // methods of reached types
reflectSeen bool // whether we have seen a reflect method call
}
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())
}
heap.Init(&d.wq)
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.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.At2(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() {
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)
if isgotype {
p := d.ldr.Data(symIdx)
if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface {
for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs) {
if d.ctxt.Debugvlog > 1 {
d.ctxt.Logf("reached iface method: %s\n", sig)
}
d.ifaceMethod[sig] = true
}
}
}
var methods []methodref2
for i := 0; i < relocs.Count(); i++ {
r := relocs.At2(i)
t := r.Type()
if t == objabi.R_WEAKADDROFF {
continue
}
if t == objabi.R_METHODOFF {
if i+2 >= relocs.Count() {
panic("expect three consecutive R_METHODOFF relocs")
}
methods = append(methods, methodref2{src: symIdx, r: i})
i += 2
continue
}
if t == 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
}
d.mark(r.Sym(), symIdx)
}
naux := d.ldr.NAux(symIdx)
for i := 0; i < naux; i++ {
d.mark(d.ldr.Aux2(symIdx, i).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.decodetypeMethods2(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
}
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] = parent
}
if *flagDumpDep {
to := d.ldr.SymName(symIdx)
if to != "" {
from := "_"
if parent != 0 {
from = d.ldr.SymName(parent)
}
fmt.Printf("%s -> %s\n", from, to)
}
}
}
}
func (d *deadcodePass) markMethod(m methodref2) {
relocs := d.ldr.Relocs(m.src)
d.mark(relocs.At2(m.r).Sym(), m.src)
d.mark(relocs.At2(m.r+1).Sym(), m.src)
d.mark(relocs.At2(m.r+2).Sym(), m.src)
}
// deadcode marks all reachable symbols.
//
// The basis of the dead code elimination is a flood fill of symbols,
@ -42,24 +247,173 @@ import (
//
// Any unreached text symbols are removed from ctxt.Textp.
func deadcode(ctxt *Link) {
deadcode2(ctxt)
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()
}
n := ldr.NSym()
if ctxt.BuildMode != BuildModeShared {
// Keep a itablink if the symbol it points at is being kept.
// (When BuildModeShared, always keep itablinks.)
for i := 1; i < n; i++ {
s := loader.Sym(i)
if ldr.IsItabLink(s) {
relocs := ldr.Relocs(s)
if relocs.Count() > 0 && ldr.AttrReachable(relocs.At2(0).Sym()) {
ldr.SetAttrReachable(s, true)
}
}
}
}
}
// addToTextp populates the context Textp slice (needed in various places
// in the linker).
func addToTextp(ctxt *Link) {
// Set up ctxt.Textp, based on ctxt.Textp2.
textp := make([]*sym.Symbol, 0, len(ctxt.Textp2))
haveshlibs := len(ctxt.Shlibs) > 0
for _, tsym := range ctxt.Textp2 {
sp := ctxt.loader.Syms[tsym]
if sp == nil || !ctxt.loader.AttrReachable(tsym) {
panic("should never happen")
}
if haveshlibs && sp.Type == sym.SDYNIMPORT {
continue
}
textp = append(textp, sp)
}
ctxt.Textp = textp
// methodref2 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 methodref2 struct {
m methodsig
src loader.Sym // receiver type symbol
r int // the index of R_METHODOFF relocations
}
func (m methodref2) isExported() bool {
for _, r := range m.m {
return unicode.IsUpper(r)
}
panic("methodref has no signature")
}
// decodeMethodSig2 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) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig {
var buf bytes.Buffer
var methods []methodsig
for i := 0; i < count; i++ {
buf.WriteString(decodetypeName2(ldr, symIdx, relocs, off))
mtypSym := decodeRelocSym2(ldr, symIdx, relocs, int32(off+4))
// FIXME: add some sort of caching here, since we may see some of the
// same symbols over time for param types.
mrelocs := ldr.Relocs(mtypSym)
mp := ldr.Data(mtypSym)
buf.WriteRune('(')
inCount := decodetypeFuncInCount(arch, mp)
for i := 0; i < inCount; i++ {
if i > 0 {
buf.WriteString(", ")
}
a := decodetypeFuncInType2(ldr, arch, mtypSym, &mrelocs, i)
buf.WriteString(ldr.SymName(a))
}
buf.WriteString(") (")
outCount := decodetypeFuncOutCount(arch, mp)
for i := 0; i < outCount; i++ {
if i > 0 {
buf.WriteString(", ")
}
a := decodetypeFuncOutType2(ldr, arch, mtypSym, &mrelocs, i)
buf.WriteString(ldr.SymName(a))
}
buf.WriteRune(')')
off += size
methods = append(methods, methodsig(buf.String()))
buf.Reset()
}
return methods
}
func (d *deadcodePass) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
p := ldr.Data(symIdx)
if decodetypeKind(arch, p)&kindMask != kindInterface {
panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
}
rel := decodeReloc2(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize))
s := rel.Sym()
if s == 0 {
return nil
}
if s != symIdx {
panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx)))
}
off := int(rel.Add()) // array of reflect.imethod values
numMethods := int(decodetypeIfaceMethodCount(arch, p))
sizeofIMethod := 4 + 4
return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods)
}
func (d *deadcodePass) decodetypeMethods2(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.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount)
}

View File

@ -1,385 +0,0 @@
// 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 (
"bytes"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"container/heap"
"fmt"
"unicode"
)
var _ = fmt.Print
type workQueue []loader.Sym
// Implement container/heap.Interface.
func (q *workQueue) Len() int { return len(*q) }
func (q *workQueue) Less(i, j int) bool { return (*q)[i] < (*q)[j] }
func (q *workQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
func (q *workQueue) Push(i interface{}) { *q = append(*q, i.(loader.Sym)) }
func (q *workQueue) Pop() interface{} { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i }
// Functions for deadcode pass to use.
// Deadcode pass should call push/pop, not Push/Pop.
func (q *workQueue) push(i loader.Sym) { heap.Push(q, i) }
func (q *workQueue) pop() loader.Sym { return heap.Pop(q).(loader.Sym) }
func (q *workQueue) empty() bool { return len(*q) == 0 }
type deadcodePass2 struct {
ctxt *Link
ldr *loader.Loader
wq workQueue
ifaceMethod map[methodsig]bool // methods declared in reached interfaces
markableMethods []methodref2 // methods of reached types
reflectSeen bool // whether we have seen a reflect method call
}
func (d *deadcodePass2) init() {
d.ldr.InitReachable()
d.ifaceMethod = make(map[methodsig]bool)
if d.ctxt.Reachparent != nil {
d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
}
heap.Init(&d.wq)
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.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.At2(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 *deadcodePass2) flood() {
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)
if isgotype {
p := d.ldr.Data(symIdx)
if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface {
for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs) {
if d.ctxt.Debugvlog > 1 {
d.ctxt.Logf("reached iface method: %s\n", sig)
}
d.ifaceMethod[sig] = true
}
}
}
var methods []methodref2
for i := 0; i < relocs.Count(); i++ {
r := relocs.At2(i)
t := r.Type()
if t == objabi.R_WEAKADDROFF {
continue
}
if t == objabi.R_METHODOFF {
if i+2 >= relocs.Count() {
panic("expect three consecutive R_METHODOFF relocs")
}
methods = append(methods, methodref2{src: symIdx, r: i})
i += 2
continue
}
if t == 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
}
d.mark(r.Sym(), symIdx)
}
naux := d.ldr.NAux(symIdx)
for i := 0; i < naux; i++ {
d.mark(d.ldr.Aux2(symIdx, i).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.decodetypeMethods2(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
}
d.markableMethods = append(d.markableMethods, methods...)
}
}
}
func (d *deadcodePass2) mark(symIdx, parent loader.Sym) {
if symIdx != 0 && !d.ldr.AttrReachable(symIdx) {
d.wq.push(symIdx)
d.ldr.SetAttrReachable(symIdx, true)
if d.ctxt.Reachparent != nil {
d.ldr.Reachparent[symIdx] = parent
}
if *flagDumpDep {
to := d.ldr.SymName(symIdx)
if to != "" {
from := "_"
if parent != 0 {
from = d.ldr.SymName(parent)
}
fmt.Printf("%s -> %s\n", from, to)
}
}
}
}
func (d *deadcodePass2) markMethod(m methodref2) {
relocs := d.ldr.Relocs(m.src)
d.mark(relocs.At2(m.r).Sym(), m.src)
d.mark(relocs.At2(m.r+1).Sym(), m.src)
d.mark(relocs.At2(m.r+2).Sym(), m.src)
}
func deadcode2(ctxt *Link) {
ldr := ctxt.loader
d := deadcodePass2{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()
}
n := ldr.NSym()
if ctxt.BuildMode != BuildModeShared {
// Keep a itablink if the symbol it points at is being kept.
// (When BuildModeShared, always keep itablinks.)
for i := 1; i < n; i++ {
s := loader.Sym(i)
if ldr.IsItabLink(s) {
relocs := ldr.Relocs(s)
if relocs.Count() > 0 && ldr.AttrReachable(relocs.At2(0).Sym()) {
ldr.SetAttrReachable(s, true)
}
}
}
}
}
// methodref2 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 methodref2 struct {
m methodsig
src loader.Sym // receiver type symbol
r int // the index of R_METHODOFF relocations
}
func (m methodref2) isExported() bool {
for _, r := range m.m {
return unicode.IsUpper(r)
}
panic("methodref has no signature")
}
// decodeMethodSig2 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 *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig {
var buf bytes.Buffer
var methods []methodsig
for i := 0; i < count; i++ {
buf.WriteString(decodetypeName2(ldr, symIdx, relocs, off))
mtypSym := decodeRelocSym2(ldr, symIdx, relocs, int32(off+4))
// FIXME: add some sort of caching here, since we may see some of the
// same symbols over time for param types.
mrelocs := ldr.Relocs(mtypSym)
mp := ldr.Data(mtypSym)
buf.WriteRune('(')
inCount := decodetypeFuncInCount(arch, mp)
for i := 0; i < inCount; i++ {
if i > 0 {
buf.WriteString(", ")
}
a := decodetypeFuncInType2(ldr, arch, mtypSym, &mrelocs, i)
buf.WriteString(ldr.SymName(a))
}
buf.WriteString(") (")
outCount := decodetypeFuncOutCount(arch, mp)
for i := 0; i < outCount; i++ {
if i > 0 {
buf.WriteString(", ")
}
a := decodetypeFuncOutType2(ldr, arch, mtypSym, &mrelocs, i)
buf.WriteString(ldr.SymName(a))
}
buf.WriteRune(')')
off += size
methods = append(methods, methodsig(buf.String()))
buf.Reset()
}
return methods
}
func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
p := ldr.Data(symIdx)
if decodetypeKind(arch, p)&kindMask != kindInterface {
panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
}
rel := decodeReloc2(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize))
s := rel.Sym()
if s == 0 {
return nil
}
if s != symIdx {
panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx)))
}
off := int(rel.Add()) // array of reflect.imethod values
numMethods := int(decodetypeIfaceMethodCount(arch, p))
sizeofIMethod := 4 + 4
return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods)
}
func (d *deadcodePass2) decodetypeMethods2(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.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount)
}

View File

@ -106,7 +106,7 @@ func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section {
// Type.commonType.gc
func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte {
if s.Type == sym.SDYNIMPORT {
addr := decodetypeGcprogShlib(ctxt, s)
addr := decodetypeGcprogShlib(ctxt, s.P)
sect := findShlibSection(ctxt, s.File, addr)
if sect != nil {
// A gcprog is a 4-byte uint32 indicating length, followed by
@ -123,16 +123,16 @@ func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte {
return decodeRelocSym(s, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize)).P
}
func decodetypeGcprogShlib(ctxt *Link, s *sym.Symbol) uint64 {
func decodetypeGcprogShlib(ctxt *Link, data []byte) uint64 {
if ctxt.Arch.Family == sys.ARM64 {
return 0
}
return decodeInuxi(ctxt.Arch, s.P[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize)
return decodeInuxi(ctxt.Arch, data[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize)
}
func decodetypeGcmask(ctxt *Link, s *sym.Symbol) []byte {
if s.Type == sym.SDYNIMPORT {
addr := decodetypeGcprogShlib(ctxt, s)
addr := decodetypeGcprogShlib(ctxt, s.P)
ptrdata := decodetypePtrdata(ctxt.Arch, s.P)
sect := findShlibSection(ctxt, s.File, addr)
if sect != nil {

View File

@ -7,6 +7,7 @@ package ld
import (
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
)
// This file contains utilities to decode type.* symbols, for
@ -129,3 +130,45 @@ func decodetypeStr2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) strin
}
return str
}
func decodetypeGcmask2(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 {
r := make([]byte, ptrdata/int64(ctxt.Arch.PtrSize))
sect.ReadAt(r, int64(addr-sect.Addr))
return r
}
Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s))
return nil
}
relocs := ctxt.loader.Relocs(s)
mask := decodeRelocSym2(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize))
return ctxt.loader.Data(mask)
}
// Type.commonType.gc
func decodetypeGcprog2(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)
sect.ReadAt(progsize, int64(addr-sect.Addr))
progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize))
sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
return append(progsize, progbytes...)
}
Exitf("cannot find gcmask for %s", ctxt.loader.SymName(s))
return nil
}
relocs := ctxt.loader.Relocs(s)
rs := decodeRelocSym2(ctxt.loader, s, &relocs, 2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize))
return ctxt.loader.Data(rs)
}

View File

@ -177,14 +177,46 @@ func (c dwctxt2) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets [
var gdbscript string
var dwarfp2 []loader.Sym
// 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
}
func (d *dwctxt2) writeabbrev() 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:]
}
// dwarfp2 stores the collected DWARF symbols created during
// dwarf generation.
var dwarfp2 []dwarfSecInfo
func (d *dwctxt2) writeabbrev() dwarfSecInfo {
abrvs := d.ldr.LookupOrCreateSym(".debug_abbrev", 0)
u := d.ldr.MakeSymbolUpdater(abrvs)
u.SetType(sym.SDWARFSECT)
u.AddBytes(dwarf.GetAbbrev())
return abrvs
return dwarfSecInfo{syms: []loader.Sym{abrvs}}
}
var dwtypes dwarf.DWDie
@ -1140,6 +1172,9 @@ func (d *dwctxt2) writelines(unit *sym.CompilationUnit, ls loader.Sym) {
lsu := d.ldr.MakeSymbolUpdater(ls)
newattr(unit.DWInfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, lsu.Size(), dwSym(ls))
internalExec := d.linkctxt.BuildMode == BuildModeExe && d.linkctxt.IsInternal()
addAddrPlus := loader.GenAddAddrPlusFunc(internalExec)
// Write .debug_line Line Number Program Header (sec 6.2.4)
// Fields marked with (*) must be changed for 64-bit dwarf
unitLengthOffset := lsu.Size()
@ -1209,7 +1244,7 @@ func (d *dwctxt2) writelines(unit *sym.CompilationUnit, ls loader.Sym) {
lsu.AddUint8(0)
dwarf.Uleb128put(d, lsDwsym, 1+int64(d.arch.PtrSize))
lsu.AddUint8(dwarf.DW_LNE_set_address)
addr := lsu.AddAddrPlus(d.arch, fnSym, 0)
addr := addAddrPlus(lsu, d.arch, fnSym, 0)
// Make sure the units are sorted.
if addr < lastAddr {
d.linkctxt.Errorf(fnSym, "address wasn't increasing %x < %x",
@ -1291,12 +1326,11 @@ func appendPCDeltaCFA(arch *sys.Arch, b []byte, deltapc, cfa int64) []byte {
return b
}
func (d *dwctxt2) writeframes(syms []loader.Sym) []loader.Sym {
func (d *dwctxt2) writeframes() dwarfSecInfo {
fs := d.ldr.LookupOrCreateSym(".debug_frame", 0)
fsd := dwSym(fs)
fsu := d.ldr.MakeSymbolUpdater(fs)
fsu.SetType(sym.SDWARFSECT)
syms = append(syms, fs)
isdw64 := isDwarf64(d.linkctxt)
haslr := haslinkregister(d.linkctxt)
@ -1347,6 +1381,9 @@ func (d *dwctxt2) writeframes(syms []loader.Sym) []loader.Sym {
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
@ -1428,7 +1465,7 @@ func (d *dwctxt2) writeframes(syms []loader.Sym) []loader.Sym {
} else {
d.addDwarfAddrField(fsu, 0) // CIE offset
}
fsu.AddAddrPlus(d.arch, s, 0)
addAddrPlus(fsu, d.arch, s, 0)
fsu.AddUintXX(d.arch, uint64(len(d.ldr.Data(fn))), d.arch.PtrSize) // address range
fsu.AddBytes(deltaBuf)
@ -1437,7 +1474,7 @@ func (d *dwctxt2) writeframes(syms []loader.Sym) []loader.Sym {
}
}
return syms
return dwarfSecInfo{syms: []loader.Sym{fs}}
}
/*
@ -1458,13 +1495,13 @@ func appendSyms(syms []loader.Sym, src []sym.LoaderSym) []loader.Sym {
return syms
}
func (d *dwctxt2) writeinfo(syms []loader.Sym, units []*sym.CompilationUnit, abbrevsym loader.Sym, pubNames, pubTypes *pubWriter2) []loader.Sym {
func (d *dwctxt2) writeinfo(units []*sym.CompilationUnit, abbrevsym loader.Sym, pubNames, pubTypes *pubWriter2) dwarfSecInfo {
infosec := d.ldr.LookupOrCreateSym(".debug_info", 0)
disu := d.ldr.MakeSymbolUpdater(infosec)
disu.SetType(sym.SDWARFINFO)
d.ldr.SetAttrReachable(infosec, true)
syms = append(syms, infosec)
syms := []loader.Sym{infosec}
for _, u := range units {
compunit := u.DWInfo
@ -1542,7 +1579,8 @@ func (d *dwctxt2) writeinfo(syms []loader.Sym, units []*sym.CompilationUnit, abb
pubTypes.endCompUnit(compunit, uint32(cusize)+4)
syms = append(syms, cu...)
}
return syms
return dwarfSecInfo{syms: syms}
}
/*
@ -1618,10 +1656,10 @@ func ispubtype(die *dwarf.DWDie) bool {
return die.Abbrev >= dwarf.DW_ABRV_NULLTYPE
}
func (d *dwctxt2) writegdbscript(syms []loader.Sym) []loader.Sym {
func (d *dwctxt2) writegdbscript() dwarfSecInfo {
// TODO (aix): make it available
if d.linkctxt.HeadType == objabi.Haix {
return syms
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
@ -1630,21 +1668,19 @@ func (d *dwctxt2) writegdbscript(syms []loader.Sym) []loader.Sym {
// (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 syms
return dwarfSecInfo{}
}
if gdbscript == "" {
return dwarfSecInfo{}
}
if gdbscript != "" {
gs := d.ldr.LookupOrCreateSym(".debug_gdb_scripts", 0)
u := d.ldr.MakeSymbolUpdater(gs)
u.SetType(sym.SDWARFSECT)
syms = append(syms, gs)
u.AddUint8(1) // magic 1 byte?
u.Addstring(gdbscript)
}
return syms
gs := d.ldr.LookupOrCreateSym(".debug_gdb_scripts", 0)
u := d.ldr.MakeSymbolUpdater(gs)
u.SetType(sym.SDWARFSECT)
u.AddUint8(1) // magic 1 byte?
u.Addstring(gdbscript)
return dwarfSecInfo{syms: []loader.Sym{gs}}
}
// FIXME: might be worth looking replacing this map with a function
@ -1970,19 +2006,8 @@ func dwarfGenerateDebugSyms(ctxt *Link) {
}
func (d *dwctxt2) dwarfGenerateDebugSyms() {
// Hack: because the "wavefront" hasn't been pushed all the way
// up to dodata(), there will have been changes made to the sym.Symbol's
// that are not yet reflected in the loader. Call a temporary
// loader routine that copies any changes back.
// WARNING: changing a symbol's content will usually require
// calling the loader cloneToExternal method, meaning that there
// can be an increase in memory, so this is likely to mess up any
// benchmarking runs.
d.ldr.PropagateSymbolChangesBackToLoader()
abbrev := d.writeabbrev()
syms := []loader.Sym{abbrev}
abbrevSec := d.writeabbrev()
dwarfp2 = append(dwarfp2, abbrevSec)
d.calcCompUnitRanges()
sort.Sort(compilationUnitByStartPC(d.linkctxt.compUnits))
@ -1992,7 +2017,7 @@ func (d *dwctxt2) dwarfGenerateDebugSyms() {
dlu := d.ldr.MakeSymbolUpdater(debugLine)
dlu.SetType(sym.SDWARFSECT)
d.ldr.SetAttrReachable(debugLine, true)
syms = append(syms, debugLine)
dwarfp2 = append(dwarfp2, dwarfSecInfo{syms: []loader.Sym{debugLine}})
debugRanges := d.ldr.LookupOrCreateSym(".debug_ranges", 0)
dru := d.ldr.MakeSymbolUpdater(debugRanges)
@ -2019,29 +2044,34 @@ func (d *dwctxt2) dwarfGenerateDebugSyms() {
pubNames := newPubWriter2(d, ".debug_pubnames")
pubTypes := newPubWriter2(d, ".debug_pubtypes")
// Need to reorder symbols so sym.SDWARFINFO is after all sym.SDWARFSECT
infosyms := d.writeinfo(nil, d.linkctxt.compUnits, abbrev, pubNames, pubTypes)
infoSec := d.writeinfo(d.linkctxt.compUnits, abbrevSec.secSym(), pubNames, pubTypes)
syms = d.writeframes(syms)
syms = append(syms, pubNames.s, pubTypes.s)
syms = d.writegdbscript(syms)
// We are now done writing SDWARFSECT symbols, so we can write
// other SDWARF* symbols.
syms = append(syms, infosyms...)
syms = d.collectlocs(syms, d.linkctxt.compUnits)
syms = append(syms, debugRanges)
framesSec := d.writeframes()
dwarfp2 = append(dwarfp2, framesSec)
dwarfp2 = append(dwarfp2, dwarfSecInfo{syms: []loader.Sym{pubNames.s}})
dwarfp2 = append(dwarfp2, dwarfSecInfo{syms: []loader.Sym{pubTypes.s}})
gdbScriptSec := d.writegdbscript()
if gdbScriptSec.secSym() != 0 {
dwarfp2 = append(dwarfp2, gdbScriptSec)
}
dwarfp2 = append(dwarfp2, infoSec)
locSec := d.collectlocs(d.linkctxt.compUnits)
if locSec.secSym() != 0 {
dwarfp2 = append(dwarfp2, locSec)
}
rsyms := []loader.Sym{debugRanges}
for _, unit := range d.linkctxt.compUnits {
for _, s := range unit.RangeSyms2 {
syms = append(syms, loader.Sym(s))
rsyms = append(rsyms, loader.Sym(s))
}
}
dwarfp2 = syms
anonVerReplacement := d.linkctxt.Syms.IncVersion()
dwarfp = d.ldr.PropagateLoaderChangesToSymbols(dwarfp2, anonVerReplacement)
dwarfp2 = append(dwarfp2, dwarfSecInfo{syms: rsyms})
}
func (d *dwctxt2) collectlocs(syms []loader.Sym, units []*sym.CompilationUnit) []loader.Sym {
func (d *dwctxt2) collectlocs(units []*sym.CompilationUnit) dwarfSecInfo {
empty := true
syms := []loader.Sym{}
for _, u := range units {
for _, fn := range u.FuncDIEs2 {
relocs := d.ldr.Relocs(loader.Sym(fn))
@ -2064,14 +2094,15 @@ func (d *dwctxt2) collectlocs(syms []loader.Sym, units []*sym.CompilationUnit) [
}
// Don't emit .debug_loc if it's empty -- it makes the ARM linker mad.
if !empty {
locsym := d.ldr.LookupOrCreateSym(".debug_loc", 0)
u := d.ldr.MakeSymbolUpdater(locsym)
u.SetType(sym.SDWARFLOC)
d.ldr.SetAttrReachable(locsym, true)
syms = append(syms, locsym)
if empty {
return dwarfSecInfo{}
}
return syms
locsym := d.ldr.LookupOrCreateSym(".debug_loc", 0)
u := d.ldr.MakeSymbolUpdater(locsym)
u.SetType(sym.SDWARFLOC)
d.ldr.SetAttrReachable(locsym, true)
return dwarfSecInfo{syms: append([]loader.Sym{locsym}, syms...)}
}
/*

View File

@ -24,7 +24,27 @@ func isDwarf64(ctxt *Link) bool {
return ctxt.HeadType == objabi.Haix
}
var dwarfp []*sym.Symbol
// dwarfSecInfo2 is a replica of the dwarfSecInfo struct but with
// *sym.Symbol content instead of loader.Sym content.
type dwarfSecInfo2 struct {
syms []*sym.Symbol
}
func (dsi *dwarfSecInfo2) secSym() *sym.Symbol {
if len(dsi.syms) == 0 {
return nil
}
return dsi.syms[0]
}
func (dsi *dwarfSecInfo2) subSyms() []*sym.Symbol {
if len(dsi.syms) == 0 {
return []*sym.Symbol{}
}
return dsi.syms[1:]
}
var dwarfp []dwarfSecInfo2
/*
* Elf.
@ -89,19 +109,13 @@ func dwarfcompress(ctxt *Link) {
return
}
var start, compressedCount int
var compressedCount int
resChannel := make(chan compressedSect)
for i, s := range dwarfp {
// Find the boundaries between sections and compress
// the whole section once we've found the last of its
// symbols.
if i+1 >= len(dwarfp) || s.Sect != dwarfp[i+1].Sect {
go func(resIndex int, syms []*sym.Symbol) {
resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms}
}(compressedCount, dwarfp[start:i+1])
compressedCount++
start = i + 1
}
for i := range dwarfp {
go func(resIndex int, syms []*sym.Symbol) {
resChannel <- compressedSect{resIndex, compressSyms(ctxt, syms), syms}
}(compressedCount, dwarfp[i].syms)
compressedCount++
}
res := make([]compressedSect, compressedCount)
for ; compressedCount > 0; compressedCount-- {
@ -109,23 +123,25 @@ func dwarfcompress(ctxt *Link) {
res[r.index] = r
}
var newDwarfp []*sym.Symbol
var newDwarfp []dwarfSecInfo2
Segdwarf.Sections = Segdwarf.Sections[:0]
for _, z := range res {
s := z.syms[0]
if z.compressed == nil {
// Compression didn't help.
newDwarfp = append(newDwarfp, z.syms...)
ds := dwarfSecInfo2{syms: z.syms}
newDwarfp = append(newDwarfp, ds)
Segdwarf.Sections = append(Segdwarf.Sections, s.Sect)
} else {
compressedSegName := ".zdebug_" + s.Sect.Name[len(".debug_"):]
sect := addsection(ctxt.Arch, &Segdwarf, compressedSegName, 04)
sect := addsection(ctxt.loader, ctxt.Arch, &Segdwarf, compressedSegName, 04)
sect.Length = uint64(len(z.compressed))
newSym := ctxt.Syms.Lookup(compressedSegName, 0)
newSym.P = z.compressed
newSym.Size = int64(len(z.compressed))
newSym.Sect = sect
newDwarfp = append(newDwarfp, newSym)
ds := dwarfSecInfo2{syms: []*sym.Symbol{newSym}}
newDwarfp = append(newDwarfp, ds)
}
}
dwarfp = newDwarfp
@ -135,20 +151,21 @@ func dwarfcompress(ctxt *Link) {
// based on Section.Vaddr and Symbol.Value.
pos := Segdwarf.Vaddr
var prevSect *sym.Section
for _, s := range dwarfp {
s.Value = int64(pos)
if s.Sect != prevSect {
s.Sect.Vaddr = uint64(s.Value)
prevSect = s.Sect
for _, si := range dwarfp {
for _, s := range si.syms {
s.Value = int64(pos)
if s.Sect != prevSect {
s.Sect.Vaddr = uint64(s.Value)
prevSect = s.Sect
}
if s.Sub != nil {
log.Fatalf("%s: unexpected sub-symbols", s)
}
pos += uint64(s.Size)
if ctxt.HeadType == objabi.Hwindows {
pos = uint64(Rnd(int64(pos), PEFILEALIGN))
}
}
if s.Sub != nil {
log.Fatalf("%s: unexpected sub-symbols", s)
}
pos += uint64(s.Size)
if ctxt.HeadType == objabi.Hwindows {
pos = uint64(Rnd(int64(pos), PEFILEALIGN))
}
}
Segdwarf.Length = pos - Segdwarf.Vaddr
}

View File

@ -1447,8 +1447,14 @@ func Elfemitreloc(ctxt *Link) {
for _, sect := range Segdata.Sections {
elfrelocsect(ctxt, sect, ctxt.datap)
}
for _, sect := range Segdwarf.Sections {
elfrelocsect(ctxt, sect, dwarfp)
for i := 0; i < len(Segdwarf.Sections); i++ {
sect := Segdwarf.Sections[i]
si := dwarfp[i]
if si.secSym() != sect.Sym ||
si.secSym().Sect != sect {
panic("inconsistency between dwarfp and Segdwarf")
}
elfrelocsect(ctxt, sect, si.syms)
}
}
@ -2230,10 +2236,9 @@ elfobj:
for _, sect := range Segdata.Sections {
elfshreloc(ctxt.Arch, sect)
}
for _, s := range dwarfp {
if len(s.R) > 0 || s.Type == sym.SDWARFINFO || s.Type == sym.SDWARFLOC {
elfshreloc(ctxt.Arch, s.Sect)
}
for _, si := range dwarfp {
s := si.secSym()
elfshreloc(ctxt.Arch, s.Sect)
}
// add a .note.GNU-stack section to mark the stack as non-executable
sh := elfshname(".note.GNU-stack")

View File

@ -383,6 +383,7 @@ func fieldtrack(arch *sys.Arch, l *loader.Loader) {
}
}
}
l.Reachparent = nil // we are done with it
if *flagFieldTrack == "" {
return
}

View File

@ -561,7 +561,7 @@ func (ctxt *Link) loadlib() {
}
// Add non-package symbols and references of externally defined symbols.
ctxt.loader.LoadNonpkgSyms(ctxt.Syms)
ctxt.loader.LoadNonpkgSyms(ctxt.Arch)
// Load symbols from shared libraries, after all Go object symbols are loaded.
for _, lib := range ctxt.Library {
@ -2218,8 +2218,8 @@ func ldshlibsyms(ctxt *Link, shlib string) {
ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
}
func addsection(arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section {
sect := new(sym.Section)
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
@ -2627,6 +2627,16 @@ func (ctxt *Link) xdefine(p string, t sym.SymKind, v int64) {
s.Attr |= sym.AttrLocal
}
func (ctxt *Link) xdefine2(p string, t sym.SymKind, v int64) {
ldr := ctxt.loader
s := ldr.CreateSymForUpdate(p, 0)
s.SetType(t)
s.SetValue(v)
s.SetReachable(true)
s.SetSpecial(true)
s.SetLocal(true)
}
func datoff(s *sym.Symbol, addr int64) int64 {
if uint64(addr) >= Segdata.Vaddr {
return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff)
@ -2772,6 +2782,24 @@ func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library
*order = append(*order, lib)
}
// addToTextp populates the context Textp slice.
func addToTextp(ctxt *Link) {
// Set up ctxt.Textp, based on ctxt.Textp2.
textp := make([]*sym.Symbol, 0, len(ctxt.Textp2))
haveshlibs := len(ctxt.Shlibs) > 0
for _, tsym := range ctxt.Textp2 {
sp := ctxt.loader.Syms[tsym]
if sp == nil || !ctxt.loader.AttrReachable(tsym) {
panic("should never happen")
}
if haveshlibs && sp.Type == sym.SDYNIMPORT {
continue
}
textp = append(textp, sp)
}
ctxt.Textp = textp
}
func (ctxt *Link) loadlibfull() {
// Load full symbol contents, resolve indexed references.
@ -2784,7 +2812,7 @@ func (ctxt *Link) loadlibfull() {
}
// Pull the symbols out.
ctxt.loader.ExtractSymbols(ctxt.Syms, ctxt.Reachparent)
ctxt.loader.ExtractSymbols(ctxt.Syms)
ctxt.lookup = ctxt.Syms.ROLookup
// Recreate dynexp using *sym.Symbol instead of loader.Sym
@ -2798,14 +2826,41 @@ func (ctxt *Link) loadlibfull() {
// Set special global symbols.
ctxt.setArchSyms(AfterLoadlibFull)
// Convert special symbols created by pcln.
pclntabFirstFunc = ctxt.loader.Syms[pclntabFirstFunc2]
pclntabLastFunc = ctxt.loader.Syms[pclntabLastFunc2]
// Populate dwarfp from dwarfp2. If we see a symbol index
// whose loader.Syms entry is nil, something went wrong.
for _, si := range dwarfp2 {
syms := make([]*sym.Symbol, 0, len(si.syms))
for _, symIdx := range si.syms {
s := ctxt.loader.Syms[symIdx]
if s == nil {
panic(fmt.Sprintf("nil sym for dwarfp2 element %d", symIdx))
}
s.Attr |= sym.AttrLocal
syms = append(syms, s)
}
dwarfp = append(dwarfp, dwarfSecInfo2{syms: syms})
}
// For now, overwrite symbol type with its "group" type, as dodata
// expected. Once we converted dodata, this will probably not be
// needed.
for i, t := range symGroupType {
if t != sym.Sxxx {
ctxt.loader.Syms[i].Type = t
}
}
symGroupType = nil
if ctxt.Debugvlog > 1 {
// loadlibfull is likely a good place to dump.
// Only dump under -v=2 and above.
ctxt.dumpsyms()
}
}
func (ctxt *Link) dumpsyms() {
for _, s := range ctxt.Syms.Allsym {
fmt.Printf("%s %s %p %v %v\n", s, s.Type, s, s.Attr.Reachable(), s.Attr.OnList())
fmt.Printf("%s %s reachable=%v onlist=%v outer=%v sub=%v\n", s, s.Type, s.Attr.Reachable(), s.Attr.OnList(), s.Outer, s.Sub)
for i := range s.R {
fmt.Println("\t", s.R[i].Type, s.R[i].Sym)
}

View File

@ -82,9 +82,6 @@ type Link struct {
tramps []loader.Sym // trampolines
// Used to implement field tracking.
Reachparent map[*sym.Symbol]*sym.Symbol
compUnits []*sym.CompilationUnit // DWARF compilation units
runtimeCU *sym.CompilationUnit // One of the runtime CUs, the last one seen.

View File

@ -1065,8 +1065,14 @@ func Machoemitreloc(ctxt *Link) {
for _, sect := range Segdata.Sections {
machorelocsect(ctxt, sect, ctxt.datap)
}
for _, sect := range Segdwarf.Sections {
machorelocsect(ctxt, sect, dwarfp)
for i := 0; i < len(Segdwarf.Sections); i++ {
sect := Segdwarf.Sections[i]
si := dwarfp[i]
if si.secSym() != sect.Sym ||
si.secSym().Sect != sect {
panic("inconsistency between dwarfp and Segdwarf")
}
machorelocsect(ctxt, sect, si.syms)
}
}

View File

@ -35,7 +35,6 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/benchmark"
"cmd/link/internal/sym"
"flag"
"log"
"os"
@ -156,9 +155,6 @@ func Main(arch *sys.Arch, theArch Arch) {
}
}
if objabi.Fieldtrack_enabled != 0 {
ctxt.Reachparent = make(map[*sym.Symbol]*sym.Symbol)
}
checkStrictDups = *FlagStrictDups
startProfile()
@ -298,10 +294,12 @@ func Main(arch *sys.Arch, theArch Arch) {
container := ctxt.pclntab()
bench.Start("findfunctab")
ctxt.findfunctab(container)
bench.Start("loadlibfull")
ctxt.loadlibfull() // XXX do it here for now
bench.Start("dwarfGenerateDebugSyms")
dwarfGenerateDebugSyms(ctxt)
bench.Start("symtab")
ctxt.symtab()
bench.Start("loadlibfull")
ctxt.loadlibfull() // XXX do it here for now
bench.Start("dodata")
ctxt.dodata()
bench.Start("address")
@ -317,22 +315,18 @@ func Main(arch *sys.Arch, theArch Arch) {
// 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.
var outputMmapped bool
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.
err := ctxt.Out.Mmap(filesize)
outputMmapped = err == nil
}
if outputMmapped {
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")
thearch.Asmb(ctxt)
bench.Start("reloc")
ctxt.reloc()
bench.Start("Munmap")
ctxt.Out.Munmap()
} else {
// If we don't mmap, we need to apply relocations before
// writing out.
@ -344,6 +338,9 @@ func Main(arch *sys.Arch, theArch Arch) {
bench.Start("Asmb2")
thearch.Asmb2(ctxt)
bench.Start("Munmap")
ctxt.Out.Close() // Close handles Munmapping if necessary.
bench.Start("undef")
ctxt.undef()
bench.Start("hostlink")

View File

@ -5,7 +5,6 @@
package ld
import (
"bufio"
"cmd/internal/sys"
"cmd/link/internal/sym"
"encoding/binary"
@ -14,6 +13,13 @@ import (
"os"
)
// If fallocate is not supported on this platform, return this error.
// Note this is the same error returned by filesystems that don't support
// fallocate, and that is intentional. The error is ignored where needed, and
// OutBuf writes to heap memory.
const fallocateNotSupportedErr = "operation not supported"
const outbufMode = 0775
// OutBuf is a buffered file writer.
//
// It is simlar to the Writer in cmd/internal/bio with a few small differences.
@ -28,8 +34,9 @@ import (
// - 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
// - possibly write more content to the file, which will not be edited later.
//
// And finally, it provides a mechanism by which you can multithread the
// writing of output files. This mechanism is accomplished by copying a OutBuf,
@ -54,28 +61,28 @@ import (
// wg.Wait()
// }
type OutBuf struct {
arch *sys.Arch
off int64
w *bufio.Writer
buf []byte // backing store of mmap'd output file
name string
f *os.File
encbuf [8]byte // temp buffer used by WriteN methods
isView bool // true if created from View()
start, length uint64 // start and length mmaped data.
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("cannont open more than one file")
return errors.New("cannot open more than one file")
}
f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
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.w = bufio.NewWriter(f)
out.f = f
return nil
}
@ -89,16 +96,12 @@ func NewOutBuf(arch *sys.Arch) *OutBuf {
var viewError = errors.New("output not mmapped")
func (out *OutBuf) View(start uint64) (*OutBuf, error) {
if out.buf == nil {
return nil, viewError
}
return &OutBuf{
arch: out.arch,
name: out.name,
buf: out.buf,
heap: out.heap,
off: int64(start),
start: start,
length: out.length,
isView: true,
}, nil
}
@ -109,10 +112,19 @@ func (out *OutBuf) Close() error {
if out.isView {
return viewCloseError
}
out.Flush()
if out.isMmapped() {
out.copyHeap()
out.munmap()
return nil
}
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
}
@ -120,16 +132,70 @@ func (out *OutBuf) Close() error {
return nil
}
func (out *OutBuf) SeekSet(p int64) {
if p == out.off {
return
// isMmapped returns true if the OutBuf is mmaped.
func (out *OutBuf) isMmapped() bool {
return len(out.buf) != 0
}
// 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.buf == nil {
out.Flush()
if _, err := out.f.Seek(p, 0); err != nil {
Exitf("seeking to %d in %s: %v", p, out.name, err)
if out.isView {
panic("can't copyHeap a view")
}
bufLen := len(out.buf)
heapLen := len(out.heap)
total := uint64(bufLen + heapLen)
out.munmap()
if heapLen != 0 {
if err := out.Mmap(total); err != nil {
panic(err)
}
copy(out.buf[bufLen:], out.heap[:heapLen])
out.heap = out.heap[:0]
}
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 = 0, 0, lenToWrite
}
out.heap = append(out.heap, make([]byte, lenNeeded-heapLen)...)
}
return heapPos, out.heap
}
func (out *OutBuf) SeekSet(p int64) {
out.off = p
}
@ -138,30 +204,18 @@ func (out *OutBuf) Offset() int64 {
}
// Write writes the contents of v to the buffer.
//
// As Write is backed by a bufio.Writer, callers do not have
// to explicitly handle the returned error as long as Flush is
// eventually called.
func (out *OutBuf) Write(v []byte) (int, error) {
if out.buf != nil {
n := copy(out.buf[out.off:], v)
out.off += int64(n)
return n, nil
}
n, err := out.w.Write(v)
n := len(v)
pos, buf := out.writeLoc(int64(n))
copy(buf[pos:], v)
out.off += int64(n)
return n, err
return n, nil
}
func (out *OutBuf) Write8(v uint8) {
if out.buf != nil {
out.buf[out.off] = v
out.off++
return
}
if err := out.w.WriteByte(v); err == nil {
out.off++
}
pos, buf := out.writeLoc(1)
buf[pos] = v
out.off++
}
// WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface.
@ -196,15 +250,11 @@ func (out *OutBuf) Write64b(v uint64) {
}
func (out *OutBuf) WriteString(s string) {
if out.buf != nil {
n := copy(out.buf[out.off:], 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)
return
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))
}
n, _ := out.w.WriteString(s)
out.off += int64(n)
}
@ -236,28 +286,10 @@ func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) {
// edit to the symbol content.
// If the output file is not Mmap'd, just writes the content.
func (out *OutBuf) WriteSym(s *sym.Symbol) {
// NB: We inline the Write call for speediness.
if out.buf != nil {
start := out.off
n := copy(out.buf[out.off:], s.P)
out.off += int64(n)
s.P = out.buf[start:out.off]
s.Attr.Set(sym.AttrReadOnly, false)
} else {
n, _ := out.w.Write(s.P)
out.off += int64(n)
}
}
func (out *OutBuf) Flush() {
var err error
if out.buf != nil {
err = out.Msync()
}
if out.w != nil {
err = out.w.Flush()
}
if err != nil {
Exitf("flushing %s: %v", out.name, err)
}
n := int64(len(s.P))
pos, buf := out.writeLoc(n)
copy(buf[pos:], s.P)
out.off += n
s.P = buf[pos : pos+n]
s.Attr.Set(sym.AttrReadOnly, false)
}

View File

@ -0,0 +1,26 @@
// 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 {
store := &syscall.Fstore_t{
Flags: syscall.F_ALLOCATEALL,
Posmode: syscall.F_PEOFPOSMODE,
Offset: 0,
Length: int64(size),
}
_, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store)))
if err != 0 {
return err
}
return nil
}

View File

@ -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()), outbufMode, 0, int64(size))
}

View File

@ -8,44 +8,34 @@ package ld
import (
"syscall"
"unsafe"
)
func (out *OutBuf) Mmap(filesize uint64) error {
err := out.f.Truncate(int64(filesize))
err := out.fallocate(filesize)
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.Error() != fallocateNotSupportedErr {
return err
}
}
err = out.f.Truncate(int64(filesize))
if err != nil {
Exitf("resize output file failed: %v", err)
}
out.length = filesize
out.buf, err = syscall.Mmap(int(out.f.Fd()), 0, int(filesize), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FILE)
return err
}
func (out *OutBuf) Munmap() {
func (out *OutBuf) munmap() {
if out.buf == nil {
return
}
err := out.Msync()
if err != nil {
Exitf("msync output file failed: %v", err)
}
syscall.Munmap(out.buf)
out.buf = nil
_, err = out.f.Seek(out.off, 0)
_, err := out.f.Seek(out.off, 0)
if err != nil {
Exitf("seek output file failed: %v", err)
}
}
func (out *OutBuf) Msync() error {
if out.buf == nil || out.length <= 0 {
return nil
}
// TODO: netbsd supports mmap and msync, but the syscall package doesn't define MSYNC.
// It is excluded from the build tag for now.
_, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(out.length), syscall.MS_SYNC)
if errno != 0 {
return errno
}
return nil
}

View File

@ -0,0 +1,13 @@
// 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 "errors"
func (out *OutBuf) fallocate(size uint64) error {
return errors.New(fallocateNotSupportedErr)
}

View File

@ -6,10 +6,10 @@
package ld
import "errors"
func (out *OutBuf) Mmap(filesize uint64) error {
// We need space to put all the symbols before we apply relocations.
out.heap = make([]byte, filesize)
return nil
}
var errNotSupported = errors.New("mmap not supported")
func (out *OutBuf) Mmap(filesize uint64) error { return errNotSupported }
func (out *OutBuf) Munmap() { panic("unreachable") }
func (out *OutBuf) Msync() error { panic("unreachable") }
func (out *OutBuf) munmap() { panic("unreachable") }

View File

@ -33,4 +33,67 @@ func TestMMap(t *testing.T) {
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)
}
}
}

View File

@ -31,7 +31,7 @@ func (out *OutBuf) Mmap(filesize uint64) error {
return nil
}
func (out *OutBuf) Munmap() {
func (out *OutBuf) munmap() {
if out.buf == nil {
return
}
@ -41,10 +41,3 @@ func (out *OutBuf) Munmap() {
Exitf("UnmapViewOfFile failed: %v", err)
}
}
func (out *OutBuf) Msync() error {
if out.buf == nil {
return nil
}
return syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])), 0)
}

View File

@ -234,10 +234,8 @@ func (state *pclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch) loader
var pclntabNfunc int32
var pclntabFiletabOffset int32
var pclntabPclntabOffset int32
var pclntabFirstFunc *sym.Symbol
var pclntabLastFunc *sym.Symbol
var pclntabFirstFunc2 loader.Sym
var pclntabLastFunc2 loader.Sym
var pclntabFirstFunc loader.Sym
var pclntabLastFunc loader.Sym
// pclntab generates the pcln table for the link output. Return value
// is a bitmap indexed by global symbol that marks 'container' text
@ -276,8 +274,8 @@ func (ctxt *Link) pclntab() loader.Bitmap {
continue
}
nfunc++
if pclntabFirstFunc2 == 0 {
pclntabFirstFunc2 = s
if pclntabFirstFunc == 0 {
pclntabFirstFunc = s
}
ss := ldr.SymSect(s)
if ss != prevSect {
@ -325,6 +323,25 @@ func (ctxt *Link) pclntab() loader.Bitmap {
return newoff
}
setAddr := (*loader.SymbolBuilder).SetAddrPlus
if ctxt.IsExe() && ctxt.IsInternal() && !ctxt.DynlinkingGo() {
// Internal linking static executable. At this point the function
// addresses are known, so we can just use them instead of emitting
// relocations.
// For other cases we are generating a relocatable binary so we
// still need to emit relocations.
//
// Also not do this optimization when using plugins (DynlinkingGo),
// as on darwin it does weird things with runtime.etext symbol.
// TODO: remove the weird thing and remove this condition.
setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
if v := ldr.SymValue(tgt); v != 0 {
return s.SetUint(arch, off, uint64(v+add))
}
return s.SetAddrPlus(arch, off, tgt, add)
}
}
pcsp := sym.Pcdata{}
pcfile := sym.Pcdata{}
pcline := sym.Pcdata{}
@ -347,7 +364,7 @@ func (ctxt *Link) pclntab() loader.Bitmap {
// invalid funcoff value to mark the hole. See also
// runtime/symtab.go:findfunc
prevFuncSize := int64(ldr.SymSize(prevFunc))
ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
setAddr(ftab, ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0))
nfunc++
}
@ -397,10 +414,7 @@ func (ctxt *Link) pclntab() loader.Bitmap {
funcstart := int32(dSize)
funcstart += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize
// NB: for the static binary internal-link case, we could just
// emit the symbol value instead of creating a relocation here
// (might speed things up for that case).
ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s, 0)
setAddr(ftab, ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s, 0)
ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart))
// Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
@ -416,7 +430,7 @@ func (ctxt *Link) pclntab() loader.Bitmap {
ftab.Grow(int64(end))
// entry uintptr
off = int32(ftab.SetAddrPlus(ctxt.Arch, int64(off), s, 0))
off = int32(setAddr(ftab, ctxt.Arch, int64(off), s, 0))
// name int32
sn := ldr.SymName(s)
@ -497,7 +511,7 @@ func (ctxt *Link) pclntab() loader.Bitmap {
}
// TODO: Dedup.
funcdataBytes += int64(len(ldr.Data(funcdata[i])))
ftab.SetAddrPlus(ctxt.Arch, dataoff, funcdata[i], funcdataoff[i])
setAddr(ftab, ctxt.Arch, dataoff, funcdata[i], funcdataoff[i])
}
off += int32(len(funcdata)) * int32(ctxt.Arch.PtrSize)
}
@ -511,9 +525,9 @@ func (ctxt *Link) pclntab() loader.Bitmap {
}
last := ctxt.Textp2[len(ctxt.Textp2)-1]
pclntabLastFunc2 = last
pclntabLastFunc = last
// Final entry of table is just end pc.
ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, ldr.SymSize(last))
setAddr(ftab, ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, ldr.SymSize(last))
// Start file table.
dSize := len(ftab.Data())

View File

@ -557,11 +557,17 @@ func (f *peFile) emitRelocations(ctxt *Link) {
}
dwarfLoop:
for _, sect := range Segdwarf.Sections {
for i := 0; i < len(Segdwarf.Sections); i++ {
sect := Segdwarf.Sections[i]
si := dwarfp[i]
if si.secSym() != sect.Sym ||
si.secSym().Sect != 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, dwarfp, sect.Vaddr)
return relocsect(sect, si.syms, sect.Vaddr)
})
continue dwarfLoop
}

View File

@ -33,6 +33,7 @@ package ld
import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"fmt"
"path/filepath"
@ -271,12 +272,12 @@ func (libs byPkg) Swap(a, b int) {
}
// Create a table with information on the text sections.
func textsectionmap(ctxt *Link) uint32 {
t := ctxt.Syms.Lookup("runtime.textsectionmap", 0)
t.Type = sym.SRODATA
t.Attr |= sym.AttrReachable
// 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)
t.SetReachable(true)
nsections := int64(0)
for _, sect := range Segtext.Sections {
@ -308,107 +309,106 @@ func textsectionmap(ctxt *Link) uint32 {
off = t.SetUint(ctxt.Arch, off, sect.Vaddr-textbase)
off = t.SetUint(ctxt.Arch, off, sect.Length)
if n == 0 {
s := ctxt.Syms.ROLookup("runtime.text", 0)
if s == nil {
Errorf(nil, "Unable to find symbol runtime.text\n")
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 := ctxt.Syms.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
if s == nil {
Errorf(nil, "Unable to find symbol runtime.text.%d\n", n)
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 uint32(n)
return t.Sym(), uint32(n)
}
var symGroupType []sym.SymKind // temporarily assign a symbol's "group" type
func (ctxt *Link) symtab() {
if ctxt.HeadType != objabi.Haix {
ldr := ctxt.loader
if !ctxt.IsAIX() {
switch ctxt.BuildMode {
case BuildModeCArchive, BuildModeCShared:
s := ctxt.Syms.ROLookup(*flagEntrySymbol, sym.SymVerABI0)
if s != nil {
addinitarrdata(ctxt, s)
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.text", sym.STEXT, 0)
ctxt.xdefine("runtime.etext", sym.STEXT, 0)
ctxt.xdefine("runtime.itablink", sym.SRODATA, 0)
ctxt.xdefine("runtime.eitablink", sym.SRODATA, 0)
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)
ctxt.xdefine2("runtime.text", sym.STEXT, 0)
ctxt.xdefine2("runtime.etext", sym.STEXT, 0)
ctxt.xdefine2("runtime.itablink", sym.SRODATA, 0)
ctxt.xdefine2("runtime.eitablink", sym.SRODATA, 0)
ctxt.xdefine2("runtime.rodata", sym.SRODATA, 0)
ctxt.xdefine2("runtime.erodata", sym.SRODATA, 0)
ctxt.xdefine2("runtime.types", sym.SRODATA, 0)
ctxt.xdefine2("runtime.etypes", sym.SRODATA, 0)
ctxt.xdefine2("runtime.noptrdata", sym.SNOPTRDATA, 0)
ctxt.xdefine2("runtime.enoptrdata", sym.SNOPTRDATA, 0)
ctxt.xdefine2("runtime.data", sym.SDATA, 0)
ctxt.xdefine2("runtime.edata", sym.SDATA, 0)
ctxt.xdefine2("runtime.bss", sym.SBSS, 0)
ctxt.xdefine2("runtime.ebss", sym.SBSS, 0)
ctxt.xdefine2("runtime.noptrbss", sym.SNOPTRBSS, 0)
ctxt.xdefine2("runtime.enoptrbss", sym.SNOPTRBSS, 0)
ctxt.xdefine2("runtime.end", sym.SBSS, 0)
ctxt.xdefine2("runtime.epclntab", sym.SRODATA, 0)
ctxt.xdefine2("runtime.esymtab", sym.SRODATA, 0)
// garbage collection symbols
s := ctxt.Syms.Lookup("runtime.gcdata", 0)
s := ldr.CreateSymForUpdate("runtime.gcdata", 0)
s.SetType(sym.SRODATA)
s.SetSize(0)
s.SetReachable(true)
ctxt.xdefine2("runtime.egcdata", sym.SRODATA, 0)
s.Type = sym.SRODATA
s.Size = 0
s.Attr |= sym.AttrReachable
ctxt.xdefine("runtime.egcdata", sym.SRODATA, 0)
s = ctxt.Syms.Lookup("runtime.gcbss", 0)
s.Type = sym.SRODATA
s.Size = 0
s.Attr |= sym.AttrReachable
ctxt.xdefine("runtime.egcbss", sym.SRODATA, 0)
s = ldr.CreateSymForUpdate("runtime.gcbss", 0)
s.SetType(sym.SRODATA)
s.SetSize(0)
s.SetReachable(true)
ctxt.xdefine2("runtime.egcbss", sym.SRODATA, 0)
// pseudo-symbols to mark locations of type, string, and go string data.
var symtype *sym.Symbol
var symtyperel *sym.Symbol
var symtype, symtyperel loader.Sym
if !ctxt.DynlinkingGo() {
if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
s = ctxt.Syms.Lookup("type.*", 0)
s = ldr.CreateSymForUpdate("type.*", 0)
s.SetType(sym.STYPE)
s.SetSize(0)
s.SetReachable(true)
symtype = s.Sym()
s.Type = sym.STYPE
s.Size = 0
s.Attr |= sym.AttrReachable
symtype = s
s = ctxt.Syms.Lookup("typerel.*", 0)
s.Type = sym.STYPERELRO
s.Size = 0
s.Attr |= sym.AttrReachable
symtyperel = s
s = ldr.CreateSymForUpdate("typerel.*", 0)
s.SetType(sym.STYPERELRO)
s.SetSize(0)
s.SetReachable(true)
symtyperel = s.Sym()
} else {
s = ctxt.Syms.Lookup("type.*", 0)
s.Type = sym.STYPE
s.Size = 0
s.Attr |= sym.AttrReachable
symtype = s
symtyperel = s
s = ldr.CreateSymForUpdate("type.*", 0)
s.SetType(sym.STYPE)
s.SetSize(0)
s.SetReachable(true)
symtype = s.Sym()
symtyperel = s.Sym()
}
}
groupSym := func(name string, t sym.SymKind) *sym.Symbol {
s := ctxt.Syms.Lookup(name, 0)
s.Type = t
s.Size = 0
s.Attr |= sym.AttrLocal | sym.AttrReachable
return s
groupSym := func(name string, t sym.SymKind) loader.Sym {
s := ldr.CreateSymForUpdate(name, 0)
s.SetType(t)
s.SetSize(0)
s.SetLocal(true)
s.SetReachable(true)
return s.Sym()
}
var (
symgostring = groupSym("go.string.*", sym.SGOSTRING)
@ -416,7 +416,7 @@ func (ctxt *Link) symtab() {
symgcbits = groupSym("runtime.gcbits.*", sym.SGCBITS)
)
var symgofuncrel *sym.Symbol
var symgofuncrel loader.Sym
if !ctxt.DynlinkingGo() {
if ctxt.UseRelro() {
symgofuncrel = groupSym("go.funcrel.*", sym.SGOFUNCRELRO)
@ -425,14 +425,14 @@ func (ctxt *Link) symtab() {
}
}
symitablink := ctxt.Syms.Lookup("runtime.itablink", 0)
symitablink.Type = sym.SITABLINK
symitablink := ldr.CreateSymForUpdate("runtime.itablink", 0)
symitablink.SetType(sym.SITABLINK)
symt := ctxt.Syms.Lookup("runtime.symtab", 0)
symt.Attr |= sym.AttrLocal
symt.Type = sym.SSYMTAB
symt.Size = 0
symt.Attr |= sym.AttrReachable
symt := ldr.CreateSymForUpdate("runtime.symtab", 0)
symt.SetType(sym.SSYMTAB)
symt.SetSize(0)
symt.SetReachable(true)
symt.SetLocal(true)
nitablinks := 0
@ -440,147 +440,150 @@ func (ctxt *Link) symtab() {
// within a type they sort by size, so the .* symbols
// just defined above will be first.
// hide the specific symbols.
for _, s := range ctxt.Syms.Allsym {
if ctxt.LinkMode != LinkExternal && isStaticTemp(s.Name) {
s.Attr |= sym.AttrNotInSymbolTable
nsym := loader.Sym(ldr.NSym())
symGroupType = make([]sym.SymKind, nsym)
for s := loader.Sym(1); s < nsym; s++ {
name := ldr.SymName(s)
if !ctxt.IsExternal() && isStaticTemp(name) {
ldr.SetAttrNotInSymbolTable(s, true)
}
if !s.Attr.Reachable() || s.Attr.Special() ||
(s.Type != sym.SRODATA && s.Type != sym.SGOFUNC) {
if !ldr.AttrReachable(s) || ldr.AttrSpecial(s) || (ldr.SymType(s) != sym.SRODATA && ldr.SymType(s) != sym.SGOFUNC) {
continue
}
switch {
case strings.HasPrefix(s.Name, "type."):
case strings.HasPrefix(name, "type."):
if !ctxt.DynlinkingGo() {
s.Attr |= sym.AttrNotInSymbolTable
ldr.SetAttrNotInSymbolTable(s, true)
}
if ctxt.UseRelro() {
s.Type = sym.STYPERELRO
s.Outer = symtyperel
symGroupType[s] = sym.STYPERELRO
ldr.SetOuterSym(s, symtyperel)
} else {
s.Type = sym.STYPE
s.Outer = symtype
symGroupType[s] = sym.STYPE
ldr.SetOuterSym(s, symtype)
}
case strings.HasPrefix(s.Name, "go.importpath.") && ctxt.UseRelro():
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.
s.Type = sym.STYPERELRO
symGroupType[s] = sym.STYPERELRO
case strings.HasPrefix(s.Name, "go.itablink."):
case strings.HasPrefix(name, "go.itablink."):
nitablinks++
s.Type = sym.SITABLINK
s.Attr |= sym.AttrNotInSymbolTable
s.Outer = symitablink
symGroupType[s] = sym.SITABLINK
ldr.SetAttrNotInSymbolTable(s, true)
ldr.SetOuterSym(s, symitablink.Sym())
case strings.HasPrefix(s.Name, "go.string."):
s.Type = sym.SGOSTRING
s.Attr |= sym.AttrNotInSymbolTable
s.Outer = symgostring
case strings.HasPrefix(name, "go.string."):
symGroupType[s] = sym.SGOSTRING
ldr.SetAttrNotInSymbolTable(s, true)
ldr.SetOuterSym(s, symgostring)
case strings.HasPrefix(s.Name, "runtime.gcbits."):
s.Type = sym.SGCBITS
s.Attr |= sym.AttrNotInSymbolTable
s.Outer = symgcbits
case strings.HasPrefix(name, "runtime.gcbits."):
symGroupType[s] = sym.SGCBITS
ldr.SetAttrNotInSymbolTable(s, true)
ldr.SetOuterSym(s, symgcbits)
case strings.HasSuffix(s.Name, "·f"):
case strings.HasSuffix(name, "·f"):
if !ctxt.DynlinkingGo() {
s.Attr |= sym.AttrNotInSymbolTable
ldr.SetAttrNotInSymbolTable(s, true)
}
if ctxt.UseRelro() {
s.Type = sym.SGOFUNCRELRO
s.Outer = symgofuncrel
symGroupType[s] = sym.SGOFUNCRELRO
ldr.SetOuterSym(s, symgofuncrel)
} else {
s.Type = sym.SGOFUNC
s.Outer = symgofunc
symGroupType[s] = sym.SGOFUNC
ldr.SetOuterSym(s, symgofunc)
}
case strings.HasPrefix(s.Name, "gcargs."),
strings.HasPrefix(s.Name, "gclocals."),
strings.HasPrefix(s.Name, "gclocals·"),
s.Type == sym.SGOFUNC && s != symgofunc,
strings.HasSuffix(s.Name, ".opendefer"):
s.Type = sym.SGOFUNC
s.Attr |= sym.AttrNotInSymbolTable
s.Outer = symgofunc
s.Align = 4
liveness += (s.Size + int64(s.Align) - 1) &^ (int64(s.Align) - 1)
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.SetOuterSym(s, symgofunc)
const align = 4
ldr.SetSymAlign(s, align)
liveness += (ldr.SymSize(s) + int64(align) - 1) &^ (int64(align) - 1)
}
}
if ctxt.BuildMode == BuildModeShared {
abihashgostr := ctxt.Syms.Lookup("go.link.abihash."+filepath.Base(*flagOutfile), 0)
abihashgostr.Attr |= sym.AttrReachable
abihashgostr.Type = sym.SRODATA
hashsym := ctxt.Syms.Lookup("go.link.abihashbytes", 0)
abihashgostr := ldr.CreateSymForUpdate("go.link.abihash."+filepath.Base(*flagOutfile), 0)
abihashgostr.SetReachable(true)
abihashgostr.SetType(sym.SRODATA)
hashsym := ldr.LookupOrCreateSym("go.link.abihashbytes", 0)
abihashgostr.AddAddr(ctxt.Arch, hashsym)
abihashgostr.AddUint(ctxt.Arch, uint64(hashsym.Size))
abihashgostr.AddUint(ctxt.Arch, uint64(ldr.SymSize(hashsym)))
}
if ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() {
for _, l := range ctxt.Library {
s := ctxt.Syms.Lookup("go.link.pkghashbytes."+l.Pkg, 0)
s.Attr |= sym.AttrReachable
s.Type = sym.SRODATA
s.Size = int64(len(l.Hash))
s.P = []byte(l.Hash)
str := ctxt.Syms.Lookup("go.link.pkghash."+l.Pkg, 0)
str.Attr |= sym.AttrReachable
str.Type = sym.SRODATA
str.AddAddr(ctxt.Arch, s)
s := ldr.CreateSymForUpdate("go.link.pkghashbytes."+l.Pkg, 0)
s.SetReachable(true)
s.SetType(sym.SRODATA)
s.SetSize(int64(len(l.Hash)))
s.SetData([]byte(l.Hash))
str := ldr.CreateSymForUpdate("go.link.pkghash."+l.Pkg, 0)
str.SetReachable(true)
str.SetType(sym.SRODATA)
str.AddAddr(ctxt.Arch, s.Sym())
str.AddUint(ctxt.Arch, uint64(len(l.Hash)))
}
}
nsections := textsectionmap(ctxt)
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 := ctxt.Moduledata
moduledata := ldr.MakeSymbolUpdater(ctxt.Moduledata2)
pclntab := ldr.Lookup("runtime.pclntab", 0)
// The pclntab slice
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0))
moduledata.AddUint(ctxt.Arch, uint64(ctxt.Syms.Lookup("runtime.pclntab", 0).Size))
moduledata.AddUint(ctxt.Arch, uint64(ctxt.Syms.Lookup("runtime.pclntab", 0).Size))
moduledata.AddAddr(ctxt.Arch, pclntab)
moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pclntab)))
moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pclntab)))
// The ftab slice
moduledata.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabPclntabOffset))
moduledata.AddAddrPlus(ctxt.Arch, pclntab, int64(pclntabPclntabOffset))
moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1))
moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1))
// The filetab slice
moduledata.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabFiletabOffset))
moduledata.AddAddrPlus(ctxt.Arch, pclntab, int64(pclntabFiletabOffset))
moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
// findfunctab
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.findfunctab", 0))
moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.findfunctab", 0))
// minpc, maxpc
moduledata.AddAddr(ctxt.Arch, pclntabFirstFunc)
moduledata.AddAddrPlus(ctxt.Arch, pclntabLastFunc, pclntabLastFunc.Size)
moduledata.AddAddrPlus(ctxt.Arch, pclntabLastFunc, ldr.SymSize(pclntabLastFunc))
// pointers to specific parts of the module
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.text", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.etext", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.noptrdata", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.enoptrdata", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.data", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.edata", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.bss", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.ebss", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.noptrbss", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.enoptrbss", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.end", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.gcdata", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.gcbss", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.types", 0))
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.etypes", 0))
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.HeadType == objabi.Haix && ctxt.LinkMode == LinkExternal {
// Add R_REF relocation to prevent ld's garbage collection of
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()
r.Sym = ctxt.Syms.Lookup(name, 0)
r.Type = objabi.R_XCOFFREF
r.Siz = uint8(ctxt.Arch.PtrSize)
r, _ := moduledata.AddRel(objabi.R_XCOFFREF)
r.SetSym(ldr.Lookup(name, 0))
r.SetSiz(uint8(ctxt.Arch.PtrSize))
}
addRef("runtime.rodata")
addRef("runtime.erodata")
@ -588,26 +591,27 @@ func (ctxt *Link) symtab() {
}
// text section information
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.textsectionmap", 0))
moduledata.AddAddr(ctxt.Arch, textsectionmapSym)
moduledata.AddUint(ctxt.Arch, uint64(nsections))
moduledata.AddUint(ctxt.Arch, uint64(nsections))
// The typelinks slice
typelinkSym := ctxt.Syms.Lookup("runtime.typelink", 0)
ntypelinks := uint64(typelinkSym.Size) / 4
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
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.itablink", 0))
moduledata.AddAddr(ctxt.Arch, symitablink.Sym())
moduledata.AddUint(ctxt.Arch, uint64(nitablinks))
moduledata.AddUint(ctxt.Arch, uint64(nitablinks))
// The ptab slice
if ptab := ctxt.Syms.ROLookup("go.plugin.tabs", 0); ptab != nil && ptab.Attr.Reachable() {
ptab.Attr |= sym.AttrLocal
ptab.Type = sym.SRODATA
nentries := uint64(len(ptab.P) / 8) // sizeof(nameOff) + sizeof(typeOff)
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)
@ -617,23 +621,23 @@ func (ctxt *Link) symtab() {
moduledata.AddUint(ctxt.Arch, 0)
}
if ctxt.BuildMode == BuildModePlugin {
addgostring(ctxt, moduledata, "go.link.thispluginpath", objabi.PathToPrefix(*flagPluginPath))
addgostring(ctxt, ldr, moduledata, "go.link.thispluginpath", objabi.PathToPrefix(*flagPluginPath))
pkghashes := ctxt.Syms.Lookup("go.link.pkghashes", 0)
pkghashes.Attr |= sym.AttrReachable
pkghashes.Attr |= sym.AttrLocal
pkghashes.Type = sym.SRODATA
pkghashes := ldr.CreateSymForUpdate("go.link.pkghashes", 0)
pkghashes.SetReachable(true)
pkghashes.SetLocal(true)
pkghashes.SetType(sym.SRODATA)
for i, l := range ctxt.Library {
// pkghashes[i].name
addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkgname.%d", i), l.Pkg)
addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go.link.pkgname.%d", i), l.Pkg)
// pkghashes[i].linktimehash
addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkglinkhash.%d", i), l.Hash)
addgostring(ctxt, ldr, pkghashes, fmt.Sprintf("go.link.pkglinkhash.%d", i), l.Hash)
// pkghashes[i].runtimehash
hash := ctxt.Syms.ROLookup("go.link.pkghash."+l.Pkg, 0)
hash := ldr.Lookup("go.link.pkghash."+l.Pkg, 0)
pkghashes.AddAddr(ctxt.Arch, hash)
}
moduledata.AddAddr(ctxt.Arch, pkghashes)
moduledata.AddAddr(ctxt.Arch, pkghashes.Sym())
moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library)))
moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Library)))
} else {
@ -651,28 +655,28 @@ func (ctxt *Link) symtab() {
// it something slightly more comprehensible.
thismodulename = "the executable"
}
addgostring(ctxt, moduledata, "go.link.thismodulename", thismodulename)
addgostring(ctxt, ldr, moduledata, "go.link.thismodulename", thismodulename)
modulehashes := ctxt.Syms.Lookup("go.link.abihashes", 0)
modulehashes.Attr |= sym.AttrReachable
modulehashes.Attr |= sym.AttrLocal
modulehashes.Type = sym.SRODATA
modulehashes := ldr.CreateSymForUpdate("go.link.abihashes", 0)
modulehashes.SetReachable(true)
modulehashes.SetLocal(true)
modulehashes.SetType(sym.SRODATA)
for i, shlib := range ctxt.Shlibs {
// modulehashes[i].modulename
modulename := filepath.Base(shlib.Path)
addgostring(ctxt, modulehashes, fmt.Sprintf("go.link.libname.%d", i), modulename)
addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go.link.libname.%d", i), modulename)
// modulehashes[i].linktimehash
addgostring(ctxt, modulehashes, fmt.Sprintf("go.link.linkhash.%d", i), string(shlib.Hash))
addgostring(ctxt, ldr, modulehashes, fmt.Sprintf("go.link.linkhash.%d", i), string(shlib.Hash))
// modulehashes[i].runtimehash
abihash := ctxt.Syms.Lookup("go.link.abihash."+modulename, 0)
abihash.Attr |= sym.AttrReachable
abihash := ldr.LookupOrCreateSym("go.link.abihash."+modulename, 0)
ldr.SetAttrReachable(abihash, true)
modulehashes.AddAddr(ctxt.Arch, abihash)
}
moduledata.AddAddr(ctxt.Arch, modulehashes)
moduledata.AddAddr(ctxt.Arch, modulehashes.Sym())
moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs)))
moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Shlibs)))
} else {
@ -694,15 +698,16 @@ func (ctxt *Link) symtab() {
// 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 := ctxt.Syms.ROLookup("type.runtime.moduledata", 0)
moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype.P)
moduledata.Grow(moduledata.Size)
moduledatatype := ldr.Lookup("type.runtime.moduledata", 0)
moduledata.SetSize(decodetypeSize(ctxt.Arch, ldr.Data(moduledatatype)))
moduledata.Grow(moduledata.Size())
lastmoduledatap := ctxt.Syms.Lookup("runtime.lastmoduledatap", 0)
if lastmoduledatap.Type != sym.SDYNIMPORT {
lastmoduledatap.Type = sym.SNOPTRDATA
lastmoduledatap.Size = 0 // overwrite existing value
lastmoduledatap.AddAddr(ctxt.Arch, moduledata)
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())
}
}

View File

@ -28,6 +28,10 @@ type Target struct {
// Target type functions
//
func (t *Target) IsExe() bool {
return t.BuildMode == BuildModeExe
}
func (t *Target) IsShared() bool {
return t.BuildMode == BuildModeShared
}

View File

@ -1389,7 +1389,6 @@ func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) {
}
f.loaderSize = off + uint64(stlen)
ctxt.Out.Flush()
/* again for printing */
if !*flagA {
@ -1559,8 +1558,6 @@ func Asmbxcoff(ctxt *Link, fileoff int64) {
// write string table
xfile.stringTable.write(ctxt.Out)
ctxt.Out.Flush()
// write headers
xcoffwrite(ctxt)
}
@ -1660,12 +1657,18 @@ func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) {
}
dwarfLoop:
for _, sect := range Segdwarf.Sections {
for i := 0; i < len(Segdwarf.Sections); i++ {
sect := Segdwarf.Sections[i]
si := dwarfp[i]
if si.secSym() != sect.Sym ||
si.secSym().Sect != 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, dwarfp, sect.Vaddr)
xcoffSect.Snreloc = relocsect(sect, si.syms, sect.Vaddr)
continue dwarfLoop
}
}

View File

@ -198,7 +198,9 @@ type Loader struct {
payloadBatch []extSymPayload
payloads []*extSymPayload // contents of linker-materialized external syms
values []int64 // symbol values, indexed by global sym index
sects []*sym.Section // symbol's section, indexed by global index
sects []*sym.Section // sections
symSects []uint16 // symbol's section, index to sects array
itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
@ -326,6 +328,7 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc) *Loader {
builtinSyms: make([]Sym, nbuiltin),
flags: flags,
elfsetstring: elfsetstring,
sects: []*sym.Section{nil}, // reserve index 0 for nil section
}
}
@ -947,10 +950,30 @@ func (l *Loader) SetAttrReadOnly(i Sym, v bool) {
// 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.
return l.OuterSym(i) != 0
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;
@ -990,7 +1013,6 @@ func (l *Loader) growValues(reqLen int) {
curLen := len(l.values)
if reqLen > curLen {
l.values = append(l.values, make([]int64, reqLen+1-curLen)...)
l.sects = append(l.sects, make([]*sym.Section, reqLen+1-curLen)...)
}
}
@ -1053,12 +1075,35 @@ func (l *Loader) SetSymAlign(i Sym, align int32) {
// SymValue returns the section of the i-th symbol. i is global index.
func (l *Loader) SymSect(i Sym) *sym.Section {
return l.sects[i]
return l.sects[l.symSects[i]]
}
// SetSymValue sets the section of the i-th symbol. i is global index.
func (l *Loader) SetSymSect(i Sym, sect *sym.Section) {
l.sects[i] = sect
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
@ -1398,6 +1443,16 @@ func (l *Loader) SubSym(i Sym) Sym {
return l.sub[i]
}
// SetOuterSym sets the outer symbol of i to o (without setting
// sub symbols).
func (l *Loader) SetOuterSym(i Sym, o Sym) {
if o != 0 {
l.outer[i] = o
} else {
delete(l.outer, i)
}
}
// Initialize Reachable bitmap and its siblings for running deadcode pass.
func (l *Loader) InitReachable() {
l.growAttrBitmaps(l.NSym() + 1)
@ -1769,26 +1824,27 @@ func (l *Loader) preloadSyms(r *oReader, kind int) {
// Add non-package symbols and references to external symbols (which are always
// named).
func (l *Loader) LoadNonpkgSyms(syms *sym.Symbols) {
func (l *Loader) LoadNonpkgSyms(arch *sys.Arch) {
for _, o := range l.objs[1:] {
l.preloadSyms(o.r, nonPkgDef)
}
for _, o := range l.objs[1:] {
loadObjRefs(l, o.r, syms)
loadObjRefs(l, o.r, arch)
}
}
func loadObjRefs(l *Loader, r *oReader, syms *sym.Symbols) {
func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
ndef := r.NSym() + r.NNonpkgdef()
for i, n := 0, r.NNonpkgref(); i < n; i++ {
osym := r.Sym(ndef + i)
name := strings.Replace(osym.Name(r.Reader), "\"\".", 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() {
gi := r.syms[ndef+i]
l.SetAttrLocal(gi, true)
}
l.preprocess(arch, gi, name)
}
}
@ -1806,24 +1862,29 @@ func abiToVer(abi uint16, localSymVersion int) int {
return v
}
func preprocess(arch *sys.Arch, s *sym.Symbol) {
if s.Name != "" && s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 {
x, err := strconv.ParseUint(s.Name[5:], 16, 64)
// preprocess looks for integer/floating point constant symbols whose
// content is encoded into the symbol name, and promotes them into
// real symbols with RODATA type and a payload that matches the
// encoded content.
func (l *Loader) preprocess(arch *sys.Arch, s Sym, name string) {
if name != "" && name[0] == '$' && len(name) > 5 && l.SymType(s) == 0 && len(l.Data(s)) == 0 {
x, err := strconv.ParseUint(name[5:], 16, 64)
if err != nil {
log.Panicf("failed to parse $-symbol %s: %v", s.Name, err)
log.Panicf("failed to parse $-symbol %s: %v", name, err)
}
s.Type = sym.SRODATA
s.Attr |= sym.AttrLocal
switch s.Name[:5] {
su := l.MakeSymbolUpdater(s)
su.SetType(sym.SRODATA)
su.SetLocal(true)
switch name[:5] {
case "$f32.":
if uint64(uint32(x)) != x {
log.Panicf("$-symbol %s too large: %d", s.Name, x)
log.Panicf("$-symbol %s too large: %d", name, x)
}
s.AddUint32(arch, uint32(x))
su.AddUint32(arch, uint32(x))
case "$f64.", "$i64.":
s.AddUint64(arch, x)
su.AddUint64(arch, x)
default:
log.Panicf("unrecognized $-symbol: %s", s.Name)
log.Panicf("unrecognized $-symbol: %s", name)
}
}
}
@ -1832,6 +1893,7 @@ func preprocess(arch *sys.Arch, s *sym.Symbol) {
func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
// create all Symbols first.
l.growSyms(l.NSym())
l.growSects(l.NSym())
nr := 0 // total number of sym.Reloc's we'll need
for _, o := range l.objs[1:] {
@ -1893,9 +1955,6 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
// Transfer over attributes.
l.migrateAttributes(i, s)
// Preprocess symbol. May set 'AttrLocal'.
preprocess(arch, s)
}
// load contents of defined symbols
@ -1909,8 +1968,6 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
// Resolve ABI aliases for external symbols. This is only
// needed for internal cgo linking.
// (The old code does this in deadcode, but deadcode2 doesn't
// do this.)
for _, i := range l.extReader.syms {
if s := l.Syms[i]; s != nil && s.Attr.Reachable() {
for ri := range s.R {
@ -2094,7 +2151,7 @@ func (l *Loader) PropagateLoaderChangesToSymbols(toconvert []Sym, anonVerReplace
// ExtractSymbols grabs the symbols out of the loader for work that hasn't been
// ported to the new symbol type.
func (l *Loader) ExtractSymbols(syms *sym.Symbols, rp map[*sym.Symbol]*sym.Symbol) {
func (l *Loader) ExtractSymbols(syms *sym.Symbols) {
// Add symbols to the ctxt.Syms lookup table. This explicitly skips things
// created via loader.Create (marked with versions less than zero), since
// if we tried to add these we'd wind up with collisions. We do, however,
@ -2114,13 +2171,6 @@ func (l *Loader) ExtractSymbols(syms *sym.Symbols, rp map[*sym.Symbol]*sym.Symbo
}
}
for i, s := range l.Reachparent {
if i == 0 {
continue
}
rp[l.Syms[i]] = l.Syms[s]
}
// Provide lookup functions for sym.Symbols.
l.SymLookup = func(name string, ver int) *sym.Symbol {
i := l.LookupOrCreateSym(name, ver)
@ -2204,13 +2254,15 @@ func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
name := strings.Replace(osym.Name(r.Reader), "\"\".", r.pkgprefix, -1)
t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
// NB: for the test below, we can skip most anonymous symbols
// since they will never be turned into sym.Symbols (ex:
// funcdata), however DWARF subprogram DIE symbols (which are
// nameless) will eventually need to be turned into
// sym.Symbols (with relocations), so the simplest thing to do
// is include them as part of this loop.
if name == "" && t != sym.SDWARFINFO {
continue
// since they will never be turned into sym.Symbols (eg:
// funcdata). DWARF symbols are an exception however -- we
// want to include all reachable but nameless DWARF symbols.
if name == "" {
switch t {
case sym.SDWARFINFO, sym.SDWARFRANGE, sym.SDWARFLOC, sym.SDWARFLINES:
default:
continue
}
}
ver := abiToVer(osym.ABI(), r.version)
if t == sym.SXREF {
@ -2225,32 +2277,12 @@ func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
continue
}
s := l.addNewSym(gi, name, ver, r.unit, t)
l.migrateAttributes(gi, s)
l.addNewSym(gi, name, ver, r.unit, t)
nr += r.NReloc(i)
}
return nr
}
// funcInfoSym records the sym.Symbol for a function, along with a copy
// of the corresponding goobj2.Sym and the index of its FuncInfo aux sym.
// We use this to delay populating FuncInfo until we can batch-allocate
// slices for their sub-objects.
type funcInfoSym struct {
s *sym.Symbol // sym.Symbol for a live function
osym *goobj2.Sym // object file symbol data for that function
isym int // global symbol index of FuncInfo aux sym for func
}
// funcAllocInfo records totals/counts for all functions in an objfile;
// used to help with bulk allocation of sym.Symbol sub-objects.
type funcAllocInfo struct {
symPtr uint32 // number of *sym.Symbol's needed in file slices
inlCall uint32 // number of sym.InlinedCall's needed in inltree slices
pcData uint32 // number of sym.Pcdata's needed in pdata slices
fdOff uint32 // number of int64's needed in all Funcdataoff slices
}
// 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
@ -2397,10 +2429,9 @@ func (l *Loader) migrateAttributes(src Sym, dst *sym.Symbol) {
dst.Sub = l.Syms[sub]
}
// Set sub-symbol attribute. FIXME: would be better to do away
// with this and just use l.OuterSymbol() != 0 elsewhere within
// the linker.
dst.Attr.Set(sym.AttrSubSymbol, dst.Outer != nil)
// Set sub-symbol attribute. See the comment on the AttrSubSymbol
// method for more on this, there is some tricky stuff here.
dst.Attr.Set(sym.AttrSubSymbol, l.outer[src] != 0 && l.sub[l.outer[src]] != 0)
// Copy over dynimplib, dynimpvers, extname.
if name, ok := l.extname[src]; ok {
@ -2467,10 +2498,11 @@ func loadObjFull(l *Loader, r *oReader) {
continue
}
l.migrateAttributes(gi, s)
// Be careful not to overwrite attributes set by the linker.
// Don't use the attributes from the object file.
osym := r.Sym(i)
dupok := osym.Dupok()
local := osym.Local()
makeTypelink := osym.Typelink()
size := osym.Siz()
// Symbol data
@ -2504,14 +2536,9 @@ func loadObjFull(l *Loader, r *oReader) {
}
s.File = r.pkgprefix[:len(r.pkgprefix)-1]
if dupok {
s.Attr |= sym.AttrDuplicateOK
}
if s.Size < int64(size) {
s.Size = int64(size)
}
s.Attr.Set(sym.AttrLocal, local)
s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
}
}
@ -2681,19 +2708,17 @@ func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, exts
}
}
// Now redo the assignment of text symbols to libs/units.
// 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
}
libtextp2 := []sym.LoaderSym{}
lists := [2][]sym.LoaderSym{lib.Textp2, lib.DupTextSyms2}
for i, list := range lists {
for _, s := range list {
sym := Sym(s)
if l.attrReachable.Has(sym) && !assignedToUnit.Has(sym) {
libtextp2 = append(libtextp2, s)
textp2 = append(textp2, sym)
unit := l.SymUnit(sym)
if unit != nil {
@ -2711,7 +2736,8 @@ func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, exts
}
}
}
lib.Textp2 = libtextp2
lib.Textp2 = nil
lib.DupTextSyms2 = nil
}
}

View File

@ -107,6 +107,7 @@ func (sb *SymbolBuilder) SetDynimpvers(value string) { sb.l.SetSymDynimpvers(sb.
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)
}
@ -334,6 +335,10 @@ func (sb *SymbolBuilder) SetAddrPlus(arch *sys.Arch, off int64, tgt Sym, add int
return off + int64(r.Size)
}
func (sb *SymbolBuilder) SetAddr(arch *sys.Arch, off int64, tgt Sym) int64 {
return sb.SetAddrPlus(arch, off, tgt, 0)
}
func (sb *SymbolBuilder) Addstring(str string) int64 {
sb.setReachable()
if sb.kind == 0 {
@ -405,3 +410,24 @@ func (sb *SymbolBuilder) AddSize(arch *sys.Arch, tgt Sym) int64 {
sb.setReachable()
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
}
}

View File

@ -204,7 +204,6 @@ func asmb2(ctxt *ld.Link) {
ctxt.Out.SeekSet(int64(symo))
ld.Asmelfsym(ctxt)
ctxt.Out.Flush()
ctxt.Out.Write(ld.Elfstrdat)
if ctxt.LinkMode == ld.LinkExternal {
@ -220,7 +219,6 @@ func asmb2(ctxt *ld.Link) {
ld.Asmbelf(ctxt, int64(symo))
}
ctxt.Out.Flush()
if *ld.FlagC {
fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)

View File

@ -223,7 +223,6 @@ func asmb2(ctxt *ld.Link) {
default:
if ctxt.IsELF {
ld.Asmelfsym(ctxt)
ctxt.Out.Flush()
ctxt.Out.Write(ld.Elfstrdat)
if ctxt.LinkMode == ld.LinkExternal {
@ -233,13 +232,11 @@ func asmb2(ctxt *ld.Link) {
case objabi.Hplan9:
ld.Asmplan9sym(ctxt)
ctxt.Out.Flush()
sym := ctxt.Syms.Lookup("pclntab", 0)
if sym != nil {
ld.Lcsize = int32(len(sym.P))
ctxt.Out.Write(sym.P)
ctxt.Out.Flush()
}
}
}
@ -268,7 +265,6 @@ func asmb2(ctxt *ld.Link) {
ld.Asmbelf(ctxt, int64(symo))
}
ctxt.Out.Flush()
if *ld.FlagC {
fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)

View File

@ -733,14 +733,13 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
toctramp := ldr.CreateSymForUpdate("TOC."+ldr.SymName(tramp.Sym()), 0)
toctramp.SetType(sym.SXCOFFTOC)
toctramp.SetReachable(true)
toctramp.AddAddr(ctxt.Arch, target)
toctramp.AddAddrPlus(ctxt.Arch, target, offset)
r := loader.Reloc{
Off: 0,
Type: objabi.R_ADDRPOWER_TOCREL_DS,
Size: 8, // generates 2 relocations: HA + LO
Sym: toctramp.Sym(),
Add: offset,
}
tramp.AddReloc(r)
} else {
@ -1131,7 +1130,6 @@ func asmb2(ctxt *ld.Link) {
default:
if ctxt.IsELF {
ld.Asmelfsym(ctxt)
ctxt.Out.Flush()
ctxt.Out.Write(ld.Elfstrdat)
if ctxt.LinkMode == ld.LinkExternal {
@ -1141,18 +1139,15 @@ func asmb2(ctxt *ld.Link) {
case objabi.Hplan9:
ld.Asmplan9sym(ctxt)
ctxt.Out.Flush()
sym := ctxt.Syms.Lookup("pclntab", 0)
if sym != nil {
ld.Lcsize = int32(len(sym.P))
ctxt.Out.Write(sym.P)
ctxt.Out.Flush()
}
case objabi.Haix:
// symtab must be added once sections have been created in ld.Asmbxcoff
ctxt.Out.Flush()
}
}
@ -1181,7 +1176,6 @@ func asmb2(ctxt *ld.Link) {
ld.Asmbxcoff(ctxt, int64(fileoff))
}
ctxt.Out.Flush()
if *ld.FlagC {
fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)

View File

@ -142,7 +142,6 @@ func asmb2(ctxt *ld.Link) {
ctxt.Out.SeekSet(int64(symo))
ld.Asmelfsym(ctxt)
ctxt.Out.Flush()
ctxt.Out.Write(ld.Elfstrdat)
if ctxt.LinkMode == ld.LinkExternal {
@ -157,7 +156,6 @@ func asmb2(ctxt *ld.Link) {
default:
ld.Errorf(nil, "unsupported operating system")
}
ctxt.Out.Flush()
if *ld.FlagC {
fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)

View File

@ -515,7 +515,6 @@ func asmb2(ctxt *ld.Link) {
ctxt.Out.SeekSet(int64(symo))
ld.Asmelfsym(ctxt)
ctxt.Out.Flush()
ctxt.Out.Write(ld.Elfstrdat)
if ctxt.LinkMode == ld.LinkExternal {
@ -531,7 +530,6 @@ func asmb2(ctxt *ld.Link) {
ld.Asmbelf(ctxt, int64(symo))
}
ctxt.Out.Flush()
if *ld.FlagC {
fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)

View File

@ -56,4 +56,5 @@ type Section struct {
Reloff uint64
Rellen uint64
Sym *Symbol // symbol for the section, if any
Index uint16 // each section has a unique index, used internally
}

View File

@ -187,8 +187,6 @@ func asmb2(ctxt *ld.Link) {
if !*ld.FlagS {
writeNameSec(ctxt, len(hostImports), fns)
}
ctxt.Out.Flush()
}
func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 {

View File

@ -372,53 +372,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
}
func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
var v uint32
rs := r.Xsym
if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALL {
if rs.Dynid < 0 {
ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
return false
}
v = uint32(rs.Dynid)
v |= 1 << 27 // external relocation
} else {
v = uint32(rs.Sect.Extnum)
if v == 0 {
ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type)
return false
}
}
switch r.Type {
default:
return false
case objabi.R_ADDR:
v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28
case objabi.R_CALL,
objabi.R_PCREL:
v |= 1 << 24 // pc-relative bit
v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28
}
switch r.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
return false
}
func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
@ -646,7 +600,6 @@ func asmb2(ctxt *ld.Link) {
default:
if ctxt.IsELF {
ld.Asmelfsym(ctxt)
ctxt.Out.Flush()
ctxt.Out.Write(ld.Elfstrdat)
if ctxt.LinkMode == ld.LinkExternal {
@ -656,13 +609,11 @@ func asmb2(ctxt *ld.Link) {
case objabi.Hplan9:
ld.Asmplan9sym(ctxt)
ctxt.Out.Flush()
sym := ctxt.Syms.Lookup("pclntab", 0)
if sym != nil {
ld.Lcsize = int32(len(sym.P))
ctxt.Out.Write(sym.P)
ctxt.Out.Flush()
}
case objabi.Hwindows:
@ -702,6 +653,4 @@ func asmb2(ctxt *ld.Link) {
case objabi.Hwindows:
ld.Asmbpe(ctxt)
}
ctxt.Out.Flush()
}

View File

@ -287,7 +287,7 @@ func TestBuildForTvOS(t *testing.T) {
"-fembed-bitcode",
"-framework", "CoreFoundation",
}
lib := filepath.Join("testdata", "lib.go")
lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")
tmpDir, err := ioutil.TempDir("", "go-link-TestBuildFortvOS")
if err != nil {
t.Fatal(err)
@ -308,7 +308,7 @@ func TestBuildForTvOS(t *testing.T) {
}
link := exec.Command(CC[0], CC[1:]...)
link.Args = append(link.Args, ar, filepath.Join("testdata", "main.m"))
link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))
if out, err := link.CombinedOutput(); err != nil {
t.Fatalf("%v: %v:\n%s", link.Args, err, out)
}
@ -628,3 +628,50 @@ func TestFuncAlign(t *testing.T) {
t.Errorf("unexpected output: %s\n", out)
}
}
const helloSrc = `
package main
import "fmt"
func main() { fmt.Println("hello") }
`
func TestTrampoline(t *testing.T) {
// Test that trampoline insertion works as expected.
// For stress test, we set -debugtramp=2 flag, which sets a very low
// threshold for trampoline generation, and essentially all cross-package
// calls will use trampolines.
switch runtime.GOARCH {
case "arm", "ppc64", "ppc64le":
default:
t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
}
testenv.MustHaveGoBuild(t)
tmpdir, err := ioutil.TempDir("", "TestTrampoline")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
src := filepath.Join(tmpdir, "hello.go")
err = ioutil.WriteFile(src, []byte(helloSrc), 0666)
if err != nil {
t.Fatal(err)
}
exe := filepath.Join(tmpdir, "hello.exe")
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("build failed: %v\n%s", err, out)
}
cmd = exec.Command(exe)
out, err = cmd.CombinedOutput()
if err != nil {
t.Errorf("executable failed to run: %v\n%s", err, out)
}
if string(out) != "hello\n" {
t.Errorf("unexpected output:\n%s", out)
}
}

View File

@ -315,7 +315,7 @@ func testGoLib(t *testing.T, iscgo bool) {
}
for i := range syms {
sym := &syms[i]
if sym.Type == typ && sym.Name == name && sym.CSym == csym {
if sym.Type == typ && matchSymName(name, sym.Name) && sym.CSym == csym {
if sym.Found {
t.Fatalf("duplicate symbol %s %s", sym.Type, sym.Name)
}
@ -334,6 +334,14 @@ func TestGoLib(t *testing.T) {
testGoLib(t, false)
}
// Check that a symbol has a given name, accepting both
// new and old objects.
// TODO(go115newobj): remove.
func matchSymName(symname, want string) bool {
return symname == want ||
strings.HasPrefix(symname, want+"#") // new style, with index
}
const testexec = `
package main

View File

@ -14,6 +14,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
@ -284,9 +285,11 @@ func TestDisasmGoobj(t *testing.T) {
if err != nil {
t.Fatalf("go tool compile fmthello.go: %v\n%s", err, out)
}
// TODO(go115newobj): drop old object file support.
need := []string{
"main(SB)",
"fmthello.go:6",
`main(#\d+)?\(SB\)`, // either new or old object file
`fmthello\.go:6`,
}
args = []string{
@ -302,8 +305,9 @@ func TestDisasmGoobj(t *testing.T) {
text := string(out)
ok := true
for _, s := range need {
if !strings.Contains(text, s) {
t.Errorf("disassembly missing '%s'", s)
re := regexp.MustCompile(s)
if !re.MatchString(text) {
t.Errorf("disassembly missing %q", s)
ok = false
}
}

View File

@ -328,87 +328,7 @@ func elfsetupplt(ctxt *ld.Link) {
}
func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
var v uint32
rs := r.Xsym
if r.Type == objabi.R_PCREL {
if rs.Type == sym.SHOSTOBJ {
ld.Errorf(s, "pc-relative relocation of external symbol is not supported")
return false
}
if r.Siz != 4 {
return false
}
// emit a pair of "scattered" relocations that
// resolve to the difference of section addresses of
// the symbol and the instruction
// this value is added to the field being relocated
o1 := uint32(sectoff)
o1 |= 1 << 31 // scattered bit
o1 |= ld.MACHO_ARM_RELOC_SECTDIFF << 24
o1 |= 2 << 28 // size = 4
o2 := uint32(0)
o2 |= 1 << 31 // scattered bit
o2 |= ld.MACHO_ARM_RELOC_PAIR << 24
o2 |= 2 << 28 // size = 4
out.Write32(o1)
out.Write32(uint32(ld.Symaddr(rs)))
out.Write32(o2)
out.Write32(uint32(s.Value + int64(r.Off)))
return true
}
if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALLARM {
if rs.Dynid < 0 {
ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
return false
}
v = uint32(rs.Dynid)
v |= 1 << 27 // external relocation
} else {
v = uint32(rs.Sect.Extnum)
if v == 0 {
ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type)
return false
}
}
switch r.Type {
default:
return false
case objabi.R_ADDR:
v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28
case objabi.R_CALLARM:
v |= 1 << 24 // pc-relative bit
v |= ld.MACHO_ARM_RELOC_BR24 << 28
}
switch r.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
return false
}
func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {

View File

@ -455,14 +455,9 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
} else if ctxt.HeadType == objabi.Hdarwin {
if r.Type == objabi.R_CALL {
if ctxt.LinkMode == LinkExternal && rs.Type == sym.SDYNIMPORT {
switch ctxt.Arch.Family {
case sys.AMD64:
if ctxt.Arch.Family == sys.AMD64 {
// AMD64 dynamic relocations are relative to the end of the relocation.
o += int64(r.Siz)
case sys.I386:
// I386 dynamic relocations are relative to the start of the section.
o -= int64(r.Off) // offset in symbol
o -= int64(s.Value - int64(s.Sect.Vaddr)) // offset of symbol in section
}
} else {
if rs.Type != sym.SHOSTOBJ {
@ -470,9 +465,6 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
}
o -= int64(r.Off) // relative to section offset, not symbol
}
} else if ctxt.Arch.Family == sys.ARM {
// see ../arm/asm.go:/machoreloc1
o += Symaddr(rs) - s.Value - int64(r.Off)
} else {
o += int64(r.Siz)
}

View File

@ -27,7 +27,8 @@ import (
//
// 1. direct call
// 2. through a reachable interface type
// 3. reflect.Value.Call, .Method, or reflect.Method.Func
// 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.
@ -38,9 +39,9 @@ import (
// as reachable. This is extremely conservative, but easy and correct.
//
// The third case is handled by looking to see if any of:
// - reflect.Value.Call is reachable
// - reflect.Value.Method is reachable
// - reflect.Type.Method or MethodByName is called.
// - 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.
//
@ -65,8 +66,8 @@ func deadcode(ctxt *Link) {
d.init()
d.flood()
callSym := ctxt.Syms.ROLookup("reflect.Value.Call", sym.SymVerABIInternal)
methSym := ctxt.Syms.ROLookup("reflect.Value.Method", sym.SymVerABIInternal)
methByNameSym := ctxt.Syms.ROLookup("reflect.Value.MethodByName", sym.SymVerABIInternal)
reflectSeen := false
if ctxt.DynlinkingGo() {
@ -77,7 +78,7 @@ func deadcode(ctxt *Link) {
for {
if !reflectSeen {
if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) {
if d.reflectMethod || (methSym != nil && methSym.Attr.Reachable()) || (methByNameSym != nil && methByNameSym.Attr.Reachable()) {
// Methods might be called via reflection. Give up on
// static analysis, mark all exported methods of
// all reachable types as reachable.

View File

@ -410,53 +410,7 @@ func elfreloc1(ctxt *ld.Link, r *sym.Reloc, sectoff int64) bool {
}
func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
var v uint32
rs := r.Xsym
if rs.Type == sym.SHOSTOBJ || r.Type == objabi.R_CALL {
if rs.Dynid < 0 {
ld.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
return false
}
v = uint32(rs.Dynid)
v |= 1 << 27 // external relocation
} else {
v = uint32(rs.Sect.Extnum)
if v == 0 {
ld.Errorf(s, "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Sect.Name, rs.Type, rs.Type)
return false
}
}
switch r.Type {
default:
return false
case objabi.R_ADDR:
v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28
case objabi.R_CALL,
objabi.R_PCREL:
v |= 1 << 24 // pc-relative bit
v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28
}
switch r.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
return false
}
func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {