1// Copyright 2013 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 5// Writing of Go object files. 6 7package obj 8 9import ( 10 "bufio" 11 "cmd/internal/bio" 12 "cmd/internal/dwarf" 13 "cmd/internal/objabi" 14 "cmd/internal/sys" 15 "fmt" 16 "log" 17 "path/filepath" 18 "sort" 19 "strings" 20 "sync" 21) 22 23// objWriter writes Go object files. 24type objWriter struct { 25 wr *bufio.Writer 26 ctxt *Link 27 // Temporary buffer for zigzag int writing. 28 varintbuf [10]uint8 29 30 // Number of objects written of each type. 31 nRefs int 32 nData int 33 nReloc int 34 nPcdata int 35 nFuncdata int 36 nFile int 37 38 pkgpath string // the package import path (escaped), "" if unknown 39} 40 41func (w *objWriter) addLengths(s *LSym) { 42 w.nData += len(s.P) 43 w.nReloc += len(s.R) 44 45 if s.Type != objabi.STEXT { 46 return 47 } 48 49 pc := &s.Func.Pcln 50 51 data := 0 52 data += len(pc.Pcsp.P) 53 data += len(pc.Pcfile.P) 54 data += len(pc.Pcline.P) 55 data += len(pc.Pcinline.P) 56 for _, pcd := range pc.Pcdata { 57 data += len(pcd.P) 58 } 59 60 w.nData += data 61 w.nPcdata += len(pc.Pcdata) 62 63 w.nFuncdata += len(pc.Funcdataoff) 64 w.nFile += len(pc.File) 65} 66 67func (w *objWriter) writeLengths() { 68 w.writeInt(int64(w.nData)) 69 w.writeInt(int64(w.nReloc)) 70 w.writeInt(int64(w.nPcdata)) 71 w.writeInt(int64(0)) // TODO: remove at next object file rev 72 w.writeInt(int64(w.nFuncdata)) 73 w.writeInt(int64(w.nFile)) 74} 75 76func newObjWriter(ctxt *Link, b *bufio.Writer, pkgpath string) *objWriter { 77 return &objWriter{ 78 ctxt: ctxt, 79 wr: b, 80 pkgpath: objabi.PathToPrefix(pkgpath), 81 } 82} 83 84func WriteObjFile(ctxt *Link, bout *bio.Writer, pkgpath string) { 85 if ctxt.Flag_newobj { 86 WriteObjFile2(ctxt, bout, pkgpath) 87 return 88 } 89 90 b := bout.Writer 91 w := newObjWriter(ctxt, b, pkgpath) 92 93 // Magic header 94 w.wr.WriteString("\x00go114ld") 95 96 // Version 97 w.wr.WriteByte(1) 98 99 // Autolib 100 for _, pkg := range ctxt.Imports { 101 w.writeString(pkg) 102 } 103 w.writeString("") 104 105 // DWARF File Table 106 fileTable := ctxt.PosTable.DebugLinesFileTable() 107 w.writeInt(int64(len(fileTable))) 108 for _, str := range fileTable { 109 w.writeString(filepath.ToSlash(str)) 110 } 111 112 // Symbol references 113 for _, s := range ctxt.Text { 114 w.writeRefs(s) 115 w.addLengths(s) 116 } 117 118 if ctxt.Headtype == objabi.Haix { 119 // Data must be sorted to keep a constant order in TOC symbols. 120 // As they are created during Progedit, two symbols can be switched between 121 // two different compilations. Therefore, BuildID will be different. 122 // TODO: find a better place and optimize to only sort TOC symbols 123 sort.Slice(ctxt.Data, func(i, j int) bool { 124 return ctxt.Data[i].Name < ctxt.Data[j].Name 125 }) 126 } 127 128 for _, s := range ctxt.Data { 129 w.writeRefs(s) 130 w.addLengths(s) 131 } 132 for _, s := range ctxt.ABIAliases { 133 w.writeRefs(s) 134 w.addLengths(s) 135 } 136 // End symbol references 137 w.wr.WriteByte(0xff) 138 139 // Lengths 140 w.writeLengths() 141 142 // Data block 143 for _, s := range ctxt.Text { 144 w.wr.Write(s.P) 145 pc := &s.Func.Pcln 146 w.wr.Write(pc.Pcsp.P) 147 w.wr.Write(pc.Pcfile.P) 148 w.wr.Write(pc.Pcline.P) 149 w.wr.Write(pc.Pcinline.P) 150 for _, pcd := range pc.Pcdata { 151 w.wr.Write(pcd.P) 152 } 153 } 154 for _, s := range ctxt.Data { 155 if len(s.P) > 0 { 156 switch s.Type { 157 case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS: 158 ctxt.Diag("cannot provide data for %v sym %v", s.Type, s.Name) 159 } 160 } 161 w.wr.Write(s.P) 162 } 163 164 // Symbols 165 for _, s := range ctxt.Text { 166 w.writeSym(s) 167 } 168 for _, s := range ctxt.Data { 169 w.writeSym(s) 170 } 171 for _, s := range ctxt.ABIAliases { 172 w.writeSym(s) 173 } 174 175 // Magic footer 176 w.wr.WriteString("\xffgo114ld") 177} 178 179// Symbols are prefixed so their content doesn't get confused with the magic footer. 180const symPrefix = 0xfe 181 182func (w *objWriter) writeRef(s *LSym, isPath bool) { 183 if s == nil || s.RefIdx != 0 { 184 return 185 } 186 w.wr.WriteByte(symPrefix) 187 if isPath { 188 w.writeString(filepath.ToSlash(s.Name)) 189 } else if w.pkgpath != "" { 190 // w.pkgpath is already escaped. 191 n := strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1) 192 w.writeString(n) 193 } else { 194 w.writeString(s.Name) 195 } 196 // Write ABI/static information. 197 abi := int64(s.ABI()) 198 if s.Static() { 199 abi = -1 200 } 201 w.writeInt(abi) 202 w.nRefs++ 203 s.RefIdx = w.nRefs 204} 205 206func (w *objWriter) writeRefs(s *LSym) { 207 w.writeRef(s, false) 208 w.writeRef(s.Gotype, false) 209 for _, r := range s.R { 210 w.writeRef(r.Sym, false) 211 } 212 213 if s.Type == objabi.STEXT { 214 pc := &s.Func.Pcln 215 for _, d := range pc.Funcdata { 216 w.writeRef(d, false) 217 } 218 for _, f := range pc.File { 219 fsym := w.ctxt.Lookup(f) 220 w.writeRef(fsym, true) 221 } 222 for _, call := range pc.InlTree.nodes { 223 w.writeRef(call.Func, false) 224 f, _ := linkgetlineFromPos(w.ctxt, call.Pos) 225 fsym := w.ctxt.Lookup(f) 226 w.writeRef(fsym, true) 227 } 228 } 229} 230 231func (ctxt *Link) writeSymDebug(s *LSym) { 232 fmt.Fprintf(ctxt.Bso, "%s ", s.Name) 233 if s.Type != 0 { 234 fmt.Fprintf(ctxt.Bso, "%v ", s.Type) 235 } 236 if s.Static() { 237 fmt.Fprint(ctxt.Bso, "static ") 238 } 239 if s.DuplicateOK() { 240 fmt.Fprintf(ctxt.Bso, "dupok ") 241 } 242 if s.CFunc() { 243 fmt.Fprintf(ctxt.Bso, "cfunc ") 244 } 245 if s.NoSplit() { 246 fmt.Fprintf(ctxt.Bso, "nosplit ") 247 } 248 if s.TopFrame() { 249 fmt.Fprintf(ctxt.Bso, "topframe ") 250 } 251 fmt.Fprintf(ctxt.Bso, "size=%d", s.Size) 252 if s.Type == objabi.STEXT { 253 fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Func.Args), uint64(s.Func.Locals)) 254 if s.Leaf() { 255 fmt.Fprintf(ctxt.Bso, " leaf") 256 } 257 } 258 fmt.Fprintf(ctxt.Bso, "\n") 259 if s.Type == objabi.STEXT { 260 for p := s.Func.Text; p != nil; p = p.Link { 261 var s string 262 if ctxt.Debugasm > 1 { 263 s = p.String() 264 } else { 265 s = p.InnermostString() 266 } 267 fmt.Fprintf(ctxt.Bso, "\t%#04x %s\n", uint(int(p.Pc)), s) 268 } 269 } 270 for i := 0; i < len(s.P); i += 16 { 271 fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i)) 272 j := i 273 for ; j < i+16 && j < len(s.P); j++ { 274 fmt.Fprintf(ctxt.Bso, " %02x", s.P[j]) 275 } 276 for ; j < i+16; j++ { 277 fmt.Fprintf(ctxt.Bso, " ") 278 } 279 fmt.Fprintf(ctxt.Bso, " ") 280 for j = i; j < i+16 && j < len(s.P); j++ { 281 c := int(s.P[j]) 282 if ' ' <= c && c <= 0x7e { 283 fmt.Fprintf(ctxt.Bso, "%c", c) 284 } else { 285 fmt.Fprintf(ctxt.Bso, ".") 286 } 287 } 288 289 fmt.Fprintf(ctxt.Bso, "\n") 290 } 291 292 sort.Sort(relocByOff(s.R)) // generate stable output 293 for _, r := range s.R { 294 name := "" 295 if r.Sym != nil { 296 name = r.Sym.Name 297 } else if r.Type == objabi.R_TLS_LE { 298 name = "TLS" 299 } 300 if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) { 301 fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add)) 302 } else { 303 fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add) 304 } 305 } 306} 307 308func (w *objWriter) writeSym(s *LSym) { 309 ctxt := w.ctxt 310 if ctxt.Debugasm > 0 { 311 w.ctxt.writeSymDebug(s) 312 } 313 314 w.wr.WriteByte(symPrefix) 315 w.wr.WriteByte(byte(s.Type)) 316 w.writeRefIndex(s) 317 flags := int64(0) 318 if s.DuplicateOK() { 319 flags |= 1 320 } 321 if s.Local() { 322 flags |= 1 << 1 323 } 324 if s.MakeTypelink() { 325 flags |= 1 << 2 326 } 327 w.writeInt(flags) 328 w.writeInt(s.Size) 329 w.writeRefIndex(s.Gotype) 330 w.writeInt(int64(len(s.P))) 331 332 w.writeInt(int64(len(s.R))) 333 var r *Reloc 334 for i := range s.R { 335 r = &s.R[i] 336 w.writeInt(int64(r.Off)) 337 w.writeInt(int64(r.Siz)) 338 w.writeInt(int64(r.Type)) 339 w.writeInt(r.Add) 340 w.writeRefIndex(r.Sym) 341 } 342 343 if s.Type != objabi.STEXT { 344 return 345 } 346 347 w.writeInt(int64(s.Func.Args)) 348 w.writeInt(int64(s.Func.Locals)) 349 w.writeBool(s.NoSplit()) 350 flags = int64(0) 351 if s.Leaf() { 352 flags |= 1 353 } 354 if s.CFunc() { 355 flags |= 1 << 1 356 } 357 if s.ReflectMethod() { 358 flags |= 1 << 2 359 } 360 if ctxt.Flag_shared { 361 flags |= 1 << 3 362 } 363 if s.TopFrame() { 364 flags |= 1 << 4 365 } 366 w.writeInt(flags) 367 w.writeInt(int64(0)) // TODO: remove at next object file rev 368 369 pc := &s.Func.Pcln 370 w.writeInt(int64(len(pc.Pcsp.P))) 371 w.writeInt(int64(len(pc.Pcfile.P))) 372 w.writeInt(int64(len(pc.Pcline.P))) 373 w.writeInt(int64(len(pc.Pcinline.P))) 374 w.writeInt(int64(len(pc.Pcdata))) 375 for _, pcd := range pc.Pcdata { 376 w.writeInt(int64(len(pcd.P))) 377 } 378 w.writeInt(int64(len(pc.Funcdataoff))) 379 for i := range pc.Funcdataoff { 380 w.writeRefIndex(pc.Funcdata[i]) 381 } 382 for i := range pc.Funcdataoff { 383 w.writeInt(pc.Funcdataoff[i]) 384 } 385 w.writeInt(int64(len(pc.File))) 386 for _, f := range pc.File { 387 fsym := ctxt.Lookup(f) 388 w.writeRefIndex(fsym) 389 } 390 w.writeInt(int64(len(pc.InlTree.nodes))) 391 for _, call := range pc.InlTree.nodes { 392 w.writeInt(int64(call.Parent)) 393 f, l := linkgetlineFromPos(w.ctxt, call.Pos) 394 fsym := ctxt.Lookup(f) 395 w.writeRefIndex(fsym) 396 w.writeInt(int64(l)) 397 w.writeRefIndex(call.Func) 398 w.writeInt(int64(call.ParentPC)) 399 } 400} 401 402func (w *objWriter) writeBool(b bool) { 403 if b { 404 w.writeInt(1) 405 } else { 406 w.writeInt(0) 407 } 408} 409 410func (w *objWriter) writeInt(sval int64) { 411 var v uint64 412 uv := (uint64(sval) << 1) ^ uint64(sval>>63) 413 p := w.varintbuf[:] 414 for v = uv; v >= 0x80; v >>= 7 { 415 p[0] = uint8(v | 0x80) 416 p = p[1:] 417 } 418 p[0] = uint8(v) 419 p = p[1:] 420 w.wr.Write(w.varintbuf[:len(w.varintbuf)-len(p)]) 421} 422 423func (w *objWriter) writeString(s string) { 424 w.writeInt(int64(len(s))) 425 w.wr.WriteString(s) 426} 427 428func (w *objWriter) writeRefIndex(s *LSym) { 429 if s == nil { 430 w.writeInt(0) 431 return 432 } 433 if s.RefIdx == 0 { 434 log.Fatalln("writing an unreferenced symbol", s.Name) 435 } 436 w.writeInt(int64(s.RefIdx)) 437} 438 439// relocByOff sorts relocations by their offsets. 440type relocByOff []Reloc 441 442func (x relocByOff) Len() int { return len(x) } 443func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off } 444func (x relocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 445 446// implement dwarf.Context 447type dwCtxt struct{ *Link } 448 449func (c dwCtxt) PtrSize() int { 450 return c.Arch.PtrSize 451} 452func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) { 453 ls := s.(*LSym) 454 ls.WriteInt(c.Link, ls.Size, size, i) 455} 456func (c dwCtxt) AddUint16(s dwarf.Sym, i uint16) { 457 c.AddInt(s, 2, int64(i)) 458} 459func (c dwCtxt) AddUint8(s dwarf.Sym, i uint8) { 460 b := []byte{byte(i)} 461 c.AddBytes(s, b) 462} 463func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) { 464 ls := s.(*LSym) 465 ls.WriteBytes(c.Link, ls.Size, b) 466} 467func (c dwCtxt) AddString(s dwarf.Sym, v string) { 468 ls := s.(*LSym) 469 ls.WriteString(c.Link, ls.Size, len(v), v) 470 ls.WriteInt(c.Link, ls.Size, 1, 0) 471} 472func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) { 473 ls := s.(*LSym) 474 size := c.PtrSize() 475 if data != nil { 476 rsym := data.(*LSym) 477 ls.WriteAddr(c.Link, ls.Size, size, rsym, value) 478 } else { 479 ls.WriteInt(c.Link, ls.Size, size, value) 480 } 481} 482func (c dwCtxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) { 483 ls := s.(*LSym) 484 rsym := data.(*LSym) 485 ls.WriteCURelativeAddr(c.Link, ls.Size, rsym, value) 486} 487func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) { 488 panic("should be used only in the linker") 489} 490func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) { 491 size := 4 492 if isDwarf64(c.Link) { 493 size = 8 494 } 495 496 ls := s.(*LSym) 497 rsym := t.(*LSym) 498 ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs) 499 r := &ls.R[len(ls.R)-1] 500 r.Type = objabi.R_DWARFSECREF 501} 502func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) { 503 ls := s.(*LSym) 504 rsym := f.(*LSym) 505 ls.WriteAddr(c.Link, ls.Size, 4, rsym, 0) 506 r := &ls.R[len(ls.R)-1] 507 r.Type = objabi.R_DWARFFILEREF 508} 509 510func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 { 511 ls := s.(*LSym) 512 return ls.Size 513} 514 515// Here "from" is a symbol corresponding to an inlined or concrete 516// function, "to" is the symbol for the corresponding abstract 517// function, and "dclIdx" is the index of the symbol of interest with 518// respect to the Dcl slice of the original pre-optimization version 519// of the inlined function. 520func (c dwCtxt) RecordDclReference(from dwarf.Sym, to dwarf.Sym, dclIdx int, inlIndex int) { 521 ls := from.(*LSym) 522 tls := to.(*LSym) 523 ridx := len(ls.R) - 1 524 c.Link.DwFixups.ReferenceChildDIE(ls, ridx, tls, dclIdx, inlIndex) 525} 526 527func (c dwCtxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) { 528 ls := s.(*LSym) 529 c.Link.DwFixups.RegisterChildDIEOffsets(ls, vars, offsets) 530} 531 532func (c dwCtxt) Logf(format string, args ...interface{}) { 533 c.Link.Logf(format, args...) 534} 535 536func isDwarf64(ctxt *Link) bool { 537 return ctxt.Headtype == objabi.Haix 538} 539 540func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, dwarfAbsFnSym, dwarfDebugLines *LSym) { 541 if s.Type != objabi.STEXT { 542 ctxt.Diag("dwarfSym of non-TEXT %v", s) 543 } 544 if s.Func.dwarfInfoSym == nil { 545 s.Func.dwarfInfoSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name) 546 if ctxt.Flag_locationlists { 547 s.Func.dwarfLocSym = ctxt.LookupDerived(s, dwarf.LocPrefix+s.Name) 548 } 549 s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name) 550 if s.WasInlined() { 551 s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s) 552 } 553 s.Func.dwarfDebugLinesSym = ctxt.LookupDerived(s, dwarf.DebugLinesPrefix+s.Name) 554 } 555 return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym 556} 557 558func (s *LSym) Len() int64 { 559 return s.Size 560} 561 562// fileSymbol returns a symbol corresponding to the source file of the 563// first instruction (prog) of the specified function. This will 564// presumably be the file in which the function is defined. 565func (ctxt *Link) fileSymbol(fn *LSym) *LSym { 566 p := fn.Func.Text 567 if p != nil { 568 f, _ := linkgetlineFromPos(ctxt, p.Pos) 569 fsym := ctxt.Lookup(f) 570 return fsym 571 } 572 return nil 573} 574 575// populateDWARF fills in the DWARF Debugging Information Entries for 576// TEXT symbol 's'. The various DWARF symbols must already have been 577// initialized in InitTextSym. 578func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string) { 579 info, loc, ranges, absfunc, lines := ctxt.dwarfSym(s) 580 if info.Size != 0 { 581 ctxt.Diag("makeFuncDebugEntry double process %v", s) 582 } 583 var scopes []dwarf.Scope 584 var inlcalls dwarf.InlCalls 585 if ctxt.DebugInfo != nil { 586 scopes, inlcalls = ctxt.DebugInfo(s, info, curfn) 587 } 588 var err error 589 dwctxt := dwCtxt{ctxt} 590 filesym := ctxt.fileSymbol(s) 591 fnstate := &dwarf.FnState{ 592 Name: s.Name, 593 Importpath: myimportpath, 594 Info: info, 595 Filesym: filesym, 596 Loc: loc, 597 Ranges: ranges, 598 Absfn: absfunc, 599 StartPC: s, 600 Size: s.Size, 601 External: !s.Static(), 602 Scopes: scopes, 603 InlCalls: inlcalls, 604 UseBASEntries: ctxt.UseBASEntries, 605 } 606 if absfunc != nil { 607 err = dwarf.PutAbstractFunc(dwctxt, fnstate) 608 if err != nil { 609 ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) 610 } 611 err = dwarf.PutConcreteFunc(dwctxt, fnstate) 612 } else { 613 err = dwarf.PutDefaultFunc(dwctxt, fnstate) 614 } 615 if err != nil { 616 ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) 617 } 618 // Fill in the debug lines symbol. 619 ctxt.generateDebugLinesSymbol(s, lines) 620} 621 622// DwarfIntConst creates a link symbol for an integer constant with the 623// given name, type and value. 624func (ctxt *Link) DwarfIntConst(myimportpath, name, typename string, val int64) { 625 if myimportpath == "" { 626 return 627 } 628 s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) { 629 s.Type = objabi.SDWARFINFO 630 ctxt.Data = append(ctxt.Data, s) 631 }) 632 dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val) 633} 634 635func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath string) { 636 absfn := ctxt.DwFixups.AbsFuncDwarfSym(s) 637 if absfn.Size != 0 { 638 ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s) 639 } 640 if s.Func == nil { 641 s.Func = new(FuncInfo) 642 } 643 scopes, _ := ctxt.DebugInfo(s, absfn, curfn) 644 dwctxt := dwCtxt{ctxt} 645 filesym := ctxt.fileSymbol(s) 646 fnstate := dwarf.FnState{ 647 Name: s.Name, 648 Importpath: myimportpath, 649 Info: absfn, 650 Filesym: filesym, 651 Absfn: absfn, 652 External: !s.Static(), 653 Scopes: scopes, 654 UseBASEntries: ctxt.UseBASEntries, 655 } 656 if err := dwarf.PutAbstractFunc(dwctxt, &fnstate); err != nil { 657 ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) 658 } 659} 660 661// This table is designed to aid in the creation of references between 662// DWARF subprogram DIEs. 663// 664// In most cases when one DWARF DIE has to refer to another DWARF DIE, 665// the target of the reference has an LSym, which makes it easy to use 666// the existing relocation mechanism. For DWARF inlined routine DIEs, 667// however, the subprogram DIE has to refer to a child 668// parameter/variable DIE of the abstract subprogram. This child DIE 669// doesn't have an LSym, and also of interest is the fact that when 670// DWARF generation is happening for inlined function F within caller 671// G, it's possible that DWARF generation hasn't happened yet for F, 672// so there is no way to know the offset of a child DIE within F's 673// abstract function. Making matters more complex, each inlined 674// instance of F may refer to a subset of the original F's variables 675// (depending on what happens with optimization, some vars may be 676// eliminated). 677// 678// The fixup table below helps overcome this hurdle. At the point 679// where a parameter/variable reference is made (via a call to 680// "ReferenceChildDIE"), a fixup record is generate that records 681// the relocation that is targeting that child variable. At a later 682// point when the abstract function DIE is emitted, there will be 683// a call to "RegisterChildDIEOffsets", at which point the offsets 684// needed to apply fixups are captured. Finally, once the parallel 685// portion of the compilation is done, fixups can actually be applied 686// during the "Finalize" method (this can't be done during the 687// parallel portion of the compile due to the possibility of data 688// races). 689// 690// This table is also used to record the "precursor" function node for 691// each function that is the target of an inline -- child DIE references 692// have to be made with respect to the original pre-optimization 693// version of the function (to allow for the fact that each inlined 694// body may be optimized differently). 695type DwarfFixupTable struct { 696 ctxt *Link 697 mu sync.Mutex 698 symtab map[*LSym]int // maps abstract fn LSYM to index in svec 699 svec []symFixups 700 precursor map[*LSym]fnState // maps fn Lsym to precursor Node, absfn sym 701} 702 703type symFixups struct { 704 fixups []relFixup 705 doffsets []declOffset 706 inlIndex int32 707 defseen bool 708} 709 710type declOffset struct { 711 // Index of variable within DCL list of pre-optimization function 712 dclIdx int32 713 // Offset of var's child DIE with respect to containing subprogram DIE 714 offset int32 715} 716 717type relFixup struct { 718 refsym *LSym 719 relidx int32 720 dclidx int32 721} 722 723type fnState struct { 724 // precursor function (really *gc.Node) 725 precursor interface{} 726 // abstract function symbol 727 absfn *LSym 728} 729 730func NewDwarfFixupTable(ctxt *Link) *DwarfFixupTable { 731 return &DwarfFixupTable{ 732 ctxt: ctxt, 733 symtab: make(map[*LSym]int), 734 precursor: make(map[*LSym]fnState), 735 } 736} 737 738func (ft *DwarfFixupTable) GetPrecursorFunc(s *LSym) interface{} { 739 if fnstate, found := ft.precursor[s]; found { 740 return fnstate.precursor 741 } 742 return nil 743} 744 745func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) { 746 if _, found := ft.precursor[s]; found { 747 ft.ctxt.Diag("internal error: DwarfFixupTable.SetPrecursorFunc double call on %v", s) 748 } 749 750 // initialize abstract function symbol now. This is done here so 751 // as to avoid data races later on during the parallel portion of 752 // the back end. 753 absfn := ft.ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name+dwarf.AbstractFuncSuffix) 754 absfn.Set(AttrDuplicateOK, true) 755 absfn.Type = objabi.SDWARFINFO 756 ft.ctxt.Data = append(ft.ctxt.Data, absfn) 757 758 ft.precursor[s] = fnState{precursor: fn, absfn: absfn} 759} 760 761// Make a note of a child DIE reference: relocation 'ridx' within symbol 's' 762// is targeting child 'c' of DIE with symbol 'tgt'. 763func (ft *DwarfFixupTable) ReferenceChildDIE(s *LSym, ridx int, tgt *LSym, dclidx int, inlIndex int) { 764 // Protect against concurrent access if multiple backend workers 765 ft.mu.Lock() 766 defer ft.mu.Unlock() 767 768 // Create entry for symbol if not already present. 769 idx, found := ft.symtab[tgt] 770 if !found { 771 ft.svec = append(ft.svec, symFixups{inlIndex: int32(inlIndex)}) 772 idx = len(ft.svec) - 1 773 ft.symtab[tgt] = idx 774 } 775 776 // Do we have child DIE offsets available? If so, then apply them, 777 // otherwise create a fixup record. 778 sf := &ft.svec[idx] 779 if len(sf.doffsets) > 0 { 780 found := false 781 for _, do := range sf.doffsets { 782 if do.dclIdx == int32(dclidx) { 783 off := do.offset 784 s.R[ridx].Add += int64(off) 785 found = true 786 break 787 } 788 } 789 if !found { 790 ft.ctxt.Diag("internal error: DwarfFixupTable.ReferenceChildDIE unable to locate child DIE offset for dclIdx=%d src=%v tgt=%v", dclidx, s, tgt) 791 } 792 } else { 793 sf.fixups = append(sf.fixups, relFixup{s, int32(ridx), int32(dclidx)}) 794 } 795} 796 797// Called once DWARF generation is complete for a given abstract function, 798// whose children might have been referenced via a call above. Stores 799// the offsets for any child DIEs (vars, params) so that they can be 800// consumed later in on DwarfFixupTable.Finalize, which applies any 801// outstanding fixups. 802func (ft *DwarfFixupTable) RegisterChildDIEOffsets(s *LSym, vars []*dwarf.Var, coffsets []int32) { 803 // Length of these two slices should agree 804 if len(vars) != len(coffsets) { 805 ft.ctxt.Diag("internal error: RegisterChildDIEOffsets vars/offsets length mismatch") 806 return 807 } 808 809 // Generate the slice of declOffset's based in vars/coffsets 810 doffsets := make([]declOffset, len(coffsets)) 811 for i := range coffsets { 812 doffsets[i].dclIdx = vars[i].ChildIndex 813 doffsets[i].offset = coffsets[i] 814 } 815 816 ft.mu.Lock() 817 defer ft.mu.Unlock() 818 819 // Store offsets for this symbol. 820 idx, found := ft.symtab[s] 821 if !found { 822 sf := symFixups{inlIndex: -1, defseen: true, doffsets: doffsets} 823 ft.svec = append(ft.svec, sf) 824 ft.symtab[s] = len(ft.svec) - 1 825 } else { 826 sf := &ft.svec[idx] 827 sf.doffsets = doffsets 828 sf.defseen = true 829 } 830} 831 832func (ft *DwarfFixupTable) processFixups(slot int, s *LSym) { 833 sf := &ft.svec[slot] 834 for _, f := range sf.fixups { 835 dfound := false 836 for _, doffset := range sf.doffsets { 837 if doffset.dclIdx == f.dclidx { 838 f.refsym.R[f.relidx].Add += int64(doffset.offset) 839 dfound = true 840 break 841 } 842 } 843 if !dfound { 844 ft.ctxt.Diag("internal error: DwarfFixupTable has orphaned fixup on %v targeting %v relidx=%d dclidx=%d", f.refsym, s, f.relidx, f.dclidx) 845 } 846 } 847} 848 849// return the LSym corresponding to the 'abstract subprogram' DWARF 850// info entry for a function. 851func (ft *DwarfFixupTable) AbsFuncDwarfSym(fnsym *LSym) *LSym { 852 // Protect against concurrent access if multiple backend workers 853 ft.mu.Lock() 854 defer ft.mu.Unlock() 855 856 if fnstate, found := ft.precursor[fnsym]; found { 857 return fnstate.absfn 858 } 859 ft.ctxt.Diag("internal error: AbsFuncDwarfSym requested for %v, not seen during inlining", fnsym) 860 return nil 861} 862 863// Called after all functions have been compiled; the main job of this 864// function is to identify cases where there are outstanding fixups. 865// This scenario crops up when we have references to variables of an 866// inlined routine, but that routine is defined in some other package. 867// This helper walks through and locate these fixups, then invokes a 868// helper to create an abstract subprogram DIE for each one. 869func (ft *DwarfFixupTable) Finalize(myimportpath string, trace bool) { 870 if trace { 871 ft.ctxt.Logf("DwarfFixupTable.Finalize invoked for %s\n", myimportpath) 872 } 873 874 // Collect up the keys from the precursor map, then sort the 875 // resulting list (don't want to rely on map ordering here). 876 fns := make([]*LSym, len(ft.precursor)) 877 idx := 0 878 for fn := range ft.precursor { 879 fns[idx] = fn 880 idx++ 881 } 882 sort.Sort(BySymName(fns)) 883 884 // Should not be called during parallel portion of compilation. 885 if ft.ctxt.InParallel { 886 ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize call during parallel backend") 887 } 888 889 // Generate any missing abstract functions. 890 for _, s := range fns { 891 absfn := ft.AbsFuncDwarfSym(s) 892 slot, found := ft.symtab[absfn] 893 if !found || !ft.svec[slot].defseen { 894 ft.ctxt.GenAbstractFunc(s) 895 } 896 } 897 898 // Apply fixups. 899 for _, s := range fns { 900 absfn := ft.AbsFuncDwarfSym(s) 901 slot, found := ft.symtab[absfn] 902 if !found { 903 ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize orphan abstract function for %v", s) 904 } else { 905 ft.processFixups(slot, s) 906 } 907 } 908} 909 910type BySymName []*LSym 911 912func (s BySymName) Len() int { return len(s) } 913func (s BySymName) Less(i, j int) bool { return s[i].Name < s[j].Name } 914func (s BySymName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 915