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