github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/dwarfgen/dwarf.go (about) 1 // Copyright 2011 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 package dwarfgen 6 7 import ( 8 "bytes" 9 "flag" 10 "fmt" 11 "sort" 12 13 "github.com/go-asm/go/buildcfg" 14 15 "github.com/go-asm/go/cmd/compile/base" 16 "github.com/go-asm/go/cmd/compile/ir" 17 "github.com/go-asm/go/cmd/compile/reflectdata" 18 "github.com/go-asm/go/cmd/compile/ssa" 19 "github.com/go-asm/go/cmd/compile/ssagen" 20 "github.com/go-asm/go/cmd/compile/types" 21 "github.com/go-asm/go/cmd/dwarf" 22 "github.com/go-asm/go/cmd/obj" 23 "github.com/go-asm/go/cmd/objabi" 24 "github.com/go-asm/go/cmd/src" 25 ) 26 27 func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (scopes []dwarf.Scope, inlcalls dwarf.InlCalls) { 28 fn := curfn.(*ir.Func) 29 30 if fn.Nname != nil { 31 expect := fn.Linksym() 32 if fnsym.ABI() == obj.ABI0 { 33 expect = fn.LinksymABI(obj.ABI0) 34 } 35 if fnsym != expect { 36 base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect) 37 } 38 } 39 40 // Back when there were two different *Funcs for a function, this code 41 // was not consistent about whether a particular *Node being processed 42 // was an ODCLFUNC or ONAME node. Partly this is because inlined function 43 // bodies have no ODCLFUNC node, which was it's own inconsistency. 44 // In any event, the handling of the two different nodes for DWARF purposes 45 // was subtly different, likely in unintended ways. CL 272253 merged the 46 // two nodes' Func fields, so that code sees the same *Func whether it is 47 // holding the ODCLFUNC or the ONAME. This resulted in changes in the 48 // DWARF output. To preserve the existing DWARF output and leave an 49 // intentional change for a future CL, this code does the following when 50 // fn.Op == ONAME: 51 // 52 // 1. Disallow use of createComplexVars in createDwarfVars. 53 // It was not possible to reach that code for an ONAME before, 54 // because the DebugInfo was set only on the ODCLFUNC Func. 55 // Calling into it in the ONAME case causes an index out of bounds panic. 56 // 57 // 2. Do not populate apdecls. fn.Func.Dcl was in the ODCLFUNC Func, 58 // not the ONAME Func. Populating apdecls for the ONAME case results 59 // in selected being populated after createSimpleVars is called in 60 // createDwarfVars, and then that causes the loop to skip all the entries 61 // in dcl, meaning that the RecordAutoType calls don't happen. 62 // 63 // These two adjustments keep toolstash -cmp working for now. 64 // Deciding the right answer is, as they say, future work. 65 // 66 // We can tell the difference between the old ODCLFUNC and ONAME 67 // cases by looking at the infosym.Name. If it's empty, DebugInfo is 68 // being called from (*obj.Link).populateDWARF, which used to use 69 // the ODCLFUNC. If it's non-empty (the name will end in $abstract), 70 // DebugInfo is being called from (*obj.Link).DwarfAbstractFunc, 71 // which used to use the ONAME form. 72 isODCLFUNC := infosym.Name == "" 73 74 var apdecls []*ir.Name 75 // Populate decls for fn. 76 if isODCLFUNC { 77 for _, n := range fn.Dcl { 78 if n.Op() != ir.ONAME { // might be OTYPE or OLITERAL 79 continue 80 } 81 switch n.Class { 82 case ir.PAUTO: 83 if !n.Used() { 84 // Text == nil -> generating abstract function 85 if fnsym.Func().Text != nil { 86 base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)") 87 } 88 continue 89 } 90 case ir.PPARAM, ir.PPARAMOUT: 91 default: 92 continue 93 } 94 apdecls = append(apdecls, n) 95 if n.Type().Kind() == types.TSSA { 96 // Can happen for TypeInt128 types. This only happens for 97 // spill locations, so not a huge deal. 98 continue 99 } 100 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) 101 } 102 } 103 104 decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls) 105 106 // For each type referenced by the functions auto vars but not 107 // already referenced by a dwarf var, attach an R_USETYPE relocation to 108 // the function symbol to insure that the type included in DWARF 109 // processing during linking. 110 typesyms := []*obj.LSym{} 111 for t := range fnsym.Func().Autot { 112 typesyms = append(typesyms, t) 113 } 114 sort.Sort(obj.BySymName(typesyms)) 115 for _, sym := range typesyms { 116 r := obj.Addrel(infosym) 117 r.Sym = sym 118 r.Type = objabi.R_USETYPE 119 } 120 fnsym.Func().Autot = nil 121 122 var varScopes []ir.ScopeID 123 for _, decl := range decls { 124 pos := declPos(decl) 125 varScopes = append(varScopes, findScope(fn.Marks, pos)) 126 } 127 128 scopes = assembleScopes(fnsym, fn, dwarfVars, varScopes) 129 if base.Flag.GenDwarfInl > 0 { 130 inlcalls = assembleInlines(fnsym, dwarfVars) 131 } 132 return scopes, inlcalls 133 } 134 135 func declPos(decl *ir.Name) src.XPos { 136 return decl.Canonical().Pos() 137 } 138 139 // createDwarfVars process fn, returning a list of DWARF variables and the 140 // Nodes they represent. 141 func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var) { 142 // Collect a raw list of DWARF vars. 143 var vars []*dwarf.Var 144 var decls []*ir.Name 145 var selected ir.NameSet 146 147 if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK { 148 decls, vars, selected = createComplexVars(fnsym, fn) 149 } else if fn.ABI == obj.ABIInternal && base.Flag.N != 0 && complexOK { 150 decls, vars, selected = createABIVars(fnsym, fn, apDecls) 151 } else { 152 decls, vars, selected = createSimpleVars(fnsym, apDecls) 153 } 154 if fn.DebugInfo != nil { 155 // Recover zero sized variables eliminated by the stackframe pass 156 for _, n := range fn.DebugInfo.(*ssa.FuncDebug).OptDcl { 157 if n.Class != ir.PAUTO { 158 continue 159 } 160 types.CalcSize(n.Type()) 161 if n.Type().Size() == 0 { 162 decls = append(decls, n) 163 vars = append(vars, createSimpleVar(fnsym, n)) 164 vars[len(vars)-1].StackOffset = 0 165 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) 166 } 167 } 168 } 169 170 dcl := apDecls 171 if fnsym.WasInlined() { 172 dcl = preInliningDcls(fnsym) 173 } else { 174 // The backend's stackframe pass prunes away entries from the 175 // fn's Dcl list, including PARAMOUT nodes that correspond to 176 // output params passed in registers. Add back in these 177 // entries here so that we can process them properly during 178 // DWARF-gen. See issue 48573 for more details. 179 debugInfo := fn.DebugInfo.(*ssa.FuncDebug) 180 for _, n := range debugInfo.RegOutputParams { 181 if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() { 182 panic("invalid ir.Name on debugInfo.RegOutputParams list") 183 } 184 dcl = append(dcl, n) 185 } 186 } 187 188 // If optimization is enabled, the list above will typically be 189 // missing some of the original pre-optimization variables in the 190 // function (they may have been promoted to registers, folded into 191 // constants, dead-coded away, etc). Input arguments not eligible 192 // for SSA optimization are also missing. Here we add back in entries 193 // for selected missing vars. Note that the recipe below creates a 194 // conservative location. The idea here is that we want to 195 // communicate to the user that "yes, there is a variable named X 196 // in this function, but no, I don't have enough information to 197 // reliably report its contents." 198 // For non-SSA-able arguments, however, the correct information 199 // is known -- they have a single home on the stack. 200 for _, n := range dcl { 201 if selected.Has(n) { 202 continue 203 } 204 c := n.Sym().Name[0] 205 if c == '.' || n.Type().IsUntyped() { 206 continue 207 } 208 if n.Class == ir.PPARAM && !ssa.CanSSA(n.Type()) { 209 // SSA-able args get location lists, and may move in and 210 // out of registers, so those are handled elsewhere. 211 // Autos and named output params seem to get handled 212 // with VARDEF, which creates location lists. 213 // Args not of SSA-able type are treated here; they 214 // are homed on the stack in a single place for the 215 // entire call. 216 vars = append(vars, createSimpleVar(fnsym, n)) 217 decls = append(decls, n) 218 continue 219 } 220 typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) 221 decls = append(decls, n) 222 abbrev := dwarf.DW_ABRV_AUTO_LOCLIST 223 isReturnValue := (n.Class == ir.PPARAMOUT) 224 if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT { 225 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 226 } 227 if n.Esc() == ir.EscHeap { 228 // The variable in question has been promoted to the heap. 229 // Its address is in n.Heapaddr. 230 // TODO(thanm): generate a better location expression 231 } 232 inlIndex := 0 233 if base.Flag.GenDwarfInl > 1 { 234 if n.InlFormal() || n.InlLocal() { 235 inlIndex = posInlIndex(n.Pos()) + 1 236 if n.InlFormal() { 237 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 238 } 239 } 240 } 241 declpos := base.Ctxt.InnermostPos(n.Pos()) 242 vars = append(vars, &dwarf.Var{ 243 Name: n.Sym().Name, 244 IsReturnValue: isReturnValue, 245 Abbrev: abbrev, 246 StackOffset: int32(n.FrameOffset()), 247 Type: base.Ctxt.Lookup(typename), 248 DeclFile: declpos.RelFilename(), 249 DeclLine: declpos.RelLine(), 250 DeclCol: declpos.RelCol(), 251 InlIndex: int32(inlIndex), 252 ChildIndex: -1, 253 DictIndex: n.DictIndex, 254 }) 255 // Record go type of to insure that it gets emitted by the linker. 256 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) 257 } 258 259 // Sort decls and vars. 260 sortDeclsAndVars(fn, decls, vars) 261 262 return decls, vars 263 } 264 265 // sortDeclsAndVars sorts the decl and dwarf var lists according to 266 // parameter declaration order, so as to insure that when a subprogram 267 // DIE is emitted, its parameter children appear in declaration order. 268 // Prior to the advent of the register ABI, sorting by frame offset 269 // would achieve this; with the register we now need to go back to the 270 // original function signature. 271 func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) { 272 paramOrder := make(map[*ir.Name]int) 273 idx := 1 274 for _, f := range fn.Type().RecvParamsResults() { 275 if n, ok := f.Nname.(*ir.Name); ok { 276 paramOrder[n] = idx 277 idx++ 278 } 279 } 280 sort.Stable(varsAndDecls{decls, vars, paramOrder}) 281 } 282 283 type varsAndDecls struct { 284 decls []*ir.Name 285 vars []*dwarf.Var 286 paramOrder map[*ir.Name]int 287 } 288 289 func (v varsAndDecls) Len() int { 290 return len(v.decls) 291 } 292 293 func (v varsAndDecls) Less(i, j int) bool { 294 nameLT := func(ni, nj *ir.Name) bool { 295 oi, foundi := v.paramOrder[ni] 296 oj, foundj := v.paramOrder[nj] 297 if foundi { 298 if foundj { 299 return oi < oj 300 } else { 301 return true 302 } 303 } 304 return false 305 } 306 return nameLT(v.decls[i], v.decls[j]) 307 } 308 309 func (v varsAndDecls) Swap(i, j int) { 310 v.vars[i], v.vars[j] = v.vars[j], v.vars[i] 311 v.decls[i], v.decls[j] = v.decls[j], v.decls[i] 312 } 313 314 // Given a function that was inlined at some point during the 315 // compilation, return a sorted list of nodes corresponding to the 316 // autos/locals in that function prior to inlining. If this is a 317 // function that is not local to the package being compiled, then the 318 // names of the variables may have been "versioned" to avoid conflicts 319 // with local vars; disregard this versioning when sorting. 320 func preInliningDcls(fnsym *obj.LSym) []*ir.Name { 321 fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func) 322 var rdcl []*ir.Name 323 for _, n := range fn.Inl.Dcl { 324 c := n.Sym().Name[0] 325 // Avoid reporting "_" parameters, since if there are more than 326 // one, it can result in a collision later on, as in #23179. 327 if n.Sym().Name == "_" || c == '.' || n.Type().IsUntyped() { 328 continue 329 } 330 rdcl = append(rdcl, n) 331 } 332 return rdcl 333 } 334 335 // createSimpleVars creates a DWARF entry for every variable declared in the 336 // function, claiming that they are permanently on the stack. 337 func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { 338 var vars []*dwarf.Var 339 var decls []*ir.Name 340 var selected ir.NameSet 341 for _, n := range apDecls { 342 if ir.IsAutoTmp(n) { 343 continue 344 } 345 346 decls = append(decls, n) 347 vars = append(vars, createSimpleVar(fnsym, n)) 348 selected.Add(n) 349 } 350 return decls, vars, selected 351 } 352 353 func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { 354 var abbrev int 355 var offs int64 356 357 localAutoOffset := func() int64 { 358 offs = n.FrameOffset() 359 if base.Ctxt.Arch.FixedFrameSize == 0 { 360 offs -= int64(types.PtrSize) 361 } 362 if buildcfg.FramePointerEnabled { 363 offs -= int64(types.PtrSize) 364 } 365 return offs 366 } 367 368 switch n.Class { 369 case ir.PAUTO: 370 offs = localAutoOffset() 371 abbrev = dwarf.DW_ABRV_AUTO 372 case ir.PPARAM, ir.PPARAMOUT: 373 abbrev = dwarf.DW_ABRV_PARAM 374 if n.IsOutputParamInRegisters() { 375 offs = localAutoOffset() 376 } else { 377 offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize 378 } 379 380 default: 381 base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n) 382 } 383 384 typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) 385 delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type())) 386 inlIndex := 0 387 if base.Flag.GenDwarfInl > 1 { 388 if n.InlFormal() || n.InlLocal() { 389 inlIndex = posInlIndex(n.Pos()) + 1 390 if n.InlFormal() { 391 abbrev = dwarf.DW_ABRV_PARAM 392 } 393 } 394 } 395 declpos := base.Ctxt.InnermostPos(declPos(n)) 396 return &dwarf.Var{ 397 Name: n.Sym().Name, 398 IsReturnValue: n.Class == ir.PPARAMOUT, 399 IsInlFormal: n.InlFormal(), 400 Abbrev: abbrev, 401 StackOffset: int32(offs), 402 Type: base.Ctxt.Lookup(typename), 403 DeclFile: declpos.RelFilename(), 404 DeclLine: declpos.RelLine(), 405 DeclCol: declpos.RelCol(), 406 InlIndex: int32(inlIndex), 407 ChildIndex: -1, 408 DictIndex: n.DictIndex, 409 } 410 } 411 412 // createABIVars creates DWARF variables for functions in which the 413 // register ABI is enabled but optimization is turned off. It uses a 414 // hybrid approach in which register-resident input params are 415 // captured with location lists, and all other vars use the "simple" 416 // strategy. 417 func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { 418 419 // Invoke createComplexVars to generate dwarf vars for input parameters 420 // that are register-allocated according to the ABI rules. 421 decls, vars, selected := createComplexVars(fnsym, fn) 422 423 // Now fill in the remainder of the variables: input parameters 424 // that are not register-resident, output parameters, and local 425 // variables. 426 for _, n := range apDecls { 427 if ir.IsAutoTmp(n) { 428 continue 429 } 430 if _, ok := selected[n]; ok { 431 // already handled 432 continue 433 } 434 435 decls = append(decls, n) 436 vars = append(vars, createSimpleVar(fnsym, n)) 437 selected.Add(n) 438 } 439 440 return decls, vars, selected 441 } 442 443 // createComplexVars creates recomposed DWARF vars with location lists, 444 // suitable for describing optimized code. 445 func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { 446 debugInfo := fn.DebugInfo.(*ssa.FuncDebug) 447 448 // Produce a DWARF variable entry for each user variable. 449 var decls []*ir.Name 450 var vars []*dwarf.Var 451 var ssaVars ir.NameSet 452 453 for varID, dvar := range debugInfo.Vars { 454 n := dvar 455 ssaVars.Add(n) 456 for _, slot := range debugInfo.VarSlots[varID] { 457 ssaVars.Add(debugInfo.Slots[slot].N) 458 } 459 460 if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil { 461 decls = append(decls, n) 462 vars = append(vars, dvar) 463 } 464 } 465 466 return decls, vars, ssaVars 467 } 468 469 // createComplexVar builds a single DWARF variable entry and location list. 470 func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var { 471 debug := fn.DebugInfo.(*ssa.FuncDebug) 472 n := debug.Vars[varID] 473 474 var abbrev int 475 switch n.Class { 476 case ir.PAUTO: 477 abbrev = dwarf.DW_ABRV_AUTO_LOCLIST 478 case ir.PPARAM, ir.PPARAMOUT: 479 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 480 default: 481 return nil 482 } 483 484 gotype := reflectdata.TypeLinksym(n.Type()) 485 delete(fnsym.Func().Autot, gotype) 486 typename := dwarf.InfoPrefix + gotype.Name[len("type:"):] 487 inlIndex := 0 488 if base.Flag.GenDwarfInl > 1 { 489 if n.InlFormal() || n.InlLocal() { 490 inlIndex = posInlIndex(n.Pos()) + 1 491 if n.InlFormal() { 492 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 493 } 494 } 495 } 496 declpos := base.Ctxt.InnermostPos(n.Pos()) 497 dvar := &dwarf.Var{ 498 Name: n.Sym().Name, 499 IsReturnValue: n.Class == ir.PPARAMOUT, 500 IsInlFormal: n.InlFormal(), 501 Abbrev: abbrev, 502 Type: base.Ctxt.Lookup(typename), 503 // The stack offset is used as a sorting key, so for decomposed 504 // variables just give it the first one. It's not used otherwise. 505 // This won't work well if the first slot hasn't been assigned a stack 506 // location, but it's not obvious how to do better. 507 StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]), 508 DeclFile: declpos.RelFilename(), 509 DeclLine: declpos.RelLine(), 510 DeclCol: declpos.RelCol(), 511 InlIndex: int32(inlIndex), 512 ChildIndex: -1, 513 DictIndex: n.DictIndex, 514 } 515 list := debug.LocationLists[varID] 516 if len(list) != 0 { 517 dvar.PutLocationList = func(listSym, startPC dwarf.Sym) { 518 debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym)) 519 } 520 } 521 return dvar 522 } 523 524 // RecordFlags records the specified command-line flags to be placed 525 // in the DWARF info. 526 func RecordFlags(flags ...string) { 527 if base.Ctxt.Pkgpath == "" { 528 panic("missing pkgpath") 529 } 530 531 type BoolFlag interface { 532 IsBoolFlag() bool 533 } 534 type CountFlag interface { 535 IsCountFlag() bool 536 } 537 var cmd bytes.Buffer 538 for _, name := range flags { 539 f := flag.Lookup(name) 540 if f == nil { 541 continue 542 } 543 getter := f.Value.(flag.Getter) 544 if getter.String() == f.DefValue { 545 // Flag has default value, so omit it. 546 continue 547 } 548 if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() { 549 val, ok := getter.Get().(bool) 550 if ok && val { 551 fmt.Fprintf(&cmd, " -%s", f.Name) 552 continue 553 } 554 } 555 if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() { 556 val, ok := getter.Get().(int) 557 if ok && val == 1 { 558 fmt.Fprintf(&cmd, " -%s", f.Name) 559 continue 560 } 561 } 562 fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get()) 563 } 564 565 // Adds flag to producer string signaling whether regabi is turned on or 566 // off. 567 // Once regabi is turned on across the board and the relative GOEXPERIMENT 568 // knobs no longer exist this code should be removed. 569 if buildcfg.Experiment.RegabiArgs { 570 cmd.Write([]byte(" regabi")) 571 } 572 573 if cmd.Len() == 0 { 574 return 575 } 576 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath) 577 s.Type = objabi.SDWARFCUINFO 578 // Sometimes (for example when building tests) we can link 579 // together two package main archives. So allow dups. 580 s.Set(obj.AttrDuplicateOK, true) 581 base.Ctxt.Data = append(base.Ctxt.Data, s) 582 s.P = cmd.Bytes()[1:] 583 } 584 585 // RecordPackageName records the name of the package being 586 // compiled, so that the linker can save it in the compile unit's DIE. 587 func RecordPackageName() { 588 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath) 589 s.Type = objabi.SDWARFCUINFO 590 // Sometimes (for example when building tests) we can link 591 // together two package main archives. So allow dups. 592 s.Set(obj.AttrDuplicateOK, true) 593 base.Ctxt.Data = append(base.Ctxt.Data, s) 594 s.P = []byte(types.LocalPkg.Name) 595 }