mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Currently, relocation type is stored as uint8 in object files, as
Go relocations do not exceed 255. In the linker, however, it is
used as a 16-bit type, because external relocations can exceed
255. The linker has to store the extra byte in a side table. This
complicates many things.
Just store it as uint16 in object files. This simplifies things,
with a small cost of increasing the object file sizes.
before after
hello.o 1672 1678
runtime.a 7927784 8056194
Change-Id: I313cf44ad0b8b3b76e35055ae55d911ff35e3158
Reviewed-on: https://go-review.googlesource.com/c/go/+/268477
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
455 lines
12 KiB
Go
455 lines
12 KiB
Go
// Copyright 2019 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package loader
|
|
|
|
import (
|
|
"bytes"
|
|
"cmd/internal/goobj"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/sys"
|
|
"cmd/link/internal/sym"
|
|
"fmt"
|
|
"testing"
|
|
)
|
|
|
|
// dummyAddSym adds the named symbol to the loader as if it had been
|
|
// read from a Go object file. Note that it allocates a global
|
|
// index without creating an associated object reader, so one can't
|
|
// do anything interesting with this symbol (such as look at its
|
|
// data or relocations).
|
|
func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym {
|
|
idx := uint32(len(ldr.objSyms))
|
|
st := loadState{l: ldr}
|
|
return st.addSym(name, 0, or, idx, nonPkgDef, &goobj.Sym{})
|
|
}
|
|
|
|
func mkLoader() *Loader {
|
|
edummy := func(str string, off int) {}
|
|
er := ErrorReporter{}
|
|
ldr := NewLoader(0, edummy, &er)
|
|
er.ldr = ldr
|
|
return ldr
|
|
}
|
|
|
|
func TestAddMaterializedSymbol(t *testing.T) {
|
|
ldr := mkLoader()
|
|
dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
|
|
or := &dummyOreader
|
|
|
|
// Create some syms from a dummy object file symbol to get things going.
|
|
ts1 := addDummyObjSym(t, ldr, or, "type.uint8")
|
|
ts2 := addDummyObjSym(t, ldr, or, "mumble")
|
|
ts3 := addDummyObjSym(t, ldr, or, "type.string")
|
|
|
|
// Create some external symbols.
|
|
es1 := ldr.LookupOrCreateSym("extnew1", 0)
|
|
if es1 == 0 {
|
|
t.Fatalf("LookupOrCreateSym failed for extnew1")
|
|
}
|
|
es1x := ldr.LookupOrCreateSym("extnew1", 0)
|
|
if es1x != es1 {
|
|
t.Fatalf("LookupOrCreateSym lookup: expected %d got %d for second lookup", es1, es1x)
|
|
}
|
|
es2 := ldr.LookupOrCreateSym("go.info.type.uint8", 0)
|
|
if es2 == 0 {
|
|
t.Fatalf("LookupOrCreateSym failed for go.info.type.uint8")
|
|
}
|
|
// Create a nameless symbol
|
|
es3 := ldr.CreateStaticSym("")
|
|
if es3 == 0 {
|
|
t.Fatalf("CreateStaticSym failed for nameless sym")
|
|
}
|
|
|
|
// Grab symbol builder pointers
|
|
sb1 := ldr.MakeSymbolUpdater(es1)
|
|
sb2 := ldr.MakeSymbolUpdater(es2)
|
|
sb3 := ldr.MakeSymbolUpdater(es3)
|
|
|
|
// Suppose we create some more symbols, which triggers a grow.
|
|
// Make sure the symbol builder's payload pointer is valid,
|
|
// even across a grow.
|
|
for i := 0; i < 9999; i++ {
|
|
ldr.CreateStaticSym("dummy")
|
|
}
|
|
|
|
// Check get/set symbol type
|
|
es3typ := sb3.Type()
|
|
if es3typ != sym.Sxxx {
|
|
t.Errorf("SymType(es3): expected %v, got %v", sym.Sxxx, es3typ)
|
|
}
|
|
sb3.SetType(sym.SRODATA)
|
|
es3typ = sb3.Type()
|
|
if es3typ != sym.SRODATA {
|
|
t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
|
|
}
|
|
es3typ = ldr.SymType(es3)
|
|
if es3typ != sym.SRODATA {
|
|
t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
|
|
}
|
|
|
|
// New symbols should not initially be reachable.
|
|
if ldr.AttrReachable(es1) || ldr.AttrReachable(es2) || ldr.AttrReachable(es3) {
|
|
t.Errorf("newly materialized symbols should not be reachable")
|
|
}
|
|
|
|
// ... however it should be possible to set/unset their reachability.
|
|
ldr.SetAttrReachable(es3, true)
|
|
if !ldr.AttrReachable(es3) {
|
|
t.Errorf("expected reachable symbol after update")
|
|
}
|
|
ldr.SetAttrReachable(es3, false)
|
|
if ldr.AttrReachable(es3) {
|
|
t.Errorf("expected unreachable symbol after update")
|
|
}
|
|
|
|
// Test expansion of attr bitmaps
|
|
for idx := 0; idx < 36; idx++ {
|
|
es := ldr.LookupOrCreateSym(fmt.Sprintf("zext%d", idx), 0)
|
|
if ldr.AttrOnList(es) {
|
|
t.Errorf("expected OnList after creation")
|
|
}
|
|
ldr.SetAttrOnList(es, true)
|
|
if !ldr.AttrOnList(es) {
|
|
t.Errorf("expected !OnList after update")
|
|
}
|
|
if ldr.AttrDuplicateOK(es) {
|
|
t.Errorf("expected DupOK after creation")
|
|
}
|
|
ldr.SetAttrDuplicateOK(es, true)
|
|
if !ldr.AttrDuplicateOK(es) {
|
|
t.Errorf("expected !DupOK after update")
|
|
}
|
|
}
|
|
|
|
sb1 = ldr.MakeSymbolUpdater(es1)
|
|
sb2 = ldr.MakeSymbolUpdater(es2)
|
|
|
|
// Get/set a few other attributes
|
|
if ldr.AttrVisibilityHidden(es3) {
|
|
t.Errorf("expected initially not hidden")
|
|
}
|
|
ldr.SetAttrVisibilityHidden(es3, true)
|
|
if !ldr.AttrVisibilityHidden(es3) {
|
|
t.Errorf("expected hidden after update")
|
|
}
|
|
|
|
// Test get/set symbol value.
|
|
toTest := []Sym{ts2, es3}
|
|
for i, s := range toTest {
|
|
if v := ldr.SymValue(s); v != 0 {
|
|
t.Errorf("ldr.Value(%d): expected 0 got %d\n", s, v)
|
|
}
|
|
nv := int64(i + 101)
|
|
ldr.SetSymValue(s, nv)
|
|
if v := ldr.SymValue(s); v != nv {
|
|
t.Errorf("ldr.SetValue(%d,%d): expected %d got %d\n", s, nv, nv, v)
|
|
}
|
|
}
|
|
|
|
// Check/set alignment
|
|
es3al := ldr.SymAlign(es3)
|
|
if es3al != 0 {
|
|
t.Errorf("SymAlign(es3): expected 0, got %d", es3al)
|
|
}
|
|
ldr.SetSymAlign(es3, 128)
|
|
es3al = ldr.SymAlign(es3)
|
|
if es3al != 128 {
|
|
t.Errorf("SymAlign(es3): expected 128, got %d", es3al)
|
|
}
|
|
|
|
// Add some relocations to the new symbols.
|
|
r1, _ := sb1.AddRel(objabi.R_ADDR)
|
|
r1.SetOff(0)
|
|
r1.SetSiz(1)
|
|
r1.SetSym(ts1)
|
|
r2, _ := sb1.AddRel(objabi.R_CALL)
|
|
r2.SetOff(3)
|
|
r2.SetSiz(8)
|
|
r2.SetSym(ts2)
|
|
r3, _ := sb2.AddRel(objabi.R_USETYPE)
|
|
r3.SetOff(7)
|
|
r3.SetSiz(1)
|
|
r3.SetSym(ts3)
|
|
|
|
// Add some data to the symbols.
|
|
d1 := []byte{1, 2, 3}
|
|
d2 := []byte{4, 5, 6, 7}
|
|
sb1.AddBytes(d1)
|
|
sb2.AddBytes(d2)
|
|
|
|
// Now invoke the usual loader interfaces to make sure
|
|
// we're getting the right things back for these symbols.
|
|
// First relocations...
|
|
expRel := [][]Reloc{{r1, r2}, {r3}}
|
|
for k, sb := range []*SymbolBuilder{sb1, sb2} {
|
|
rsl := sb.Relocs()
|
|
exp := expRel[k]
|
|
if !sameRelocSlice(&rsl, exp) {
|
|
t.Errorf("expected relocs %v, got %v", exp, rsl)
|
|
}
|
|
}
|
|
|
|
// ... then data.
|
|
dat := sb2.Data()
|
|
if bytes.Compare(dat, d2) != 0 {
|
|
t.Errorf("expected es2 data %v, got %v", d2, dat)
|
|
}
|
|
|
|
// Nameless symbol should still be nameless.
|
|
es3name := ldr.RawSymName(es3)
|
|
if "" != es3name {
|
|
t.Errorf("expected es3 name of '', got '%s'", es3name)
|
|
}
|
|
|
|
// Read value of materialized symbol.
|
|
es1val := sb1.Value()
|
|
if 0 != es1val {
|
|
t.Errorf("expected es1 value of 0, got %v", es1val)
|
|
}
|
|
|
|
// Test other misc methods
|
|
irm := ldr.IsReflectMethod(es1)
|
|
if 0 != es1val {
|
|
t.Errorf("expected IsReflectMethod(es1) value of 0, got %v", irm)
|
|
}
|
|
}
|
|
|
|
func sameRelocSlice(s1 *Relocs, s2 []Reloc) bool {
|
|
if s1.Count() != len(s2) {
|
|
return false
|
|
}
|
|
for i := 0; i < s1.Count(); i++ {
|
|
r1 := s1.At(i)
|
|
r2 := &s2[i]
|
|
if r1.Sym() != r2.Sym() ||
|
|
r1.Type() != r2.Type() ||
|
|
r1.Off() != r2.Off() ||
|
|
r1.Add() != r2.Add() ||
|
|
r1.Siz() != r2.Siz() {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
type addFunc func(l *Loader, s Sym, s2 Sym) Sym
|
|
|
|
func mkReloc(l *Loader, typ objabi.RelocType, off int32, siz uint8, add int64, sym Sym) Reloc {
|
|
r := Reloc{&goobj.Reloc{}, l.extReader, l}
|
|
r.SetType(typ)
|
|
r.SetOff(off)
|
|
r.SetSiz(siz)
|
|
r.SetAdd(add)
|
|
r.SetSym(sym)
|
|
return r
|
|
}
|
|
|
|
func TestAddDataMethods(t *testing.T) {
|
|
ldr := mkLoader()
|
|
dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
|
|
or := &dummyOreader
|
|
|
|
// Populate loader with some symbols.
|
|
addDummyObjSym(t, ldr, or, "type.uint8")
|
|
ldr.LookupOrCreateSym("hello", 0)
|
|
|
|
arch := sys.ArchAMD64
|
|
var testpoints = []struct {
|
|
which string
|
|
addDataFunc addFunc
|
|
expData []byte
|
|
expKind sym.SymKind
|
|
expRel []Reloc
|
|
}{
|
|
{
|
|
which: "AddUint8",
|
|
addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
|
|
sb := l.MakeSymbolUpdater(s)
|
|
sb.AddUint8('a')
|
|
return s
|
|
},
|
|
expData: []byte{'a'},
|
|
expKind: sym.SDATA,
|
|
},
|
|
{
|
|
which: "AddUintXX",
|
|
addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
|
|
sb := l.MakeSymbolUpdater(s)
|
|
sb.AddUintXX(arch, 25185, 2)
|
|
return s
|
|
},
|
|
expData: []byte{'a', 'b'},
|
|
expKind: sym.SDATA,
|
|
},
|
|
{
|
|
which: "SetUint8",
|
|
addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
|
|
sb := l.MakeSymbolUpdater(s)
|
|
sb.AddUint8('a')
|
|
sb.AddUint8('b')
|
|
sb.SetUint8(arch, 1, 'c')
|
|
return s
|
|
},
|
|
expData: []byte{'a', 'c'},
|
|
expKind: sym.SDATA,
|
|
},
|
|
{
|
|
which: "AddString",
|
|
addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
|
|
sb := l.MakeSymbolUpdater(s)
|
|
sb.Addstring("hello")
|
|
return s
|
|
},
|
|
expData: []byte{'h', 'e', 'l', 'l', 'o', 0},
|
|
expKind: sym.SNOPTRDATA,
|
|
},
|
|
{
|
|
which: "AddAddrPlus",
|
|
addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
|
|
sb := l.MakeSymbolUpdater(s)
|
|
sb.AddAddrPlus(arch, s2, 3)
|
|
return s
|
|
},
|
|
expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
|
|
expKind: sym.SDATA,
|
|
expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 8, 3, 6)},
|
|
},
|
|
{
|
|
which: "AddAddrPlus4",
|
|
addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
|
|
sb := l.MakeSymbolUpdater(s)
|
|
sb.AddAddrPlus4(arch, s2, 3)
|
|
return s
|
|
},
|
|
expData: []byte{0, 0, 0, 0},
|
|
expKind: sym.SDATA,
|
|
expRel: []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 4, 3, 7)},
|
|
},
|
|
{
|
|
which: "AddCURelativeAddrPlus",
|
|
addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
|
|
sb := l.MakeSymbolUpdater(s)
|
|
sb.AddCURelativeAddrPlus(arch, s2, 7)
|
|
return s
|
|
},
|
|
expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
|
|
expKind: sym.SDATA,
|
|
expRel: []Reloc{mkReloc(ldr, objabi.R_ADDRCUOFF, 0, 8, 7, 8)},
|
|
},
|
|
}
|
|
|
|
var pmi Sym
|
|
for k, tp := range testpoints {
|
|
name := fmt.Sprintf("new%d", k+1)
|
|
mi := ldr.LookupOrCreateSym(name, 0)
|
|
if mi == 0 {
|
|
t.Fatalf("LookupOrCreateSym failed for '" + name + "'")
|
|
}
|
|
mi = tp.addDataFunc(ldr, mi, pmi)
|
|
if ldr.SymType(mi) != tp.expKind {
|
|
t.Errorf("testing Loader.%s: expected kind %s got %s",
|
|
tp.which, tp.expKind, ldr.SymType(mi))
|
|
}
|
|
if bytes.Compare(ldr.Data(mi), tp.expData) != 0 {
|
|
t.Errorf("testing Loader.%s: expected data %v got %v",
|
|
tp.which, tp.expData, ldr.Data(mi))
|
|
}
|
|
relocs := ldr.Relocs(mi)
|
|
if !sameRelocSlice(&relocs, tp.expRel) {
|
|
t.Fatalf("testing Loader.%s: got relocslice %+v wanted %+v",
|
|
tp.which, relocs, tp.expRel)
|
|
}
|
|
pmi = mi
|
|
}
|
|
}
|
|
|
|
func TestOuterSub(t *testing.T) {
|
|
ldr := mkLoader()
|
|
dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
|
|
or := &dummyOreader
|
|
|
|
// Populate loader with some symbols.
|
|
addDummyObjSym(t, ldr, or, "type.uint8")
|
|
es1 := ldr.LookupOrCreateSym("outer", 0)
|
|
ldr.MakeSymbolUpdater(es1).SetSize(101)
|
|
es2 := ldr.LookupOrCreateSym("sub1", 0)
|
|
es3 := ldr.LookupOrCreateSym("sub2", 0)
|
|
es4 := ldr.LookupOrCreateSym("sub3", 0)
|
|
es5 := ldr.LookupOrCreateSym("sub4", 0)
|
|
es6 := ldr.LookupOrCreateSym("sub5", 0)
|
|
|
|
// Should not have an outer sym initially
|
|
if ldr.OuterSym(es1) != 0 {
|
|
t.Errorf("es1 outer sym set ")
|
|
}
|
|
if ldr.SubSym(es2) != 0 {
|
|
t.Errorf("es2 outer sym set ")
|
|
}
|
|
|
|
// Establish first outer/sub relationship
|
|
ldr.AddInteriorSym(es1, es2)
|
|
if ldr.OuterSym(es1) != 0 {
|
|
t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
|
|
}
|
|
if ldr.OuterSym(es2) != es1 {
|
|
t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
|
|
}
|
|
if ldr.SubSym(es1) != es2 {
|
|
t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es2)
|
|
}
|
|
if ldr.SubSym(es2) != 0 {
|
|
t.Errorf("ldr.SubSym(es2) got %d wanted %d", ldr.SubSym(es2), 0)
|
|
}
|
|
|
|
// Establish second outer/sub relationship
|
|
ldr.AddInteriorSym(es1, es3)
|
|
if ldr.OuterSym(es1) != 0 {
|
|
t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
|
|
}
|
|
if ldr.OuterSym(es2) != es1 {
|
|
t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
|
|
}
|
|
if ldr.OuterSym(es3) != es1 {
|
|
t.Errorf("ldr.OuterSym(es3) got %d wanted %d", ldr.OuterSym(es3), es1)
|
|
}
|
|
if ldr.SubSym(es1) != es3 {
|
|
t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es3)
|
|
}
|
|
if ldr.SubSym(es3) != es2 {
|
|
t.Errorf("ldr.SubSym(es3) got %d wanted %d", ldr.SubSym(es3), es2)
|
|
}
|
|
|
|
// Some more
|
|
ldr.AddInteriorSym(es1, es4)
|
|
ldr.AddInteriorSym(es1, es5)
|
|
ldr.AddInteriorSym(es1, es6)
|
|
|
|
// Set values.
|
|
ldr.SetSymValue(es2, 7)
|
|
ldr.SetSymValue(es3, 1)
|
|
ldr.SetSymValue(es4, 13)
|
|
ldr.SetSymValue(es5, 101)
|
|
ldr.SetSymValue(es6, 3)
|
|
|
|
// Sort
|
|
news := ldr.SortSub(es1)
|
|
if news != es3 {
|
|
t.Errorf("ldr.SortSub leader got %d wanted %d", news, es3)
|
|
}
|
|
pv := int64(-1)
|
|
count := 0
|
|
for ss := ldr.SubSym(es1); ss != 0; ss = ldr.SubSym(ss) {
|
|
v := ldr.SymValue(ss)
|
|
if v <= pv {
|
|
t.Errorf("ldr.SortSub sortfail at %d: val %d >= prev val %d",
|
|
ss, v, pv)
|
|
}
|
|
pv = v
|
|
count++
|
|
}
|
|
if count != 5 {
|
|
t.Errorf("expected %d in sub list got %d", 5, count)
|
|
}
|
|
}
|