1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package loader
6
7import (
8	"bytes"
9	"cmd/internal/goobj"
10	"cmd/internal/objabi"
11	"cmd/internal/sys"
12	"cmd/link/internal/sym"
13	"fmt"
14	"testing"
15)
16
17// dummyAddSym adds the named symbol to the loader as if it had been
18// read from a Go object file. Note that it allocates a global
19// index without creating an associated object reader, so one can't
20// do anything interesting with this symbol (such as look at its
21// data or relocations).
22func addDummyObjSym(t *testing.T, ldr *Loader, or *oReader, name string) Sym {
23	idx := uint32(len(ldr.objSyms))
24	st := loadState{l: ldr}
25	return st.addSym(name, 0, or, idx, nonPkgDef, &goobj.Sym{})
26}
27
28func mkLoader() *Loader {
29	er := ErrorReporter{}
30	ldr := NewLoader(0, &er)
31	er.ldr = ldr
32	return ldr
33}
34
35func TestAddMaterializedSymbol(t *testing.T) {
36	ldr := mkLoader()
37	dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
38	or := &dummyOreader
39
40	// Create some syms from a dummy object file symbol to get things going.
41	ts1 := addDummyObjSym(t, ldr, or, "type:uint8")
42	ts2 := addDummyObjSym(t, ldr, or, "mumble")
43	ts3 := addDummyObjSym(t, ldr, or, "type:string")
44
45	// Create some external symbols.
46	es1 := ldr.LookupOrCreateSym("extnew1", 0)
47	if es1 == 0 {
48		t.Fatalf("LookupOrCreateSym failed for extnew1")
49	}
50	es1x := ldr.LookupOrCreateSym("extnew1", 0)
51	if es1x != es1 {
52		t.Fatalf("LookupOrCreateSym lookup: expected %d got %d for second lookup", es1, es1x)
53	}
54	es2 := ldr.LookupOrCreateSym("go:info.type.uint8", 0)
55	if es2 == 0 {
56		t.Fatalf("LookupOrCreateSym failed for go.info.type.uint8")
57	}
58	// Create a nameless symbol
59	es3 := ldr.CreateStaticSym("")
60	if es3 == 0 {
61		t.Fatalf("CreateStaticSym failed for nameless sym")
62	}
63
64	// Grab symbol builder pointers
65	sb1 := ldr.MakeSymbolUpdater(es1)
66	sb2 := ldr.MakeSymbolUpdater(es2)
67	sb3 := ldr.MakeSymbolUpdater(es3)
68
69	// Suppose we create some more symbols, which triggers a grow.
70	// Make sure the symbol builder's payload pointer is valid,
71	// even across a grow.
72	for i := 0; i < 9999; i++ {
73		ldr.CreateStaticSym("dummy")
74	}
75
76	// Check get/set symbol type
77	es3typ := sb3.Type()
78	if es3typ != sym.Sxxx {
79		t.Errorf("SymType(es3): expected %v, got %v", sym.Sxxx, es3typ)
80	}
81	sb3.SetType(sym.SRODATA)
82	es3typ = sb3.Type()
83	if es3typ != sym.SRODATA {
84		t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
85	}
86	es3typ = ldr.SymType(es3)
87	if es3typ != sym.SRODATA {
88		t.Errorf("SymType(es3): expected %v, got %v", sym.SRODATA, es3typ)
89	}
90
91	// New symbols should not initially be reachable.
92	if ldr.AttrReachable(es1) || ldr.AttrReachable(es2) || ldr.AttrReachable(es3) {
93		t.Errorf("newly materialized symbols should not be reachable")
94	}
95
96	// ... however it should be possible to set/unset their reachability.
97	ldr.SetAttrReachable(es3, true)
98	if !ldr.AttrReachable(es3) {
99		t.Errorf("expected reachable symbol after update")
100	}
101	ldr.SetAttrReachable(es3, false)
102	if ldr.AttrReachable(es3) {
103		t.Errorf("expected unreachable symbol after update")
104	}
105
106	// Test expansion of attr bitmaps
107	for idx := 0; idx < 36; idx++ {
108		es := ldr.LookupOrCreateSym(fmt.Sprintf("zext%d", idx), 0)
109		if ldr.AttrOnList(es) {
110			t.Errorf("expected OnList after creation")
111		}
112		ldr.SetAttrOnList(es, true)
113		if !ldr.AttrOnList(es) {
114			t.Errorf("expected !OnList after update")
115		}
116		if ldr.AttrDuplicateOK(es) {
117			t.Errorf("expected DupOK after creation")
118		}
119		ldr.SetAttrDuplicateOK(es, true)
120		if !ldr.AttrDuplicateOK(es) {
121			t.Errorf("expected !DupOK after update")
122		}
123	}
124
125	sb1 = ldr.MakeSymbolUpdater(es1)
126	sb2 = ldr.MakeSymbolUpdater(es2)
127
128	// Get/set a few other attributes
129	if ldr.AttrVisibilityHidden(es3) {
130		t.Errorf("expected initially not hidden")
131	}
132	ldr.SetAttrVisibilityHidden(es3, true)
133	if !ldr.AttrVisibilityHidden(es3) {
134		t.Errorf("expected hidden after update")
135	}
136
137	// Test get/set symbol value.
138	toTest := []Sym{ts2, es3}
139	for i, s := range toTest {
140		if v := ldr.SymValue(s); v != 0 {
141			t.Errorf("ldr.Value(%d): expected 0 got %d\n", s, v)
142		}
143		nv := int64(i + 101)
144		ldr.SetSymValue(s, nv)
145		if v := ldr.SymValue(s); v != nv {
146			t.Errorf("ldr.SetValue(%d,%d): expected %d got %d\n", s, nv, nv, v)
147		}
148	}
149
150	// Check/set alignment
151	es3al := ldr.SymAlign(es3)
152	if es3al != 0 {
153		t.Errorf("SymAlign(es3): expected 0, got %d", es3al)
154	}
155	ldr.SetSymAlign(es3, 128)
156	es3al = ldr.SymAlign(es3)
157	if es3al != 128 {
158		t.Errorf("SymAlign(es3): expected 128, got %d", es3al)
159	}
160
161	// Add some relocations to the new symbols.
162	r1, _ := sb1.AddRel(objabi.R_ADDR)
163	r1.SetOff(0)
164	r1.SetSiz(1)
165	r1.SetSym(ts1)
166	r2, _ := sb1.AddRel(objabi.R_CALL)
167	r2.SetOff(3)
168	r2.SetSiz(8)
169	r2.SetSym(ts2)
170	r3, _ := sb2.AddRel(objabi.R_USETYPE)
171	r3.SetOff(7)
172	r3.SetSiz(1)
173	r3.SetSym(ts3)
174
175	// Add some data to the symbols.
176	d1 := []byte{1, 2, 3}
177	d2 := []byte{4, 5, 6, 7}
178	sb1.AddBytes(d1)
179	sb2.AddBytes(d2)
180
181	// Now invoke the usual loader interfaces to make sure
182	// we're getting the right things back for these symbols.
183	// First relocations...
184	expRel := [][]Reloc{{r1, r2}, {r3}}
185	for k, sb := range []*SymbolBuilder{sb1, sb2} {
186		rsl := sb.Relocs()
187		exp := expRel[k]
188		if !sameRelocSlice(&rsl, exp) {
189			t.Errorf("expected relocs %v, got %v", exp, rsl)
190		}
191	}
192
193	// ... then data.
194	dat := sb2.Data()
195	if !bytes.Equal(dat, d2) {
196		t.Errorf("expected es2 data %v, got %v", d2, dat)
197	}
198
199	// Nameless symbol should still be nameless.
200	es3name := ldr.SymName(es3)
201	if "" != es3name {
202		t.Errorf("expected es3 name of '', got '%s'", es3name)
203	}
204
205	// Read value of materialized symbol.
206	es1val := sb1.Value()
207	if 0 != es1val {
208		t.Errorf("expected es1 value of 0, got %v", es1val)
209	}
210
211	// Test other misc methods
212	irm := ldr.IsReflectMethod(es1)
213	if 0 != es1val {
214		t.Errorf("expected IsReflectMethod(es1) value of 0, got %v", irm)
215	}
216}
217
218func sameRelocSlice(s1 *Relocs, s2 []Reloc) bool {
219	if s1.Count() != len(s2) {
220		return false
221	}
222	for i := 0; i < s1.Count(); i++ {
223		r1 := s1.At(i)
224		r2 := &s2[i]
225		if r1.Sym() != r2.Sym() ||
226			r1.Type() != r2.Type() ||
227			r1.Off() != r2.Off() ||
228			r1.Add() != r2.Add() ||
229			r1.Siz() != r2.Siz() {
230			return false
231		}
232	}
233	return true
234}
235
236type addFunc func(l *Loader, s Sym, s2 Sym) Sym
237
238func mkReloc(l *Loader, typ objabi.RelocType, off int32, siz uint8, add int64, sym Sym) Reloc {
239	r := Reloc{&goobj.Reloc{}, l.extReader, l}
240	r.SetType(typ)
241	r.SetOff(off)
242	r.SetSiz(siz)
243	r.SetAdd(add)
244	r.SetSym(sym)
245	return r
246}
247
248func TestAddDataMethods(t *testing.T) {
249	ldr := mkLoader()
250	dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
251	or := &dummyOreader
252
253	// Populate loader with some symbols.
254	addDummyObjSym(t, ldr, or, "type:uint8")
255	ldr.LookupOrCreateSym("hello", 0)
256
257	arch := sys.ArchAMD64
258	var testpoints = []struct {
259		which       string
260		addDataFunc addFunc
261		expData     []byte
262		expKind     sym.SymKind
263		expRel      []Reloc
264	}{
265		{
266			which: "AddUint8",
267			addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
268				sb := l.MakeSymbolUpdater(s)
269				sb.AddUint8('a')
270				return s
271			},
272			expData: []byte{'a'},
273			expKind: sym.SDATA,
274		},
275		{
276			which: "AddUintXX",
277			addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
278				sb := l.MakeSymbolUpdater(s)
279				sb.AddUintXX(arch, 25185, 2)
280				return s
281			},
282			expData: []byte{'a', 'b'},
283			expKind: sym.SDATA,
284		},
285		{
286			which: "SetUint8",
287			addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
288				sb := l.MakeSymbolUpdater(s)
289				sb.AddUint8('a')
290				sb.AddUint8('b')
291				sb.SetUint8(arch, 1, 'c')
292				return s
293			},
294			expData: []byte{'a', 'c'},
295			expKind: sym.SDATA,
296		},
297		{
298			which: "AddString",
299			addDataFunc: func(l *Loader, s Sym, _ Sym) Sym {
300				sb := l.MakeSymbolUpdater(s)
301				sb.Addstring("hello")
302				return s
303			},
304			expData: []byte{'h', 'e', 'l', 'l', 'o', 0},
305			expKind: sym.SNOPTRDATA,
306		},
307		{
308			which: "AddAddrPlus",
309			addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
310				sb := l.MakeSymbolUpdater(s)
311				sb.AddAddrPlus(arch, s2, 3)
312				return s
313			},
314			expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
315			expKind: sym.SDATA,
316			expRel:  []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 8, 3, 6)},
317		},
318		{
319			which: "AddAddrPlus4",
320			addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
321				sb := l.MakeSymbolUpdater(s)
322				sb.AddAddrPlus4(arch, s2, 3)
323				return s
324			},
325			expData: []byte{0, 0, 0, 0},
326			expKind: sym.SDATA,
327			expRel:  []Reloc{mkReloc(ldr, objabi.R_ADDR, 0, 4, 3, 7)},
328		},
329		{
330			which: "AddCURelativeAddrPlus",
331			addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
332				sb := l.MakeSymbolUpdater(s)
333				sb.AddCURelativeAddrPlus(arch, s2, 7)
334				return s
335			},
336			expData: []byte{0, 0, 0, 0, 0, 0, 0, 0},
337			expKind: sym.SDATA,
338			expRel:  []Reloc{mkReloc(ldr, objabi.R_ADDRCUOFF, 0, 8, 7, 8)},
339		},
340		{
341			which: "AddPEImageRelativeAddrPlus",
342			addDataFunc: func(l *Loader, s Sym, s2 Sym) Sym {
343				sb := l.MakeSymbolUpdater(s)
344				sb.AddPEImageRelativeAddrPlus(arch, s2, 3)
345				return s
346			},
347			expData: []byte{0, 0, 0, 0},
348			expKind: sym.SDATA,
349			expRel:  []Reloc{mkReloc(ldr, objabi.R_PEIMAGEOFF, 0, 4, 3, 9)},
350		},
351	}
352
353	var pmi Sym
354	for k, tp := range testpoints {
355		name := fmt.Sprintf("new%d", k+1)
356		mi := ldr.LookupOrCreateSym(name, 0)
357		if mi == 0 {
358			t.Fatalf("LookupOrCreateSym failed for '" + name + "'")
359		}
360		mi = tp.addDataFunc(ldr, mi, pmi)
361		if ldr.SymType(mi) != tp.expKind {
362			t.Errorf("testing Loader.%s: expected kind %s got %s",
363				tp.which, tp.expKind, ldr.SymType(mi))
364		}
365		if !bytes.Equal(ldr.Data(mi), tp.expData) {
366			t.Errorf("testing Loader.%s: expected data %v got %v",
367				tp.which, tp.expData, ldr.Data(mi))
368		}
369		relocs := ldr.Relocs(mi)
370		if !sameRelocSlice(&relocs, tp.expRel) {
371			t.Fatalf("testing Loader.%s: got relocslice %+v wanted %+v",
372				tp.which, relocs, tp.expRel)
373		}
374		pmi = mi
375	}
376}
377
378func TestOuterSub(t *testing.T) {
379	ldr := mkLoader()
380	dummyOreader := oReader{version: -1, syms: make([]Sym, 100)}
381	or := &dummyOreader
382
383	// Populate loader with some symbols.
384	addDummyObjSym(t, ldr, or, "type:uint8")
385	es1 := ldr.LookupOrCreateSym("outer", 0)
386	ldr.MakeSymbolUpdater(es1).SetSize(101)
387	es2 := ldr.LookupOrCreateSym("sub1", 0)
388	es3 := ldr.LookupOrCreateSym("sub2", 0)
389	es4 := ldr.LookupOrCreateSym("sub3", 0)
390	es5 := ldr.LookupOrCreateSym("sub4", 0)
391	es6 := ldr.LookupOrCreateSym("sub5", 0)
392
393	// Should not have an outer sym initially
394	if ldr.OuterSym(es1) != 0 {
395		t.Errorf("es1 outer sym set ")
396	}
397	if ldr.SubSym(es2) != 0 {
398		t.Errorf("es2 outer sym set ")
399	}
400
401	// Establish first outer/sub relationship
402	ldr.AddInteriorSym(es1, es2)
403	if ldr.OuterSym(es1) != 0 {
404		t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
405	}
406	if ldr.OuterSym(es2) != es1 {
407		t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
408	}
409	if ldr.SubSym(es1) != es2 {
410		t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es2)
411	}
412	if ldr.SubSym(es2) != 0 {
413		t.Errorf("ldr.SubSym(es2) got %d wanted %d", ldr.SubSym(es2), 0)
414	}
415
416	// Establish second outer/sub relationship
417	ldr.AddInteriorSym(es1, es3)
418	if ldr.OuterSym(es1) != 0 {
419		t.Errorf("ldr.OuterSym(es1) got %d wanted %d", ldr.OuterSym(es1), 0)
420	}
421	if ldr.OuterSym(es2) != es1 {
422		t.Errorf("ldr.OuterSym(es2) got %d wanted %d", ldr.OuterSym(es2), es1)
423	}
424	if ldr.OuterSym(es3) != es1 {
425		t.Errorf("ldr.OuterSym(es3) got %d wanted %d", ldr.OuterSym(es3), es1)
426	}
427	if ldr.SubSym(es1) != es3 {
428		t.Errorf("ldr.SubSym(es1) got %d wanted %d", ldr.SubSym(es1), es3)
429	}
430	if ldr.SubSym(es3) != es2 {
431		t.Errorf("ldr.SubSym(es3) got %d wanted %d", ldr.SubSym(es3), es2)
432	}
433
434	// Some more
435	ldr.AddInteriorSym(es1, es4)
436	ldr.AddInteriorSym(es1, es5)
437	ldr.AddInteriorSym(es1, es6)
438
439	// Set values.
440	ldr.SetSymValue(es2, 7)
441	ldr.SetSymValue(es3, 1)
442	ldr.SetSymValue(es4, 13)
443	ldr.SetSymValue(es5, 101)
444	ldr.SetSymValue(es6, 3)
445
446	// Sort
447	news := ldr.SortSub(es1)
448	if news != es3 {
449		t.Errorf("ldr.SortSub leader got %d wanted %d", news, es3)
450	}
451	pv := int64(-1)
452	count := 0
453	for ss := ldr.SubSym(es1); ss != 0; ss = ldr.SubSym(ss) {
454		v := ldr.SymValue(ss)
455		if v <= pv {
456			t.Errorf("ldr.SortSub sortfail at %d: val %d >= prev val %d",
457				ss, v, pv)
458		}
459		pv = v
460		count++
461	}
462	if count != 5 {
463		t.Errorf("expected %d in sub list got %d", 5, count)
464	}
465}
466