2016-07-28 13:04:41 -04:00
|
|
|
// Copyright 2016 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 dwarf generates DWARF debugging information.
|
|
|
|
|
// DWARF generation is split between the compiler and the linker,
|
|
|
|
|
// this package contains the shared code.
|
|
|
|
|
package dwarf
|
|
|
|
|
|
2017-03-26 15:16:11 -07:00
|
|
|
import (
|
2019-02-20 16:39:09 +01:00
|
|
|
"bytes"
|
2017-05-02 16:46:01 +02:00
|
|
|
"errors"
|
2017-03-26 15:16:11 -07:00
|
|
|
"fmt"
|
2021-04-15 23:05:49 -04:00
|
|
|
"internal/buildcfg"
|
2022-01-27 13:10:48 -05:00
|
|
|
"os/exec"
|
2017-10-06 11:32:28 -04:00
|
|
|
"sort"
|
2019-02-20 16:39:09 +01:00
|
|
|
"strconv"
|
2017-10-06 11:32:28 -04:00
|
|
|
"strings"
|
2021-04-15 23:05:49 -04:00
|
|
|
|
|
|
|
|
"cmd/internal/objabi"
|
2017-03-26 15:16:11 -07:00
|
|
|
)
|
2016-07-28 13:04:41 -04:00
|
|
|
|
|
|
|
|
// InfoPrefix is the prefix for all the symbols containing DWARF info entries.
|
|
|
|
|
const InfoPrefix = "go.info."
|
|
|
|
|
|
2017-10-17 17:09:54 -04:00
|
|
|
// ConstInfoPrefix is the prefix for all symbols containing DWARF info
|
2017-09-03 11:59:18 +02:00
|
|
|
// entries that contain constants.
|
|
|
|
|
const ConstInfoPrefix = "go.constinfo."
|
|
|
|
|
|
2017-10-17 17:09:54 -04:00
|
|
|
// CUInfoPrefix is the prefix for symbols containing information to
|
|
|
|
|
// populate the DWARF compilation unit info entries.
|
|
|
|
|
const CUInfoPrefix = "go.cuinfo."
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
// Used to form the symbol name assigned to the DWARF 'abstract subprogram"
|
|
|
|
|
// info entry for a function
|
|
|
|
|
const AbstractFuncSuffix = "$abstract"
|
|
|
|
|
|
|
|
|
|
// Controls logging/debugging for selected aspects of DWARF subprogram
|
|
|
|
|
// generation (functions, scopes).
|
|
|
|
|
var logDwarf bool
|
|
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
// Sym represents a symbol.
|
|
|
|
|
type Sym interface {
|
2020-02-19 16:16:17 -05:00
|
|
|
Length(dwarfContext interface{}) int64
|
2016-07-28 13:04:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A Var represents a local variable or a function parameter.
|
|
|
|
|
type Var struct {
|
2017-10-18 13:45:03 -04:00
|
|
|
Name string
|
|
|
|
|
Abbrev int // Either DW_ABRV_AUTO[_LOCLIST] or DW_ABRV_PARAM[_LOCLIST]
|
|
|
|
|
IsReturnValue bool
|
2017-10-06 11:32:28 -04:00
|
|
|
IsInlFormal bool
|
2021-08-25 10:08:07 +02:00
|
|
|
DictIndex uint16 // index of the dictionary entry describing the type of this variable
|
2017-10-18 13:45:03 -04:00
|
|
|
StackOffset int32
|
cmd/compile: reimplement location list generation
Completely redesign and reimplement location list generation to be more
efficient, and hopefully not too hard to understand.
RegKills are gone. Instead of using the regalloc's liveness
calculations, redo them using the Ops' clobber information. Besides
saving a lot of Values, this avoids adding RegKills to blocks that would
be empty otherwise, which was messing up optimizations. This does mean
that it's much harder to tell whether the generation process is buggy
(there's nothing to cross-check it with), and there may be disagreements
with GC liveness. But the performance gain is significant, and it's nice
not to be messing with earlier compiler phases.
The intermediate representations are gone. Instead of producing
ssa.BlockDebugs, then dwarf.LocationLists, and then finally real
location lists, go directly from the SSA to a (mostly) real location
list. Because the SSA analysis happens before assembly, it stores
encoded block/value IDs where PCs would normally go. It would be easier
to do the SSA analysis after assembly, but I didn't want to retain the
SSA just for that.
Generation proceeds in two phases: first, it traverses the function in
CFG order, storing the state of the block at the beginning and end. End
states are used to produce the start states of the successor blocks. In
the second phase, it traverses in program text order and produces the
location lists. The processing in the second phase is redundant, but
much cheaper than storing the intermediate representation. It might be
possible to combine the two phases somewhat to take advantage of cases
where the CFG matches the block layout, but I haven't tried.
Location lists are finalized by adding a base address selection entry,
translating each encoded block/value ID to a real PC, and adding the
terminating zero entry. This probably won't work on OSX, where dsymutil
will choke on the base address selection. I tried emitting CU-relative
relocations for each address, and it was *very* bad for performance --
it uses more memory storing all the relocations than it does for the
actual location list bytes. I think I'm going to end up synthesizing the
relocations in the linker only on OSX, but TBD.
TestNexting needs updating: with more optimizations working, the
debugger doesn't stop on the continue (line 88) any more, and the test's
duplicate suppression kicks in. Also, dx and dy live a little longer
now, but they have the correct values.
Change-Id: Ie772dfe23a4e389ca573624fac4d05401ae32307
Reviewed-on: https://go-review.googlesource.com/89356
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2017-10-26 15:40:17 -04:00
|
|
|
// This package can't use the ssa package, so it can't mention ssa.FuncDebug,
|
|
|
|
|
// so indirect through a closure.
|
|
|
|
|
PutLocationList func(listSym, startPC Sym)
|
|
|
|
|
Scope int32
|
|
|
|
|
Type Sym
|
|
|
|
|
DeclFile string
|
|
|
|
|
DeclLine uint
|
|
|
|
|
DeclCol uint
|
|
|
|
|
InlIndex int32 // subtract 1 to form real index into InlTree
|
|
|
|
|
ChildIndex int32 // child DIE index in abstract function
|
|
|
|
|
IsInAbstract bool // variable exists in abstract function
|
2016-07-28 13:04:41 -04:00
|
|
|
}
|
|
|
|
|
|
2017-05-02 16:46:01 +02:00
|
|
|
// A Scope represents a lexical scope. All variables declared within a
|
|
|
|
|
// scope will only be visible to instructions covered by the scope.
|
|
|
|
|
// Lexical scopes are contiguous in source files but can end up being
|
|
|
|
|
// compiled to discontiguous blocks of instructions in the executable.
|
|
|
|
|
// The Ranges field lists all the blocks of instructions that belong
|
|
|
|
|
// in this scope.
|
|
|
|
|
type Scope struct {
|
|
|
|
|
Parent int32
|
|
|
|
|
Ranges []Range
|
|
|
|
|
Vars []*Var
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A Range represents a half-open interval [Start, End).
|
|
|
|
|
type Range struct {
|
|
|
|
|
Start, End int64
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
// This container is used by the PutFunc* variants below when
|
|
|
|
|
// creating the DWARF subprogram DIE(s) for a function.
|
|
|
|
|
type FnState struct {
|
2019-04-02 17:26:49 -04:00
|
|
|
Name string
|
|
|
|
|
Importpath string
|
|
|
|
|
Info Sym
|
|
|
|
|
Filesym Sym
|
|
|
|
|
Loc Sym
|
|
|
|
|
Ranges Sym
|
|
|
|
|
Absfn Sym
|
|
|
|
|
StartPC Sym
|
|
|
|
|
Size int64
|
|
|
|
|
External bool
|
|
|
|
|
Scopes []Scope
|
|
|
|
|
InlCalls InlCalls
|
|
|
|
|
UseBASEntries bool
|
2021-08-25 10:08:07 +02:00
|
|
|
|
|
|
|
|
dictIndexToOffset []int64
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func EnableLogging(doit bool) {
|
|
|
|
|
logDwarf = doit
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-17 14:17:07 -04:00
|
|
|
// MergeRanges creates a new range list by merging the ranges from
|
|
|
|
|
// its two arguments, then returns the new list.
|
|
|
|
|
func MergeRanges(in1, in2 []Range) []Range {
|
|
|
|
|
out := make([]Range, 0, len(in1)+len(in2))
|
2017-05-02 16:46:01 +02:00
|
|
|
i, j := 0, 0
|
|
|
|
|
for {
|
|
|
|
|
var cur Range
|
2020-08-17 14:17:07 -04:00
|
|
|
if i < len(in2) && j < len(in1) {
|
|
|
|
|
if in2[i].Start < in1[j].Start {
|
|
|
|
|
cur = in2[i]
|
2017-05-02 16:46:01 +02:00
|
|
|
i++
|
|
|
|
|
} else {
|
2020-08-17 14:17:07 -04:00
|
|
|
cur = in1[j]
|
2017-05-02 16:46:01 +02:00
|
|
|
j++
|
|
|
|
|
}
|
2020-08-17 14:17:07 -04:00
|
|
|
} else if i < len(in2) {
|
|
|
|
|
cur = in2[i]
|
2017-05-02 16:46:01 +02:00
|
|
|
i++
|
2020-08-17 14:17:07 -04:00
|
|
|
} else if j < len(in1) {
|
|
|
|
|
cur = in1[j]
|
2017-05-02 16:46:01 +02:00
|
|
|
j++
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if n := len(out); n > 0 && cur.Start <= out[n-1].End {
|
|
|
|
|
out[n-1].End = cur.End
|
|
|
|
|
} else {
|
|
|
|
|
out = append(out, cur)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-17 14:17:07 -04:00
|
|
|
return out
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UnifyRanges merges the ranges from 'c' into the list of ranges for 's'.
|
|
|
|
|
func (s *Scope) UnifyRanges(c *Scope) {
|
|
|
|
|
s.Ranges = MergeRanges(s.Ranges, c.Ranges)
|
2017-05-02 16:46:01 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-10 12:18:43 -04:00
|
|
|
// AppendRange adds r to s, if r is non-empty.
|
|
|
|
|
// If possible, it extends the last Range in s.Ranges; if not, it creates a new one.
|
|
|
|
|
func (s *Scope) AppendRange(r Range) {
|
|
|
|
|
if r.End <= r.Start {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
i := len(s.Ranges)
|
|
|
|
|
if i > 0 && s.Ranges[i-1].End == r.Start {
|
|
|
|
|
s.Ranges[i-1].End = r.End
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
s.Ranges = append(s.Ranges, r)
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
type InlCalls struct {
|
|
|
|
|
Calls []InlCall
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type InlCall struct {
|
|
|
|
|
// index into ctx.InlTree describing the call inlined here
|
|
|
|
|
InlIndex int
|
|
|
|
|
|
|
|
|
|
// Symbol of file containing inlined call site (really *obj.LSym).
|
|
|
|
|
CallFile Sym
|
|
|
|
|
|
|
|
|
|
// Line number of inlined call site.
|
|
|
|
|
CallLine uint32
|
|
|
|
|
|
|
|
|
|
// Dwarf abstract subroutine symbol (really *obj.LSym).
|
|
|
|
|
AbsFunSym Sym
|
|
|
|
|
|
|
|
|
|
// Indices of child inlines within Calls array above.
|
|
|
|
|
Children []int
|
|
|
|
|
|
|
|
|
|
// entries in this list are PAUTO's created by the inliner to
|
|
|
|
|
// capture the promoted formals and locals of the inlined callee.
|
|
|
|
|
InlVars []*Var
|
|
|
|
|
|
|
|
|
|
// PC ranges for this inlined call.
|
|
|
|
|
Ranges []Range
|
|
|
|
|
|
|
|
|
|
// Root call (not a child of some other call).
|
|
|
|
|
Root bool
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
// A Context specifies how to add data to a Sym.
|
|
|
|
|
type Context interface {
|
|
|
|
|
PtrSize() int
|
|
|
|
|
AddInt(s Sym, size int, i int64)
|
|
|
|
|
AddBytes(s Sym, b []byte)
|
|
|
|
|
AddAddress(s Sym, t interface{}, ofs int64)
|
2019-04-02 17:26:49 -04:00
|
|
|
AddCURelativeAddress(s Sym, t interface{}, ofs int64)
|
2016-07-28 13:04:41 -04:00
|
|
|
AddSectionOffset(s Sym, size int, t interface{}, ofs int64)
|
2018-09-28 16:44:30 +02:00
|
|
|
AddDWARFAddrSectionOffset(s Sym, t interface{}, ofs int64)
|
2017-10-06 11:32:28 -04:00
|
|
|
CurrentOffset(s Sym) int64
|
|
|
|
|
RecordDclReference(from Sym, to Sym, dclIdx int, inlIndex int)
|
|
|
|
|
RecordChildDieOffsets(s Sym, vars []*Var, offsets []int32)
|
2016-07-28 13:04:41 -04:00
|
|
|
AddString(s Sym, v string)
|
2017-10-24 16:08:46 -04:00
|
|
|
AddFileRef(s Sym, f interface{})
|
2017-10-06 11:32:28 -04:00
|
|
|
Logf(format string, args ...interface{})
|
2016-07-28 13:04:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AppendUleb128 appends v to b using DWARF's unsigned LEB128 encoding.
|
|
|
|
|
func AppendUleb128(b []byte, v uint64) []byte {
|
|
|
|
|
for {
|
|
|
|
|
c := uint8(v & 0x7f)
|
|
|
|
|
v >>= 7
|
|
|
|
|
if v != 0 {
|
|
|
|
|
c |= 0x80
|
|
|
|
|
}
|
|
|
|
|
b = append(b, c)
|
|
|
|
|
if c&0x80 == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AppendSleb128 appends v to b using DWARF's signed LEB128 encoding.
|
|
|
|
|
func AppendSleb128(b []byte, v int64) []byte {
|
|
|
|
|
for {
|
|
|
|
|
c := uint8(v & 0x7f)
|
|
|
|
|
s := uint8(v & 0x40)
|
|
|
|
|
v >>= 7
|
|
|
|
|
if (v != -1 || s == 0) && (v != 0 || s != 0) {
|
|
|
|
|
c |= 0x80
|
|
|
|
|
}
|
|
|
|
|
b = append(b, c)
|
|
|
|
|
if c&0x80 == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 15:16:11 -07:00
|
|
|
// sevenbits contains all unsigned seven bit numbers, indexed by their value.
|
|
|
|
|
var sevenbits = [...]byte{
|
|
|
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
|
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
|
|
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
|
|
|
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
|
|
|
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
|
|
|
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
|
|
|
|
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
|
|
|
|
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sevenBitU returns the unsigned LEB128 encoding of v if v is seven bits and nil otherwise.
|
|
|
|
|
// The contents of the returned slice must not be modified.
|
|
|
|
|
func sevenBitU(v int64) []byte {
|
|
|
|
|
if uint64(v) < uint64(len(sevenbits)) {
|
|
|
|
|
return sevenbits[v : v+1]
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2016-07-28 13:04:41 -04:00
|
|
|
|
2017-03-26 15:16:11 -07:00
|
|
|
// sevenBitS returns the signed LEB128 encoding of v if v is seven bits and nil otherwise.
|
|
|
|
|
// The contents of the returned slice must not be modified.
|
|
|
|
|
func sevenBitS(v int64) []byte {
|
|
|
|
|
if uint64(v) <= 63 {
|
|
|
|
|
return sevenbits[v : v+1]
|
|
|
|
|
}
|
|
|
|
|
if uint64(-v) <= 64 {
|
|
|
|
|
return sevenbits[128+v : 128+v+1]
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Uleb128put appends v to s using DWARF's unsigned LEB128 encoding.
|
2016-07-28 13:04:41 -04:00
|
|
|
func Uleb128put(ctxt Context, s Sym, v int64) {
|
2017-03-26 15:16:11 -07:00
|
|
|
b := sevenBitU(v)
|
|
|
|
|
if b == nil {
|
|
|
|
|
var encbuf [20]byte
|
|
|
|
|
b = AppendUleb128(encbuf[:0], uint64(v))
|
|
|
|
|
}
|
2016-07-28 13:04:41 -04:00
|
|
|
ctxt.AddBytes(s, b)
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 15:16:11 -07:00
|
|
|
// Sleb128put appends v to s using DWARF's signed LEB128 encoding.
|
2016-07-28 13:04:41 -04:00
|
|
|
func Sleb128put(ctxt Context, s Sym, v int64) {
|
2017-03-26 15:16:11 -07:00
|
|
|
b := sevenBitS(v)
|
|
|
|
|
if b == nil {
|
|
|
|
|
var encbuf [20]byte
|
|
|
|
|
b = AppendSleb128(encbuf[:0], v)
|
|
|
|
|
}
|
2016-07-28 13:04:41 -04:00
|
|
|
ctxt.AddBytes(s, b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2019-04-30 14:45:35 -04:00
|
|
|
* Defining Abbrevs. This is hardcoded on a per-platform basis (that is,
|
|
|
|
|
* each platform will see a fixed abbrev table for all objects); the number
|
|
|
|
|
* of abbrev entries is fairly small (compared to C++ objects). The DWARF
|
|
|
|
|
* spec places no restriction on the ordering of attributes in the
|
|
|
|
|
* Abbrevs and DIEs, and we will always write them out in the order
|
|
|
|
|
* of declaration in the abbrev.
|
2016-07-28 13:04:41 -04:00
|
|
|
*/
|
|
|
|
|
type dwAttrForm struct {
|
|
|
|
|
attr uint16
|
|
|
|
|
form uint8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Go-specific type attributes.
|
|
|
|
|
const (
|
|
|
|
|
DW_AT_go_kind = 0x2900
|
|
|
|
|
DW_AT_go_key = 0x2901
|
|
|
|
|
DW_AT_go_elem = 0x2902
|
2017-04-26 17:58:31 -04:00
|
|
|
// Attribute for DW_TAG_member of a struct type.
|
|
|
|
|
// Nonzero value indicates the struct field is an embedded field.
|
|
|
|
|
DW_AT_go_embedded_field = 0x2903
|
2018-04-12 17:07:14 -04:00
|
|
|
DW_AT_go_runtime_type = 0x2904
|
2016-07-28 13:04:41 -04:00
|
|
|
|
2019-02-25 13:56:18 +01:00
|
|
|
DW_AT_go_package_name = 0x2905 // Attribute for DW_TAG_compile_unit
|
2021-08-25 10:08:07 +02:00
|
|
|
DW_AT_go_dict_index = 0x2906 // Attribute for DW_TAG_typedef_type, index of the dictionary entry describing the real type of this type shape
|
2019-02-25 13:56:18 +01:00
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
DW_AT_internal_location = 253 // params and locals; not emitted
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Index into the abbrevs table below.
|
|
|
|
|
const (
|
|
|
|
|
DW_ABRV_NULL = iota
|
|
|
|
|
DW_ABRV_COMPUNIT
|
2018-09-25 11:52:24 +02:00
|
|
|
DW_ABRV_COMPUNIT_TEXTLESS
|
2016-07-28 13:04:41 -04:00
|
|
|
DW_ABRV_FUNCTION
|
2021-09-03 15:04:52 +02:00
|
|
|
DW_ABRV_WRAPPER
|
2017-10-06 11:32:28 -04:00
|
|
|
DW_ABRV_FUNCTION_ABSTRACT
|
|
|
|
|
DW_ABRV_FUNCTION_CONCRETE
|
2021-09-03 15:04:52 +02:00
|
|
|
DW_ABRV_WRAPPER_CONCRETE
|
2017-10-06 11:32:28 -04:00
|
|
|
DW_ABRV_INLINED_SUBROUTINE
|
|
|
|
|
DW_ABRV_INLINED_SUBROUTINE_RANGES
|
2016-07-28 13:04:41 -04:00
|
|
|
DW_ABRV_VARIABLE
|
2017-09-03 11:59:18 +02:00
|
|
|
DW_ABRV_INT_CONSTANT
|
2016-07-28 13:04:41 -04:00
|
|
|
DW_ABRV_AUTO
|
2017-07-24 13:37:04 -04:00
|
|
|
DW_ABRV_AUTO_LOCLIST
|
2017-10-06 11:32:28 -04:00
|
|
|
DW_ABRV_AUTO_ABSTRACT
|
|
|
|
|
DW_ABRV_AUTO_CONCRETE
|
|
|
|
|
DW_ABRV_AUTO_CONCRETE_LOCLIST
|
2016-07-28 13:04:41 -04:00
|
|
|
DW_ABRV_PARAM
|
2017-07-24 13:37:04 -04:00
|
|
|
DW_ABRV_PARAM_LOCLIST
|
2017-10-06 11:32:28 -04:00
|
|
|
DW_ABRV_PARAM_ABSTRACT
|
|
|
|
|
DW_ABRV_PARAM_CONCRETE
|
|
|
|
|
DW_ABRV_PARAM_CONCRETE_LOCLIST
|
2017-05-02 16:46:01 +02:00
|
|
|
DW_ABRV_LEXICAL_BLOCK_RANGES
|
|
|
|
|
DW_ABRV_LEXICAL_BLOCK_SIMPLE
|
2016-07-28 13:04:41 -04:00
|
|
|
DW_ABRV_STRUCTFIELD
|
|
|
|
|
DW_ABRV_FUNCTYPEPARAM
|
|
|
|
|
DW_ABRV_DOTDOTDOT
|
|
|
|
|
DW_ABRV_ARRAYRANGE
|
|
|
|
|
DW_ABRV_NULLTYPE
|
|
|
|
|
DW_ABRV_BASETYPE
|
|
|
|
|
DW_ABRV_ARRAYTYPE
|
|
|
|
|
DW_ABRV_CHANTYPE
|
|
|
|
|
DW_ABRV_FUNCTYPE
|
|
|
|
|
DW_ABRV_IFACETYPE
|
|
|
|
|
DW_ABRV_MAPTYPE
|
|
|
|
|
DW_ABRV_PTRTYPE
|
|
|
|
|
DW_ABRV_BARE_PTRTYPE // only for void*, no DW_AT_type attr to please gdb 6.
|
|
|
|
|
DW_ABRV_SLICETYPE
|
|
|
|
|
DW_ABRV_STRINGTYPE
|
|
|
|
|
DW_ABRV_STRUCTTYPE
|
|
|
|
|
DW_ABRV_TYPEDECL
|
2021-08-25 10:08:07 +02:00
|
|
|
DW_ABRV_DICT_INDEX
|
2016-07-28 13:04:41 -04:00
|
|
|
DW_NABRV
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type dwAbbrev struct {
|
|
|
|
|
tag uint8
|
|
|
|
|
children uint8
|
|
|
|
|
attr []dwAttrForm
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-30 14:45:35 -04:00
|
|
|
var abbrevsFinalized bool
|
|
|
|
|
|
|
|
|
|
// expandPseudoForm takes an input DW_FORM_xxx value and translates it
|
|
|
|
|
// into a platform-appropriate concrete form. Existing concrete/real
|
|
|
|
|
// DW_FORM values are left untouched. For the moment the only
|
|
|
|
|
// pseudo-form is DW_FORM_udata_pseudo, which gets expanded to
|
|
|
|
|
// DW_FORM_data4 on Darwin and DW_FORM_udata everywhere else. See
|
|
|
|
|
// issue #31459 for more context.
|
|
|
|
|
func expandPseudoForm(form uint8) uint8 {
|
|
|
|
|
// Is this a pseudo-form?
|
|
|
|
|
if form != DW_FORM_udata_pseudo {
|
|
|
|
|
return form
|
|
|
|
|
}
|
|
|
|
|
expandedForm := DW_FORM_udata
|
2021-04-15 23:05:49 -04:00
|
|
|
if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" {
|
2019-04-30 14:45:35 -04:00
|
|
|
expandedForm = DW_FORM_data4
|
|
|
|
|
}
|
|
|
|
|
return uint8(expandedForm)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Abbrevs() returns the finalized abbrev array for the platform,
|
|
|
|
|
// expanding any DW_FORM pseudo-ops to real values.
|
2020-05-01 12:24:03 -04:00
|
|
|
func Abbrevs() []dwAbbrev {
|
2019-04-30 14:45:35 -04:00
|
|
|
if abbrevsFinalized {
|
2020-05-01 12:24:03 -04:00
|
|
|
return abbrevs[:]
|
2019-04-30 14:45:35 -04:00
|
|
|
}
|
|
|
|
|
for i := 1; i < DW_NABRV; i++ {
|
|
|
|
|
for j := 0; j < len(abbrevs[i].attr); j++ {
|
|
|
|
|
abbrevs[i].attr[j].form = expandPseudoForm(abbrevs[i].attr[j].form)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
abbrevsFinalized = true
|
2020-05-01 12:24:03 -04:00
|
|
|
return abbrevs[:]
|
2019-04-30 14:45:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// abbrevs is a raw table of abbrev entries; it needs to be post-processed
|
|
|
|
|
// by the Abbrevs() function above prior to being consumed, to expand
|
|
|
|
|
// the 'pseudo-form' entries below to real DWARF form values.
|
|
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
var abbrevs = [DW_NABRV]dwAbbrev{
|
|
|
|
|
/* The mandatory DW_ABRV_NULL entry. */
|
|
|
|
|
{0, 0, []dwAttrForm{}},
|
|
|
|
|
|
|
|
|
|
/* COMPUNIT */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_compile_unit,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_language, DW_FORM_data1},
|
2017-05-24 17:43:08 -04:00
|
|
|
{DW_AT_stmt_list, DW_FORM_sec_offset},
|
2017-10-10 16:19:49 -04:00
|
|
|
{DW_AT_low_pc, DW_FORM_addr},
|
|
|
|
|
{DW_AT_ranges, DW_FORM_sec_offset},
|
2016-07-28 13:04:41 -04:00
|
|
|
{DW_AT_comp_dir, DW_FORM_string},
|
2017-05-08 14:37:02 -04:00
|
|
|
{DW_AT_producer, DW_FORM_string},
|
2019-02-25 13:56:18 +01:00
|
|
|
{DW_AT_go_package_name, DW_FORM_string},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2018-09-25 11:52:24 +02:00
|
|
|
/* COMPUNIT_TEXTLESS */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_compile_unit,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_language, DW_FORM_data1},
|
|
|
|
|
{DW_AT_comp_dir, DW_FORM_string},
|
|
|
|
|
{DW_AT_producer, DW_FORM_string},
|
2019-02-25 13:56:18 +01:00
|
|
|
{DW_AT_go_package_name, DW_FORM_string},
|
2018-09-25 11:52:24 +02:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
/* FUNCTION */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_subprogram,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_low_pc, DW_FORM_addr},
|
2017-09-06 22:05:24 -04:00
|
|
|
{DW_AT_high_pc, DW_FORM_addr},
|
2017-05-24 17:43:08 -04:00
|
|
|
{DW_AT_frame_base, DW_FORM_block1},
|
2017-10-24 16:08:46 -04:00
|
|
|
{DW_AT_decl_file, DW_FORM_data4},
|
2016-07-28 13:04:41 -04:00
|
|
|
{DW_AT_external, DW_FORM_flag},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2021-09-03 15:04:52 +02:00
|
|
|
/* WRAPPER */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_subprogram,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_low_pc, DW_FORM_addr},
|
|
|
|
|
{DW_AT_high_pc, DW_FORM_addr},
|
|
|
|
|
{DW_AT_frame_base, DW_FORM_block1},
|
|
|
|
|
{DW_AT_trampoline, DW_FORM_flag},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
/* FUNCTION_ABSTRACT */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_subprogram,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_inline, DW_FORM_data1},
|
|
|
|
|
{DW_AT_external, DW_FORM_flag},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* FUNCTION_CONCRETE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_subprogram,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_abstract_origin, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_low_pc, DW_FORM_addr},
|
|
|
|
|
{DW_AT_high_pc, DW_FORM_addr},
|
|
|
|
|
{DW_AT_frame_base, DW_FORM_block1},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2021-09-03 15:04:52 +02:00
|
|
|
/* WRAPPER_CONCRETE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_subprogram,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_abstract_origin, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_low_pc, DW_FORM_addr},
|
|
|
|
|
{DW_AT_high_pc, DW_FORM_addr},
|
|
|
|
|
{DW_AT_frame_base, DW_FORM_block1},
|
|
|
|
|
{DW_AT_trampoline, DW_FORM_flag},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
/* INLINED_SUBROUTINE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_inlined_subroutine,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_abstract_origin, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_low_pc, DW_FORM_addr},
|
|
|
|
|
{DW_AT_high_pc, DW_FORM_addr},
|
|
|
|
|
{DW_AT_call_file, DW_FORM_data4},
|
2019-04-30 14:45:35 -04:00
|
|
|
{DW_AT_call_line, DW_FORM_udata_pseudo}, // pseudo-form
|
2017-10-06 11:32:28 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* INLINED_SUBROUTINE_RANGES */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_inlined_subroutine,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_abstract_origin, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_ranges, DW_FORM_sec_offset},
|
|
|
|
|
{DW_AT_call_file, DW_FORM_data4},
|
2019-04-30 14:45:35 -04:00
|
|
|
{DW_AT_call_line, DW_FORM_udata_pseudo}, // pseudo-form
|
2017-10-06 11:32:28 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
/* VARIABLE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_variable,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_location, DW_FORM_block1},
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_external, DW_FORM_flag},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2017-09-03 11:59:18 +02:00
|
|
|
/* INT CONSTANT */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_constant,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_const_value, DW_FORM_sdata},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
/* AUTO */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_variable,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
2017-05-26 15:34:56 -04:00
|
|
|
{DW_AT_decl_line, DW_FORM_udata},
|
2016-07-28 13:04:41 -04:00
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
2017-10-06 11:32:28 -04:00
|
|
|
{DW_AT_location, DW_FORM_block1},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2017-07-24 13:37:04 -04:00
|
|
|
/* AUTO_LOCLIST */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_variable,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
2017-08-25 12:27:15 -04:00
|
|
|
{DW_AT_decl_line, DW_FORM_udata},
|
2017-10-06 11:32:28 -04:00
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
2017-07-24 13:37:04 -04:00
|
|
|
{DW_AT_location, DW_FORM_sec_offset},
|
2017-10-06 11:32:28 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* AUTO_ABSTRACT */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_variable,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_decl_line, DW_FORM_udata},
|
2017-07-24 13:37:04 -04:00
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
/* AUTO_CONCRETE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_variable,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_abstract_origin, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_location, DW_FORM_block1},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* AUTO_CONCRETE_LOCLIST */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_variable,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_abstract_origin, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_location, DW_FORM_sec_offset},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
/* PARAM */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_formal_parameter,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
2017-10-18 13:45:03 -04:00
|
|
|
{DW_AT_variable_parameter, DW_FORM_flag},
|
2017-05-26 15:34:56 -04:00
|
|
|
{DW_AT_decl_line, DW_FORM_udata},
|
2016-07-28 13:04:41 -04:00
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
2017-10-06 11:32:28 -04:00
|
|
|
{DW_AT_location, DW_FORM_block1},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
2017-07-24 13:37:04 -04:00
|
|
|
|
|
|
|
|
/* PARAM_LOCLIST */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_formal_parameter,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
2017-10-18 13:45:03 -04:00
|
|
|
{DW_AT_variable_parameter, DW_FORM_flag},
|
2017-08-25 12:27:15 -04:00
|
|
|
{DW_AT_decl_line, DW_FORM_udata},
|
2017-10-06 11:32:28 -04:00
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
2017-07-24 13:37:04 -04:00
|
|
|
{DW_AT_location, DW_FORM_sec_offset},
|
2017-10-06 11:32:28 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* PARAM_ABSTRACT */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_formal_parameter,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_variable_parameter, DW_FORM_flag},
|
2017-07-24 13:37:04 -04:00
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
/* PARAM_CONCRETE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_formal_parameter,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_abstract_origin, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_location, DW_FORM_block1},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* PARAM_CONCRETE_LOCLIST */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_formal_parameter,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_abstract_origin, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_location, DW_FORM_sec_offset},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2017-05-02 16:46:01 +02:00
|
|
|
/* LEXICAL_BLOCK_RANGES */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_lexical_block,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
2017-05-24 17:43:08 -04:00
|
|
|
{DW_AT_ranges, DW_FORM_sec_offset},
|
2017-05-02 16:46:01 +02:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* LEXICAL_BLOCK_SIMPLE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_lexical_block,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_low_pc, DW_FORM_addr},
|
2017-09-06 22:05:24 -04:00
|
|
|
{DW_AT_high_pc, DW_FORM_addr},
|
2017-05-02 16:46:01 +02:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
/* STRUCTFIELD */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_member,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
2017-07-26 15:58:25 -04:00
|
|
|
{DW_AT_data_member_location, DW_FORM_udata},
|
2016-07-28 13:04:41 -04:00
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
2017-04-26 17:58:31 -04:00
|
|
|
{DW_AT_go_embedded_field, DW_FORM_flag},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* FUNCTYPEPARAM */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_formal_parameter,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
|
|
|
|
|
// No name!
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* DOTDOTDOT */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_unspecified_parameters,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* ARRAYRANGE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_subrange_type,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
|
|
|
|
|
// No name!
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_count, DW_FORM_udata},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Below here are the types considered public by ispubtype
|
|
|
|
|
/* NULLTYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_unspecified_type,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* BASETYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_base_type,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_encoding, DW_FORM_data1},
|
|
|
|
|
{DW_AT_byte_size, DW_FORM_data1},
|
|
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* ARRAYTYPE */
|
|
|
|
|
// child is subrange with upper bound
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_array_type,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_byte_size, DW_FORM_udata},
|
|
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* CHANTYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_typedef,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
{DW_AT_go_elem, DW_FORM_ref_addr},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* FUNCTYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_subroutine_type,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
2017-08-14 15:07:57 -07:00
|
|
|
{DW_AT_byte_size, DW_FORM_udata},
|
2016-07-28 13:04:41 -04:00
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* IFACETYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_typedef,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* MAPTYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_typedef,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
{DW_AT_go_key, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_go_elem, DW_FORM_ref_addr},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* PTRTYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_pointer_type,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* BARE_PTRTYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_pointer_type,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* SLICETYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_structure_type,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_byte_size, DW_FORM_udata},
|
|
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
{DW_AT_go_elem, DW_FORM_ref_addr},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* STRINGTYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_structure_type,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_byte_size, DW_FORM_udata},
|
|
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* STRUCTTYPE */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_structure_type,
|
|
|
|
|
DW_CHILDREN_yes,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_byte_size, DW_FORM_udata},
|
|
|
|
|
{DW_AT_go_kind, DW_FORM_data1},
|
2018-04-12 17:07:14 -04:00
|
|
|
{DW_AT_go_runtime_type, DW_FORM_addr},
|
2016-07-28 13:04:41 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/* TYPEDECL */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_typedef,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-08-25 10:08:07 +02:00
|
|
|
|
|
|
|
|
/* DICT_INDEX */
|
|
|
|
|
{
|
|
|
|
|
DW_TAG_typedef,
|
|
|
|
|
DW_CHILDREN_no,
|
|
|
|
|
[]dwAttrForm{
|
|
|
|
|
{DW_AT_name, DW_FORM_string},
|
|
|
|
|
{DW_AT_type, DW_FORM_ref_addr},
|
|
|
|
|
{DW_AT_go_dict_index, DW_FORM_udata},
|
|
|
|
|
},
|
|
|
|
|
},
|
2016-07-28 13:04:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAbbrev returns the contents of the .debug_abbrev section.
|
|
|
|
|
func GetAbbrev() []byte {
|
2019-04-30 14:45:35 -04:00
|
|
|
abbrevs := Abbrevs()
|
2016-07-28 13:04:41 -04:00
|
|
|
var buf []byte
|
|
|
|
|
for i := 1; i < DW_NABRV; i++ {
|
|
|
|
|
// See section 7.5.3
|
|
|
|
|
buf = AppendUleb128(buf, uint64(i))
|
|
|
|
|
buf = AppendUleb128(buf, uint64(abbrevs[i].tag))
|
2018-02-25 20:08:22 +09:00
|
|
|
buf = append(buf, abbrevs[i].children)
|
2016-07-28 13:04:41 -04:00
|
|
|
for _, f := range abbrevs[i].attr {
|
|
|
|
|
buf = AppendUleb128(buf, uint64(f.attr))
|
|
|
|
|
buf = AppendUleb128(buf, uint64(f.form))
|
|
|
|
|
}
|
|
|
|
|
buf = append(buf, 0, 0)
|
|
|
|
|
}
|
|
|
|
|
return append(buf, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Debugging Information Entries and their attributes.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// DWAttr represents an attribute of a DWDie.
|
|
|
|
|
//
|
|
|
|
|
// For DW_CLS_string and _block, value should contain the length, and
|
|
|
|
|
// data the data, for _reference, value is 0 and data is a DWDie* to
|
|
|
|
|
// the referenced instance, for all others, value is the whole thing
|
|
|
|
|
// and data is null.
|
|
|
|
|
type DWAttr struct {
|
|
|
|
|
Link *DWAttr
|
|
|
|
|
Atr uint16 // DW_AT_
|
|
|
|
|
Cls uint8 // DW_CLS_
|
|
|
|
|
Value int64
|
|
|
|
|
Data interface{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DWDie represents a DWARF debug info entry.
|
|
|
|
|
type DWDie struct {
|
|
|
|
|
Abbrev int
|
|
|
|
|
Link *DWDie
|
|
|
|
|
Child *DWDie
|
|
|
|
|
Attr *DWAttr
|
|
|
|
|
Sym Sym
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, data interface{}) error {
|
|
|
|
|
switch form {
|
|
|
|
|
case DW_FORM_addr: // address
|
2018-04-12 17:07:14 -04:00
|
|
|
// Allow nil addresses for DW_AT_go_runtime_type.
|
|
|
|
|
if data == nil && value == 0 {
|
|
|
|
|
ctxt.AddInt(s, ctxt.PtrSize(), 0)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if cls == DW_CLS_GO_TYPEREF {
|
|
|
|
|
ctxt.AddSectionOffset(s, ctxt.PtrSize(), data, value)
|
|
|
|
|
break
|
|
|
|
|
}
|
2016-07-28 13:04:41 -04:00
|
|
|
ctxt.AddAddress(s, data, value)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_block1: // block
|
|
|
|
|
if cls == DW_CLS_ADDRESS {
|
|
|
|
|
ctxt.AddInt(s, 1, int64(1+ctxt.PtrSize()))
|
|
|
|
|
ctxt.AddInt(s, 1, DW_OP_addr)
|
|
|
|
|
ctxt.AddAddress(s, data, 0)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value &= 0xff
|
|
|
|
|
ctxt.AddInt(s, 1, value)
|
|
|
|
|
p := data.([]byte)[:value]
|
|
|
|
|
ctxt.AddBytes(s, p)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_block2: // block
|
|
|
|
|
value &= 0xffff
|
|
|
|
|
|
|
|
|
|
ctxt.AddInt(s, 2, value)
|
|
|
|
|
p := data.([]byte)[:value]
|
|
|
|
|
ctxt.AddBytes(s, p)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_block4: // block
|
|
|
|
|
value &= 0xffffffff
|
|
|
|
|
|
|
|
|
|
ctxt.AddInt(s, 4, value)
|
|
|
|
|
p := data.([]byte)[:value]
|
|
|
|
|
ctxt.AddBytes(s, p)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_block: // block
|
|
|
|
|
Uleb128put(ctxt, s, value)
|
|
|
|
|
|
|
|
|
|
p := data.([]byte)[:value]
|
|
|
|
|
ctxt.AddBytes(s, p)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_data1: // constant
|
|
|
|
|
ctxt.AddInt(s, 1, value)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_data2: // constant
|
|
|
|
|
ctxt.AddInt(s, 2, value)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr
|
2017-05-02 16:46:01 +02:00
|
|
|
if cls == DW_CLS_PTR { // DW_AT_stmt_list and DW_AT_ranges
|
2018-09-28 16:44:30 +02:00
|
|
|
ctxt.AddDWARFAddrSectionOffset(s, data, value)
|
2016-07-28 13:04:41 -04:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
ctxt.AddInt(s, 4, value)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr
|
|
|
|
|
ctxt.AddInt(s, 8, value)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_sdata: // constant
|
|
|
|
|
Sleb128put(ctxt, s, value)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_udata: // constant
|
|
|
|
|
Uleb128put(ctxt, s, value)
|
|
|
|
|
|
|
|
|
|
case DW_FORM_string: // string
|
|
|
|
|
str := data.(string)
|
|
|
|
|
ctxt.AddString(s, str)
|
|
|
|
|
// TODO(ribrdb): verify padded strings are never used and remove this
|
|
|
|
|
for i := int64(len(str)); i < value; i++ {
|
|
|
|
|
ctxt.AddInt(s, 1, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case DW_FORM_flag: // flag
|
|
|
|
|
if value != 0 {
|
|
|
|
|
ctxt.AddInt(s, 1, 1)
|
|
|
|
|
} else {
|
|
|
|
|
ctxt.AddInt(s, 1, 0)
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-24 17:43:08 -04:00
|
|
|
// As of DWARF 3 the ref_addr is always 32 bits, unless emitting a large
|
2016-07-28 13:04:41 -04:00
|
|
|
// (> 4 GB of debug info aka "64-bit") unit, which we don't implement.
|
|
|
|
|
case DW_FORM_ref_addr: // reference to a DIE in the .info section
|
2017-05-24 17:43:08 -04:00
|
|
|
fallthrough
|
|
|
|
|
case DW_FORM_sec_offset: // offset into a DWARF section other than .info
|
2016-07-28 13:04:41 -04:00
|
|
|
if data == nil {
|
|
|
|
|
return fmt.Errorf("dwarf: null reference in %d", abbrev)
|
|
|
|
|
}
|
2018-09-28 16:44:30 +02:00
|
|
|
ctxt.AddDWARFAddrSectionOffset(s, data, value)
|
2016-07-28 13:04:41 -04:00
|
|
|
|
|
|
|
|
case DW_FORM_ref1, // reference within the compilation unit
|
|
|
|
|
DW_FORM_ref2, // reference
|
|
|
|
|
DW_FORM_ref4, // reference
|
|
|
|
|
DW_FORM_ref8, // reference
|
|
|
|
|
DW_FORM_ref_udata, // reference
|
|
|
|
|
|
|
|
|
|
DW_FORM_strp, // string
|
|
|
|
|
DW_FORM_indirect: // (see Section 7.5.3)
|
|
|
|
|
fallthrough
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("dwarf: unsupported attribute form %d / class %d", form, cls)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PutAttrs writes the attributes for a DIE to symbol 's'.
|
|
|
|
|
//
|
|
|
|
|
// Note that we can (and do) add arbitrary attributes to a DIE, but
|
|
|
|
|
// only the ones actually listed in the Abbrev will be written out.
|
|
|
|
|
func PutAttrs(ctxt Context, s Sym, abbrev int, attr *DWAttr) {
|
2019-04-30 14:45:35 -04:00
|
|
|
abbrevs := Abbrevs()
|
2016-07-28 13:04:41 -04:00
|
|
|
Outer:
|
|
|
|
|
for _, f := range abbrevs[abbrev].attr {
|
|
|
|
|
for ap := attr; ap != nil; ap = ap.Link {
|
|
|
|
|
if ap.Atr == f.attr {
|
|
|
|
|
putattr(ctxt, s, abbrev, int(f.form), int(ap.Cls), ap.Value, ap.Data)
|
|
|
|
|
continue Outer
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
putattr(ctxt, s, abbrev, int(f.form), 0, 0, nil)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-02 15:18:43 +00:00
|
|
|
// HasChildren reports whether 'die' uses an abbrev that supports children.
|
2016-07-28 13:04:41 -04:00
|
|
|
func HasChildren(die *DWDie) bool {
|
2019-04-30 14:45:35 -04:00
|
|
|
abbrevs := Abbrevs()
|
2016-07-28 13:04:41 -04:00
|
|
|
return abbrevs[die.Abbrev].children != 0
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-03 11:59:18 +02:00
|
|
|
// PutIntConst writes a DIE for an integer constant
|
|
|
|
|
func PutIntConst(ctxt Context, info, typ Sym, name string, val int64) {
|
|
|
|
|
Uleb128put(ctxt, info, DW_ABRV_INT_CONSTANT)
|
|
|
|
|
putattr(ctxt, info, DW_ABRV_INT_CONSTANT, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
|
|
|
|
|
putattr(ctxt, info, DW_ABRV_INT_CONSTANT, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, typ)
|
|
|
|
|
putattr(ctxt, info, DW_ABRV_INT_CONSTANT, DW_FORM_sdata, DW_CLS_CONSTANT, val, nil)
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-07 16:25:41 -05:00
|
|
|
// PutGlobal writes a DIE for a global variable.
|
|
|
|
|
func PutGlobal(ctxt Context, info, typ, gvar Sym, name string) {
|
|
|
|
|
Uleb128put(ctxt, info, DW_ABRV_VARIABLE)
|
|
|
|
|
putattr(ctxt, info, DW_ABRV_VARIABLE, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
|
|
|
|
|
putattr(ctxt, info, DW_ABRV_VARIABLE, DW_FORM_block1, DW_CLS_ADDRESS, 0, gvar)
|
|
|
|
|
putattr(ctxt, info, DW_ABRV_VARIABLE, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, typ)
|
|
|
|
|
putattr(ctxt, info, DW_ABRV_VARIABLE, DW_FORM_flag, DW_CLS_FLAG, 1, nil)
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-02 17:26:49 -04:00
|
|
|
// PutBasedRanges writes a range table to sym. All addresses in ranges are
|
|
|
|
|
// relative to some base address, which must be arranged by the caller
|
|
|
|
|
// (e.g., with a DW_AT_low_pc attribute, or in a BASE-prefixed range).
|
|
|
|
|
func PutBasedRanges(ctxt Context, sym Sym, ranges []Range) {
|
2017-10-10 16:19:49 -04:00
|
|
|
ps := ctxt.PtrSize()
|
|
|
|
|
// Write ranges.
|
|
|
|
|
for _, r := range ranges {
|
2018-02-19 15:26:49 +01:00
|
|
|
ctxt.AddInt(sym, ps, r.Start)
|
|
|
|
|
ctxt.AddInt(sym, ps, r.End)
|
2017-10-10 16:19:49 -04:00
|
|
|
}
|
|
|
|
|
// Write trailer.
|
|
|
|
|
ctxt.AddInt(sym, ps, 0)
|
|
|
|
|
ctxt.AddInt(sym, ps, 0)
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-02 17:26:49 -04:00
|
|
|
// PutRanges writes a range table to s.Ranges.
|
|
|
|
|
// All addresses in ranges are relative to s.base.
|
|
|
|
|
func (s *FnState) PutRanges(ctxt Context, ranges []Range) {
|
|
|
|
|
ps := ctxt.PtrSize()
|
|
|
|
|
sym, base := s.Ranges, s.StartPC
|
|
|
|
|
|
|
|
|
|
if s.UseBASEntries {
|
|
|
|
|
// Using a Base Address Selection Entry reduces the number of relocations, but
|
|
|
|
|
// this is not done on macOS because it is not supported by dsymutil/dwarfdump/lldb
|
|
|
|
|
ctxt.AddInt(sym, ps, -1)
|
|
|
|
|
ctxt.AddAddress(sym, base, 0)
|
|
|
|
|
PutBasedRanges(ctxt, sym, ranges)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write ranges full of relocations
|
|
|
|
|
for _, r := range ranges {
|
|
|
|
|
ctxt.AddCURelativeAddress(sym, base, r.Start)
|
|
|
|
|
ctxt.AddCURelativeAddress(sym, base, r.End)
|
|
|
|
|
}
|
|
|
|
|
// Write trailer.
|
|
|
|
|
ctxt.AddInt(sym, ps, 0)
|
|
|
|
|
ctxt.AddInt(sym, ps, 0)
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
// Return TRUE if the inlined call in the specified slot is empty,
|
|
|
|
|
// meaning it has a zero-length range (no instructions), and all
|
|
|
|
|
// of its children are empty.
|
|
|
|
|
func isEmptyInlinedCall(slot int, calls *InlCalls) bool {
|
|
|
|
|
ic := &calls.Calls[slot]
|
|
|
|
|
if ic.InlIndex == -2 {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
live := false
|
|
|
|
|
for _, k := range ic.Children {
|
|
|
|
|
if !isEmptyInlinedCall(k, calls) {
|
|
|
|
|
live = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(ic.Ranges) > 0 {
|
|
|
|
|
live = true
|
|
|
|
|
}
|
|
|
|
|
if !live {
|
|
|
|
|
ic.InlIndex = -2
|
|
|
|
|
}
|
|
|
|
|
return !live
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Slot -1: return top-level inlines
|
|
|
|
|
// Slot >= 0: return children of that slot
|
|
|
|
|
func inlChildren(slot int, calls *InlCalls) []int {
|
|
|
|
|
var kids []int
|
|
|
|
|
if slot != -1 {
|
|
|
|
|
for _, k := range calls.Calls[slot].Children {
|
|
|
|
|
if !isEmptyInlinedCall(k, calls) {
|
|
|
|
|
kids = append(kids, k)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for k := 0; k < len(calls.Calls); k += 1 {
|
|
|
|
|
if calls.Calls[k].Root && !isEmptyInlinedCall(k, calls) {
|
|
|
|
|
kids = append(kids, k)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return kids
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func inlinedVarTable(inlcalls *InlCalls) map[*Var]bool {
|
|
|
|
|
vars := make(map[*Var]bool)
|
|
|
|
|
for _, ic := range inlcalls.Calls {
|
|
|
|
|
for _, v := range ic.InlVars {
|
|
|
|
|
vars[v] = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return vars
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The s.Scopes slice contains variables were originally part of the
|
|
|
|
|
// function being emitted, as well as variables that were imported
|
|
|
|
|
// from various callee functions during the inlining process. This
|
|
|
|
|
// function prunes out any variables from the latter category (since
|
|
|
|
|
// they will be emitted as part of DWARF inlined_subroutine DIEs) and
|
|
|
|
|
// then generates scopes for vars in the former category.
|
|
|
|
|
func putPrunedScopes(ctxt Context, s *FnState, fnabbrev int) error {
|
|
|
|
|
if len(s.Scopes) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
scopes := make([]Scope, len(s.Scopes), len(s.Scopes))
|
|
|
|
|
pvars := inlinedVarTable(&s.InlCalls)
|
|
|
|
|
for k, s := range s.Scopes {
|
|
|
|
|
var pruned Scope = Scope{Parent: s.Parent, Ranges: s.Ranges}
|
|
|
|
|
for i := 0; i < len(s.Vars); i++ {
|
|
|
|
|
_, found := pvars[s.Vars[i]]
|
|
|
|
|
if !found {
|
|
|
|
|
pruned.Vars = append(pruned.Vars, s.Vars[i])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sort.Sort(byChildIndex(pruned.Vars))
|
|
|
|
|
scopes[k] = pruned
|
|
|
|
|
}
|
2021-08-25 10:08:07 +02:00
|
|
|
|
|
|
|
|
s.dictIndexToOffset = putparamtypes(ctxt, s, scopes, fnabbrev)
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
var encbuf [20]byte
|
|
|
|
|
if putscope(ctxt, s, scopes, 0, fnabbrev, encbuf[:0]) < int32(len(scopes)) {
|
|
|
|
|
return errors.New("multiple toplevel scopes")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit DWARF attributes and child DIEs for an 'abstract' subprogram.
|
|
|
|
|
// The abstract subprogram DIE for a function contains its
|
|
|
|
|
// location-independent attributes (name, type, etc). Other instances
|
|
|
|
|
// of the function (any inlined copy of it, or the single out-of-line
|
|
|
|
|
// 'concrete' instance) will contain a pointer back to this abstract
|
|
|
|
|
// DIE (as a space-saving measure, so that name/type etc doesn't have
|
|
|
|
|
// to be repeated for each inlined copy).
|
|
|
|
|
func PutAbstractFunc(ctxt Context, s *FnState) error {
|
|
|
|
|
|
|
|
|
|
if logDwarf {
|
|
|
|
|
ctxt.Logf("PutAbstractFunc(%v)\n", s.Absfn)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
abbrev := DW_ABRV_FUNCTION_ABSTRACT
|
|
|
|
|
Uleb128put(ctxt, s.Absfn, int64(abbrev))
|
|
|
|
|
|
|
|
|
|
fullname := s.Name
|
|
|
|
|
if strings.HasPrefix(s.Name, "\"\".") {
|
|
|
|
|
// Generate a fully qualified name for the function in the
|
|
|
|
|
// abstract case. This is so as to avoid the need for the
|
|
|
|
|
// linker to process the DIE with patchDWARFName(); we can't
|
|
|
|
|
// allow the name attribute of an abstract subprogram DIE to
|
|
|
|
|
// be rewritten, since it would change the offsets of the
|
|
|
|
|
// child DIEs (which we're relying on in order for abstract
|
|
|
|
|
// origin references to work).
|
2018-07-10 12:23:31 -04:00
|
|
|
fullname = objabi.PathToPrefix(s.Importpath) + "." + s.Name[3:]
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
putattr(ctxt, s.Absfn, abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(fullname)), fullname)
|
|
|
|
|
|
|
|
|
|
// DW_AT_inlined value
|
|
|
|
|
putattr(ctxt, s.Absfn, abbrev, DW_FORM_data1, DW_CLS_CONSTANT, int64(DW_INL_inlined), nil)
|
|
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
var ev int64
|
2017-10-06 11:32:28 -04:00
|
|
|
if s.External {
|
2016-07-28 13:04:41 -04:00
|
|
|
ev = 1
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
putattr(ctxt, s.Absfn, abbrev, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
|
|
|
|
|
|
|
|
|
|
// Child variables (may be empty)
|
|
|
|
|
var flattened []*Var
|
|
|
|
|
|
|
|
|
|
// This slice will hold the offset in bytes for each child var DIE
|
|
|
|
|
// with respect to the start of the parent subprogram DIE.
|
|
|
|
|
var offsets []int32
|
|
|
|
|
|
|
|
|
|
// Scopes/vars
|
|
|
|
|
if len(s.Scopes) > 0 {
|
|
|
|
|
// For abstract subprogram DIEs we want to flatten out scope info:
|
|
|
|
|
// lexical scope DIEs contain range and/or hi/lo PC attributes,
|
|
|
|
|
// which we explicitly don't want for the abstract subprogram DIE.
|
|
|
|
|
pvars := inlinedVarTable(&s.InlCalls)
|
|
|
|
|
for _, scope := range s.Scopes {
|
|
|
|
|
for i := 0; i < len(scope.Vars); i++ {
|
|
|
|
|
_, found := pvars[scope.Vars[i]]
|
2017-12-11 15:53:31 -05:00
|
|
|
if found || !scope.Vars[i].IsInAbstract {
|
|
|
|
|
continue
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2017-12-11 15:53:31 -05:00
|
|
|
flattened = append(flattened, scope.Vars[i])
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(flattened) > 0 {
|
|
|
|
|
sort.Sort(byChildIndex(flattened))
|
|
|
|
|
|
2017-12-11 15:53:31 -05:00
|
|
|
if logDwarf {
|
|
|
|
|
ctxt.Logf("putAbstractScope(%v): vars:", s.Info)
|
|
|
|
|
for i, v := range flattened {
|
|
|
|
|
ctxt.Logf(" %d:%s", i, v.Name)
|
|
|
|
|
}
|
|
|
|
|
ctxt.Logf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
// This slice will hold the offset in bytes for each child
|
|
|
|
|
// variable DIE with respect to the start of the parent
|
|
|
|
|
// subprogram DIE.
|
|
|
|
|
for _, v := range flattened {
|
|
|
|
|
offsets = append(offsets, int32(ctxt.CurrentOffset(s.Absfn)))
|
|
|
|
|
putAbstractVar(ctxt, s.Absfn, v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ctxt.RecordChildDieOffsets(s.Absfn, flattened, offsets)
|
|
|
|
|
|
|
|
|
|
Uleb128put(ctxt, s.Absfn, 0)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit DWARF attributes and child DIEs for an inlined subroutine. The
|
|
|
|
|
// first attribute of an inlined subroutine DIE is a reference back to
|
|
|
|
|
// its corresponding 'abstract' DIE (containing location-independent
|
|
|
|
|
// attributes such as name, type, etc). Inlined subroutine DIEs can
|
|
|
|
|
// have other inlined subroutine DIEs as children.
|
2021-07-01 11:44:43 -07:00
|
|
|
func putInlinedFunc(ctxt Context, s *FnState, callIdx int) error {
|
2017-10-06 11:32:28 -04:00
|
|
|
ic := s.InlCalls.Calls[callIdx]
|
|
|
|
|
callee := ic.AbsFunSym
|
|
|
|
|
|
|
|
|
|
abbrev := DW_ABRV_INLINED_SUBROUTINE_RANGES
|
|
|
|
|
if len(ic.Ranges) == 1 {
|
|
|
|
|
abbrev = DW_ABRV_INLINED_SUBROUTINE
|
|
|
|
|
}
|
|
|
|
|
Uleb128put(ctxt, s.Info, int64(abbrev))
|
|
|
|
|
|
|
|
|
|
if logDwarf {
|
2021-07-01 11:44:43 -07:00
|
|
|
ctxt.Logf("putInlinedFunc(callee=%v,abbrev=%d)\n", callee, abbrev)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Abstract origin.
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, callee)
|
|
|
|
|
|
|
|
|
|
if abbrev == DW_ABRV_INLINED_SUBROUTINE_RANGES {
|
2020-02-19 16:16:17 -05:00
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges)
|
2019-04-02 17:26:49 -04:00
|
|
|
s.PutRanges(ctxt, ic.Ranges)
|
2017-10-06 11:32:28 -04:00
|
|
|
} else {
|
|
|
|
|
st := ic.Ranges[0].Start
|
|
|
|
|
en := ic.Ranges[0].End
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, st, s.StartPC)
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, en, s.StartPC)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit call file, line attrs.
|
|
|
|
|
ctxt.AddFileRef(s.Info, ic.CallFile)
|
2019-04-30 14:45:35 -04:00
|
|
|
form := int(expandPseudoForm(DW_FORM_udata_pseudo))
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, form, DW_CLS_CONSTANT, int64(ic.CallLine), nil)
|
2017-10-06 11:32:28 -04:00
|
|
|
|
|
|
|
|
// Variables associated with this inlined routine instance.
|
|
|
|
|
vars := ic.InlVars
|
|
|
|
|
sort.Sort(byChildIndex(vars))
|
|
|
|
|
inlIndex := ic.InlIndex
|
|
|
|
|
var encbuf [20]byte
|
|
|
|
|
for _, v := range vars {
|
2017-12-11 15:53:31 -05:00
|
|
|
if !v.IsInAbstract {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
putvar(ctxt, s, v, callee, abbrev, inlIndex, encbuf[:0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Children of this inline.
|
|
|
|
|
for _, sib := range inlChildren(callIdx, &s.InlCalls) {
|
2021-07-01 11:44:43 -07:00
|
|
|
err := putInlinedFunc(ctxt, s, sib)
|
2017-10-06 11:32:28 -04:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Uleb128put(ctxt, s.Info, 0)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit DWARF attributes and child DIEs for a 'concrete' subprogram,
|
|
|
|
|
// meaning the out-of-line copy of a function that was inlined at some
|
|
|
|
|
// point during the compilation of its containing package. The first
|
|
|
|
|
// attribute for a concrete DIE is a reference to the 'abstract' DIE
|
|
|
|
|
// for the function (which holds location-independent attributes such
|
|
|
|
|
// as name, type), then the remainder of the attributes are specific
|
|
|
|
|
// to this instance (location, frame base, etc).
|
2021-09-03 15:04:52 +02:00
|
|
|
func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool) error {
|
2017-10-06 11:32:28 -04:00
|
|
|
if logDwarf {
|
|
|
|
|
ctxt.Logf("PutConcreteFunc(%v)\n", s.Info)
|
|
|
|
|
}
|
|
|
|
|
abbrev := DW_ABRV_FUNCTION_CONCRETE
|
2021-09-03 15:04:52 +02:00
|
|
|
if isWrapper {
|
|
|
|
|
abbrev = DW_ABRV_WRAPPER_CONCRETE
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
Uleb128put(ctxt, s.Info, int64(abbrev))
|
|
|
|
|
|
|
|
|
|
// Abstract origin.
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, s.Absfn)
|
|
|
|
|
|
|
|
|
|
// Start/end PC.
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC)
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC)
|
|
|
|
|
|
|
|
|
|
// cfa / frame base
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
|
|
|
|
|
|
2021-09-03 15:04:52 +02:00
|
|
|
if isWrapper {
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0)
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
// Scopes
|
|
|
|
|
if err := putPrunedScopes(ctxt, s, abbrev); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Inlined subroutines.
|
|
|
|
|
for _, sib := range inlChildren(-1, &s.InlCalls) {
|
2021-07-01 11:44:43 -07:00
|
|
|
err := putInlinedFunc(ctxt, s, sib)
|
2017-10-06 11:32:28 -04:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2017-03-07 18:59:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
|
|
|
|
|
Uleb128put(ctxt, s.Info, 0)
|
2017-05-02 16:46:01 +02:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
// Emit DWARF attributes and child DIEs for a subprogram. Here
|
|
|
|
|
// 'default' implies that the function in question was not inlined
|
|
|
|
|
// when its containing package was compiled (hence there is no need to
|
|
|
|
|
// emit an abstract version for it to use as a base for inlined
|
|
|
|
|
// routine records).
|
2021-09-03 15:04:52 +02:00
|
|
|
func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error {
|
2017-10-06 11:32:28 -04:00
|
|
|
if logDwarf {
|
|
|
|
|
ctxt.Logf("PutDefaultFunc(%v)\n", s.Info)
|
|
|
|
|
}
|
|
|
|
|
abbrev := DW_ABRV_FUNCTION
|
2021-09-03 15:04:52 +02:00
|
|
|
if isWrapper {
|
|
|
|
|
abbrev = DW_ABRV_WRAPPER
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
Uleb128put(ctxt, s.Info, int64(abbrev))
|
|
|
|
|
|
2019-11-02 10:42:50 -04:00
|
|
|
// Expand '"".' to import path.
|
|
|
|
|
name := s.Name
|
|
|
|
|
if s.Importpath != "" {
|
|
|
|
|
name = strings.Replace(name, "\"\".", objabi.PathToPrefix(s.Importpath)+".", -1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
putattr(ctxt, s.Info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
|
2017-10-06 11:32:28 -04:00
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC)
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC)
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
|
2021-09-03 15:04:52 +02:00
|
|
|
if isWrapper {
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0)
|
|
|
|
|
} else {
|
|
|
|
|
ctxt.AddFileRef(s.Info, s.Filesym)
|
|
|
|
|
var ev int64
|
|
|
|
|
if s.External {
|
|
|
|
|
ev = 1
|
|
|
|
|
}
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scopes
|
|
|
|
|
if err := putPrunedScopes(ctxt, s, abbrev); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Inlined subroutines.
|
|
|
|
|
for _, sib := range inlChildren(-1, &s.InlCalls) {
|
2021-07-01 11:44:43 -07:00
|
|
|
err := putInlinedFunc(ctxt, s, sib)
|
2017-10-06 11:32:28 -04:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Uleb128put(ctxt, s.Info, 0)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-25 10:08:07 +02:00
|
|
|
// putparamtypes writes typedef DIEs for any parametric types that are used by this function.
|
|
|
|
|
func putparamtypes(ctxt Context, s *FnState, scopes []Scope, fnabbrev int) []int64 {
|
|
|
|
|
if fnabbrev == DW_ABRV_FUNCTION_CONCRETE {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maxDictIndex := uint16(0)
|
|
|
|
|
|
|
|
|
|
for i := range scopes {
|
|
|
|
|
for _, v := range scopes[i].Vars {
|
|
|
|
|
if v.DictIndex > maxDictIndex {
|
|
|
|
|
maxDictIndex = v.DictIndex
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if maxDictIndex == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dictIndexToOffset := make([]int64, maxDictIndex)
|
|
|
|
|
|
|
|
|
|
for i := range scopes {
|
|
|
|
|
for _, v := range scopes[i].Vars {
|
|
|
|
|
if v.DictIndex == 0 || dictIndexToOffset[v.DictIndex-1] != 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dictIndexToOffset[v.DictIndex-1] = ctxt.CurrentOffset(s.Info)
|
|
|
|
|
|
|
|
|
|
Uleb128put(ctxt, s.Info, int64(DW_ABRV_DICT_INDEX))
|
|
|
|
|
n := fmt.Sprintf(".param%d", v.DictIndex-1)
|
|
|
|
|
putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
|
|
|
|
|
putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
|
|
|
|
|
putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DictIndex-1), nil)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dictIndexToOffset
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
func putscope(ctxt Context, s *FnState, scopes []Scope, curscope int32, fnabbrev int, encbuf []byte) int32 {
|
|
|
|
|
|
|
|
|
|
if logDwarf {
|
|
|
|
|
ctxt.Logf("putscope(%v,%d): vars:", s.Info, curscope)
|
|
|
|
|
for i, v := range scopes[curscope].Vars {
|
|
|
|
|
ctxt.Logf(" %d:%d:%s", i, v.ChildIndex, v.Name)
|
|
|
|
|
}
|
|
|
|
|
ctxt.Logf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-02 16:46:01 +02:00
|
|
|
for _, v := range scopes[curscope].Vars {
|
2017-10-06 11:32:28 -04:00
|
|
|
putvar(ctxt, s, v, s.Absfn, fnabbrev, -1, encbuf)
|
2017-05-02 16:46:01 +02:00
|
|
|
}
|
|
|
|
|
this := curscope
|
|
|
|
|
curscope++
|
|
|
|
|
for curscope < int32(len(scopes)) {
|
|
|
|
|
scope := scopes[curscope]
|
|
|
|
|
if scope.Parent != this {
|
|
|
|
|
return curscope
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 07:40:44 +01:00
|
|
|
if len(scopes[curscope].Vars) == 0 {
|
|
|
|
|
curscope = putscope(ctxt, s, scopes, curscope, fnabbrev, encbuf)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-02 16:46:01 +02:00
|
|
|
if len(scope.Ranges) == 1 {
|
2017-10-06 11:32:28 -04:00
|
|
|
Uleb128put(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_SIMPLE)
|
|
|
|
|
putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].Start, s.StartPC)
|
|
|
|
|
putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].End, s.StartPC)
|
2017-05-02 16:46:01 +02:00
|
|
|
} else {
|
2017-10-06 11:32:28 -04:00
|
|
|
Uleb128put(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES)
|
2020-02-19 16:16:17 -05:00
|
|
|
putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges)
|
2017-05-02 16:46:01 +02:00
|
|
|
|
2019-04-02 17:26:49 -04:00
|
|
|
s.PutRanges(ctxt, scope.Ranges)
|
2017-05-02 16:46:01 +02:00
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
curscope = putscope(ctxt, s, scopes, curscope, fnabbrev, encbuf)
|
2018-02-15 07:40:44 +01:00
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
Uleb128put(ctxt, s.Info, 0)
|
2017-05-02 16:46:01 +02:00
|
|
|
}
|
|
|
|
|
return curscope
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-11 15:53:31 -05:00
|
|
|
// Given a default var abbrev code, select corresponding concrete code.
|
|
|
|
|
func concreteVarAbbrev(varAbbrev int) int {
|
|
|
|
|
switch varAbbrev {
|
|
|
|
|
case DW_ABRV_AUTO:
|
|
|
|
|
return DW_ABRV_AUTO_CONCRETE
|
|
|
|
|
case DW_ABRV_PARAM:
|
|
|
|
|
return DW_ABRV_PARAM_CONCRETE
|
|
|
|
|
case DW_ABRV_AUTO_LOCLIST:
|
|
|
|
|
return DW_ABRV_AUTO_CONCRETE_LOCLIST
|
|
|
|
|
case DW_ABRV_PARAM_LOCLIST:
|
|
|
|
|
return DW_ABRV_PARAM_CONCRETE_LOCLIST
|
|
|
|
|
default:
|
|
|
|
|
panic("should never happen")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
// Pick the correct abbrev code for variable or parameter DIE.
|
|
|
|
|
func determineVarAbbrev(v *Var, fnabbrev int) (int, bool, bool) {
|
|
|
|
|
abbrev := v.Abbrev
|
|
|
|
|
|
cmd/compile: cover control flow insns in location lists
The information that's used to generate DWARF location lists is very
ssa.Value centric; it uses Values as start and end coordinates to define
ranges. That mostly works fine, but control flow instructions don't come
from Values, so the ranges couldn't cover them.
Control flow instructions are generated when the SSA representation is
converted to assembly, so that's the best place to extend the ranges
to cover them. (Before that, there's nothing to refer to, and afterward
the boundaries between blocks have been lost.) That requires block
information in the debugInfo type, which then flows down to make
everything else awkward. On the plus side, there's a little less copying
slices around than there used to be, so it should be a little faster.
Previously, the ranges for empty blocks were not very meaningful. That
was fine, because they had no Values to cover, so no debug information
was generated for them. But they do have control flow instructions
(that's why they exist) and so now it's important that the information
be correct. Introduce two sentinel values, BlockStart and BlockEnd, that
denote the boundary of a block, even if the block is empty. BlockEnd
replaces the previous SurvivedBlock flag.
There's one more problem: the last instruction in the function will be a
control flow instruction, so any live ranges need to be extended past
it. But there's no instruction after it to use as the end of the range.
Instead, leave the EndProg field of those ranges as nil and fix it up to
point to past the end of the assembled text at the very last moment.
Change-Id: I81f884020ff36fd6fe8d7888fc57c99412c4245b
Reviewed-on: https://go-review.googlesource.com/63010
Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-09-11 14:28:34 -04:00
|
|
|
// If the variable was entirely optimized out, don't emit a location list;
|
|
|
|
|
// convert to an inline abbreviation and emit an empty location.
|
|
|
|
|
missing := false
|
|
|
|
|
switch {
|
cmd/compile: reimplement location list generation
Completely redesign and reimplement location list generation to be more
efficient, and hopefully not too hard to understand.
RegKills are gone. Instead of using the regalloc's liveness
calculations, redo them using the Ops' clobber information. Besides
saving a lot of Values, this avoids adding RegKills to blocks that would
be empty otherwise, which was messing up optimizations. This does mean
that it's much harder to tell whether the generation process is buggy
(there's nothing to cross-check it with), and there may be disagreements
with GC liveness. But the performance gain is significant, and it's nice
not to be messing with earlier compiler phases.
The intermediate representations are gone. Instead of producing
ssa.BlockDebugs, then dwarf.LocationLists, and then finally real
location lists, go directly from the SSA to a (mostly) real location
list. Because the SSA analysis happens before assembly, it stores
encoded block/value IDs where PCs would normally go. It would be easier
to do the SSA analysis after assembly, but I didn't want to retain the
SSA just for that.
Generation proceeds in two phases: first, it traverses the function in
CFG order, storing the state of the block at the beginning and end. End
states are used to produce the start states of the successor blocks. In
the second phase, it traverses in program text order and produces the
location lists. The processing in the second phase is redundant, but
much cheaper than storing the intermediate representation. It might be
possible to combine the two phases somewhat to take advantage of cases
where the CFG matches the block layout, but I haven't tried.
Location lists are finalized by adding a base address selection entry,
translating each encoded block/value ID to a real PC, and adding the
terminating zero entry. This probably won't work on OSX, where dsymutil
will choke on the base address selection. I tried emitting CU-relative
relocations for each address, and it was *very* bad for performance --
it uses more memory storing all the relocations than it does for the
actual location list bytes. I think I'm going to end up synthesizing the
relocations in the linker only on OSX, but TBD.
TestNexting needs updating: with more optimizations working, the
debugger doesn't stop on the continue (line 88) any more, and the test's
duplicate suppression kicks in. Also, dx and dy live a little longer
now, but they have the correct values.
Change-Id: Ie772dfe23a4e389ca573624fac4d05401ae32307
Reviewed-on: https://go-review.googlesource.com/89356
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2017-10-26 15:40:17 -04:00
|
|
|
case abbrev == DW_ABRV_AUTO_LOCLIST && v.PutLocationList == nil:
|
cmd/compile: cover control flow insns in location lists
The information that's used to generate DWARF location lists is very
ssa.Value centric; it uses Values as start and end coordinates to define
ranges. That mostly works fine, but control flow instructions don't come
from Values, so the ranges couldn't cover them.
Control flow instructions are generated when the SSA representation is
converted to assembly, so that's the best place to extend the ranges
to cover them. (Before that, there's nothing to refer to, and afterward
the boundaries between blocks have been lost.) That requires block
information in the debugInfo type, which then flows down to make
everything else awkward. On the plus side, there's a little less copying
slices around than there used to be, so it should be a little faster.
Previously, the ranges for empty blocks were not very meaningful. That
was fine, because they had no Values to cover, so no debug information
was generated for them. But they do have control flow instructions
(that's why they exist) and so now it's important that the information
be correct. Introduce two sentinel values, BlockStart and BlockEnd, that
denote the boundary of a block, even if the block is empty. BlockEnd
replaces the previous SurvivedBlock flag.
There's one more problem: the last instruction in the function will be a
control flow instruction, so any live ranges need to be extended past
it. But there's no instruction after it to use as the end of the range.
Instead, leave the EndProg field of those ranges as nil and fix it up to
point to past the end of the assembled text at the very last moment.
Change-Id: I81f884020ff36fd6fe8d7888fc57c99412c4245b
Reviewed-on: https://go-review.googlesource.com/63010
Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-09-11 14:28:34 -04:00
|
|
|
missing = true
|
2017-10-06 11:32:28 -04:00
|
|
|
abbrev = DW_ABRV_AUTO
|
cmd/compile: reimplement location list generation
Completely redesign and reimplement location list generation to be more
efficient, and hopefully not too hard to understand.
RegKills are gone. Instead of using the regalloc's liveness
calculations, redo them using the Ops' clobber information. Besides
saving a lot of Values, this avoids adding RegKills to blocks that would
be empty otherwise, which was messing up optimizations. This does mean
that it's much harder to tell whether the generation process is buggy
(there's nothing to cross-check it with), and there may be disagreements
with GC liveness. But the performance gain is significant, and it's nice
not to be messing with earlier compiler phases.
The intermediate representations are gone. Instead of producing
ssa.BlockDebugs, then dwarf.LocationLists, and then finally real
location lists, go directly from the SSA to a (mostly) real location
list. Because the SSA analysis happens before assembly, it stores
encoded block/value IDs where PCs would normally go. It would be easier
to do the SSA analysis after assembly, but I didn't want to retain the
SSA just for that.
Generation proceeds in two phases: first, it traverses the function in
CFG order, storing the state of the block at the beginning and end. End
states are used to produce the start states of the successor blocks. In
the second phase, it traverses in program text order and produces the
location lists. The processing in the second phase is redundant, but
much cheaper than storing the intermediate representation. It might be
possible to combine the two phases somewhat to take advantage of cases
where the CFG matches the block layout, but I haven't tried.
Location lists are finalized by adding a base address selection entry,
translating each encoded block/value ID to a real PC, and adding the
terminating zero entry. This probably won't work on OSX, where dsymutil
will choke on the base address selection. I tried emitting CU-relative
relocations for each address, and it was *very* bad for performance --
it uses more memory storing all the relocations than it does for the
actual location list bytes. I think I'm going to end up synthesizing the
relocations in the linker only on OSX, but TBD.
TestNexting needs updating: with more optimizations working, the
debugger doesn't stop on the continue (line 88) any more, and the test's
duplicate suppression kicks in. Also, dx and dy live a little longer
now, but they have the correct values.
Change-Id: Ie772dfe23a4e389ca573624fac4d05401ae32307
Reviewed-on: https://go-review.googlesource.com/89356
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2017-10-26 15:40:17 -04:00
|
|
|
case abbrev == DW_ABRV_PARAM_LOCLIST && v.PutLocationList == nil:
|
cmd/compile: cover control flow insns in location lists
The information that's used to generate DWARF location lists is very
ssa.Value centric; it uses Values as start and end coordinates to define
ranges. That mostly works fine, but control flow instructions don't come
from Values, so the ranges couldn't cover them.
Control flow instructions are generated when the SSA representation is
converted to assembly, so that's the best place to extend the ranges
to cover them. (Before that, there's nothing to refer to, and afterward
the boundaries between blocks have been lost.) That requires block
information in the debugInfo type, which then flows down to make
everything else awkward. On the plus side, there's a little less copying
slices around than there used to be, so it should be a little faster.
Previously, the ranges for empty blocks were not very meaningful. That
was fine, because they had no Values to cover, so no debug information
was generated for them. But they do have control flow instructions
(that's why they exist) and so now it's important that the information
be correct. Introduce two sentinel values, BlockStart and BlockEnd, that
denote the boundary of a block, even if the block is empty. BlockEnd
replaces the previous SurvivedBlock flag.
There's one more problem: the last instruction in the function will be a
control flow instruction, so any live ranges need to be extended past
it. But there's no instruction after it to use as the end of the range.
Instead, leave the EndProg field of those ranges as nil and fix it up to
point to past the end of the assembled text at the very last moment.
Change-Id: I81f884020ff36fd6fe8d7888fc57c99412c4245b
Reviewed-on: https://go-review.googlesource.com/63010
Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-09-11 14:28:34 -04:00
|
|
|
missing = true
|
2017-10-06 11:32:28 -04:00
|
|
|
abbrev = DW_ABRV_PARAM
|
cmd/compile: cover control flow insns in location lists
The information that's used to generate DWARF location lists is very
ssa.Value centric; it uses Values as start and end coordinates to define
ranges. That mostly works fine, but control flow instructions don't come
from Values, so the ranges couldn't cover them.
Control flow instructions are generated when the SSA representation is
converted to assembly, so that's the best place to extend the ranges
to cover them. (Before that, there's nothing to refer to, and afterward
the boundaries between blocks have been lost.) That requires block
information in the debugInfo type, which then flows down to make
everything else awkward. On the plus side, there's a little less copying
slices around than there used to be, so it should be a little faster.
Previously, the ranges for empty blocks were not very meaningful. That
was fine, because they had no Values to cover, so no debug information
was generated for them. But they do have control flow instructions
(that's why they exist) and so now it's important that the information
be correct. Introduce two sentinel values, BlockStart and BlockEnd, that
denote the boundary of a block, even if the block is empty. BlockEnd
replaces the previous SurvivedBlock flag.
There's one more problem: the last instruction in the function will be a
control flow instruction, so any live ranges need to be extended past
it. But there's no instruction after it to use as the end of the range.
Instead, leave the EndProg field of those ranges as nil and fix it up to
point to past the end of the assembled text at the very last moment.
Change-Id: I81f884020ff36fd6fe8d7888fc57c99412c4245b
Reviewed-on: https://go-review.googlesource.com/63010
Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-09-11 14:28:34 -04:00
|
|
|
}
|
|
|
|
|
|
2017-12-11 15:53:31 -05:00
|
|
|
// Determine whether to use a concrete variable or regular variable DIE.
|
2017-10-06 11:32:28 -04:00
|
|
|
concrete := true
|
|
|
|
|
switch fnabbrev {
|
2021-09-03 15:04:52 +02:00
|
|
|
case DW_ABRV_FUNCTION, DW_ABRV_WRAPPER:
|
2017-10-06 11:32:28 -04:00
|
|
|
concrete = false
|
|
|
|
|
break
|
2021-09-03 15:04:52 +02:00
|
|
|
case DW_ABRV_FUNCTION_CONCRETE, DW_ABRV_WRAPPER_CONCRETE:
|
2017-12-11 15:53:31 -05:00
|
|
|
// If we're emitting a concrete subprogram DIE and the variable
|
|
|
|
|
// in question is not part of the corresponding abstract function DIE,
|
|
|
|
|
// then use the default (non-concrete) abbrev for this param.
|
|
|
|
|
if !v.IsInAbstract {
|
|
|
|
|
concrete = false
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2017-12-11 15:53:31 -05:00
|
|
|
case DW_ABRV_INLINED_SUBROUTINE, DW_ABRV_INLINED_SUBROUTINE_RANGES:
|
2017-10-06 11:32:28 -04:00
|
|
|
default:
|
|
|
|
|
panic("should never happen")
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-11 15:53:31 -05:00
|
|
|
// Select proper abbrev based on concrete/non-concrete
|
|
|
|
|
if concrete {
|
|
|
|
|
abbrev = concreteVarAbbrev(abbrev)
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
return abbrev, missing, concrete
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func abbrevUsesLoclist(abbrev int) bool {
|
|
|
|
|
switch abbrev {
|
|
|
|
|
case DW_ABRV_AUTO_LOCLIST, DW_ABRV_AUTO_CONCRETE_LOCLIST,
|
|
|
|
|
DW_ABRV_PARAM_LOCLIST, DW_ABRV_PARAM_CONCRETE_LOCLIST:
|
|
|
|
|
return true
|
|
|
|
|
default:
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit DWARF attributes for a variable belonging to an 'abstract' subprogram.
|
|
|
|
|
func putAbstractVar(ctxt Context, info Sym, v *Var) {
|
|
|
|
|
// Remap abbrev
|
|
|
|
|
abbrev := v.Abbrev
|
|
|
|
|
switch abbrev {
|
|
|
|
|
case DW_ABRV_AUTO, DW_ABRV_AUTO_LOCLIST:
|
|
|
|
|
abbrev = DW_ABRV_AUTO_ABSTRACT
|
|
|
|
|
case DW_ABRV_PARAM, DW_ABRV_PARAM_LOCLIST:
|
|
|
|
|
abbrev = DW_ABRV_PARAM_ABSTRACT
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Uleb128put(ctxt, info, int64(abbrev))
|
|
|
|
|
putattr(ctxt, info, abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(v.Name)), v.Name)
|
|
|
|
|
|
|
|
|
|
// Isreturn attribute if this is a param
|
|
|
|
|
if abbrev == DW_ABRV_PARAM_ABSTRACT {
|
2017-10-18 13:45:03 -04:00
|
|
|
var isReturn int64
|
|
|
|
|
if v.IsReturnValue {
|
|
|
|
|
isReturn = 1
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
putattr(ctxt, info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Line
|
2018-01-09 21:20:10 -05:00
|
|
|
if abbrev != DW_ABRV_PARAM_ABSTRACT {
|
|
|
|
|
// See issue 23374 for more on why decl line is skipped for abs params.
|
|
|
|
|
putattr(ctxt, info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil)
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
|
|
|
|
|
// Type
|
|
|
|
|
putattr(ctxt, info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
|
|
|
|
|
|
|
|
|
|
// Var has no children => no terminator
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, encbuf []byte) {
|
|
|
|
|
// Remap abbrev according to parent DIE abbrev
|
|
|
|
|
abbrev, missing, concrete := determineVarAbbrev(v, fnabbrev)
|
|
|
|
|
|
|
|
|
|
Uleb128put(ctxt, s.Info, int64(abbrev))
|
|
|
|
|
|
|
|
|
|
// Abstract origin for concrete / inlined case
|
|
|
|
|
if concrete {
|
|
|
|
|
// Here we are making a reference to a child DIE of an abstract
|
|
|
|
|
// function subprogram DIE. The child DIE has no LSym, so instead
|
|
|
|
|
// after the call to 'putattr' below we make a call to register
|
|
|
|
|
// the child DIE reference.
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, absfn)
|
|
|
|
|
ctxt.RecordDclReference(s.Info, absfn, int(v.ChildIndex), inlIndex)
|
|
|
|
|
} else {
|
|
|
|
|
// Var name, line for abstract and default cases
|
|
|
|
|
n := v.Name
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
|
|
|
|
|
if abbrev == DW_ABRV_PARAM || abbrev == DW_ABRV_PARAM_LOCLIST || abbrev == DW_ABRV_PARAM_ABSTRACT {
|
|
|
|
|
var isReturn int64
|
|
|
|
|
if v.IsReturnValue {
|
|
|
|
|
isReturn = 1
|
|
|
|
|
}
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil)
|
|
|
|
|
}
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil)
|
2021-08-25 10:08:07 +02:00
|
|
|
if v.DictIndex > 0 && s.dictIndexToOffset != nil && s.dictIndexToOffset[v.DictIndex-1] != 0 {
|
|
|
|
|
// If the type of this variable is parametric use the entry emitted by putparamtypes
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, s.dictIndexToOffset[v.DictIndex-1], s.Info)
|
|
|
|
|
} else {
|
|
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
|
|
|
|
|
}
|
2017-10-18 13:45:03 -04:00
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
|
|
|
|
|
if abbrevUsesLoclist(abbrev) {
|
2020-02-19 16:16:17 -05:00
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Loc.Length(ctxt), s.Loc)
|
cmd/compile: reimplement location list generation
Completely redesign and reimplement location list generation to be more
efficient, and hopefully not too hard to understand.
RegKills are gone. Instead of using the regalloc's liveness
calculations, redo them using the Ops' clobber information. Besides
saving a lot of Values, this avoids adding RegKills to blocks that would
be empty otherwise, which was messing up optimizations. This does mean
that it's much harder to tell whether the generation process is buggy
(there's nothing to cross-check it with), and there may be disagreements
with GC liveness. But the performance gain is significant, and it's nice
not to be messing with earlier compiler phases.
The intermediate representations are gone. Instead of producing
ssa.BlockDebugs, then dwarf.LocationLists, and then finally real
location lists, go directly from the SSA to a (mostly) real location
list. Because the SSA analysis happens before assembly, it stores
encoded block/value IDs where PCs would normally go. It would be easier
to do the SSA analysis after assembly, but I didn't want to retain the
SSA just for that.
Generation proceeds in two phases: first, it traverses the function in
CFG order, storing the state of the block at the beginning and end. End
states are used to produce the start states of the successor blocks. In
the second phase, it traverses in program text order and produces the
location lists. The processing in the second phase is redundant, but
much cheaper than storing the intermediate representation. It might be
possible to combine the two phases somewhat to take advantage of cases
where the CFG matches the block layout, but I haven't tried.
Location lists are finalized by adding a base address selection entry,
translating each encoded block/value ID to a real PC, and adding the
terminating zero entry. This probably won't work on OSX, where dsymutil
will choke on the base address selection. I tried emitting CU-relative
relocations for each address, and it was *very* bad for performance --
it uses more memory storing all the relocations than it does for the
actual location list bytes. I think I'm going to end up synthesizing the
relocations in the linker only on OSX, but TBD.
TestNexting needs updating: with more optimizations working, the
debugger doesn't stop on the continue (line 88) any more, and the test's
duplicate suppression kicks in. Also, dx and dy live a little longer
now, but they have the correct values.
Change-Id: Ie772dfe23a4e389ca573624fac4d05401ae32307
Reviewed-on: https://go-review.googlesource.com/89356
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2017-10-26 15:40:17 -04:00
|
|
|
v.PutLocationList(s.Loc, s.StartPC)
|
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
|
|
|
} else {
|
2017-07-04 08:32:02 +02:00
|
|
|
loc := encbuf[:0]
|
cmd/compile: cover control flow insns in location lists
The information that's used to generate DWARF location lists is very
ssa.Value centric; it uses Values as start and end coordinates to define
ranges. That mostly works fine, but control flow instructions don't come
from Values, so the ranges couldn't cover them.
Control flow instructions are generated when the SSA representation is
converted to assembly, so that's the best place to extend the ranges
to cover them. (Before that, there's nothing to refer to, and afterward
the boundaries between blocks have been lost.) That requires block
information in the debugInfo type, which then flows down to make
everything else awkward. On the plus side, there's a little less copying
slices around than there used to be, so it should be a little faster.
Previously, the ranges for empty blocks were not very meaningful. That
was fine, because they had no Values to cover, so no debug information
was generated for them. But they do have control flow instructions
(that's why they exist) and so now it's important that the information
be correct. Introduce two sentinel values, BlockStart and BlockEnd, that
denote the boundary of a block, even if the block is empty. BlockEnd
replaces the previous SurvivedBlock flag.
There's one more problem: the last instruction in the function will be a
control flow instruction, so any live ranges need to be extended past
it. But there's no instruction after it to use as the end of the range.
Instead, leave the EndProg field of those ranges as nil and fix it up to
point to past the end of the assembled text at the very last moment.
Change-Id: I81f884020ff36fd6fe8d7888fc57c99412c4245b
Reviewed-on: https://go-review.googlesource.com/63010
Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-09-11 14:28:34 -04:00
|
|
|
switch {
|
|
|
|
|
case missing:
|
|
|
|
|
break // no location
|
|
|
|
|
case v.StackOffset == 0:
|
2017-07-04 08:32:02 +02:00
|
|
|
loc = append(loc, DW_OP_call_frame_cfa)
|
cmd/compile: cover control flow insns in location lists
The information that's used to generate DWARF location lists is very
ssa.Value centric; it uses Values as start and end coordinates to define
ranges. That mostly works fine, but control flow instructions don't come
from Values, so the ranges couldn't cover them.
Control flow instructions are generated when the SSA representation is
converted to assembly, so that's the best place to extend the ranges
to cover them. (Before that, there's nothing to refer to, and afterward
the boundaries between blocks have been lost.) That requires block
information in the debugInfo type, which then flows down to make
everything else awkward. On the plus side, there's a little less copying
slices around than there used to be, so it should be a little faster.
Previously, the ranges for empty blocks were not very meaningful. That
was fine, because they had no Values to cover, so no debug information
was generated for them. But they do have control flow instructions
(that's why they exist) and so now it's important that the information
be correct. Introduce two sentinel values, BlockStart and BlockEnd, that
denote the boundary of a block, even if the block is empty. BlockEnd
replaces the previous SurvivedBlock flag.
There's one more problem: the last instruction in the function will be a
control flow instruction, so any live ranges need to be extended past
it. But there's no instruction after it to use as the end of the range.
Instead, leave the EndProg field of those ranges as nil and fix it up to
point to past the end of the assembled text at the very last moment.
Change-Id: I81f884020ff36fd6fe8d7888fc57c99412c4245b
Reviewed-on: https://go-review.googlesource.com/63010
Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-09-11 14:28:34 -04:00
|
|
|
default:
|
2017-07-04 08:32:02 +02:00
|
|
|
loc = append(loc, DW_OP_fbreg)
|
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
|
|
|
loc = AppendSleb128(loc, int64(v.StackOffset))
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
|
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
|
|
|
|
|
// Var has no children => no terminator
|
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
|
|
|
}
|
|
|
|
|
|
2017-10-06 11:32:28 -04:00
|
|
|
// byChildIndex implements sort.Interface for []*dwarf.Var by child index.
|
|
|
|
|
type byChildIndex []*Var
|
|
|
|
|
|
|
|
|
|
func (s byChildIndex) Len() int { return len(s) }
|
|
|
|
|
func (s byChildIndex) Less(i, j int) bool { return s[i].ChildIndex < s[j].ChildIndex }
|
|
|
|
|
func (s byChildIndex) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
2019-02-20 16:39:09 +01:00
|
|
|
|
|
|
|
|
// IsDWARFEnabledOnAIX returns true if DWARF is possible on the
|
|
|
|
|
// current extld.
|
|
|
|
|
// AIX ld doesn't support DWARF with -bnoobjreorder with version
|
|
|
|
|
// prior to 7.2.2.
|
cmd: support space and quotes in CC and CXX
The CC and CXX environment variables now support spaces and quotes
(both double and single). This fixes two issues: first, if CC is a
single path that contains spaces (like 'c:\Program
Files\gcc\bin\gcc.exe'), that should now work if the space is quoted
or escaped (#41400). Second, if CC or CXX has multiple arguments (like
'gcc -O2'), they are now split correctly, and the arguments are passed
before other arguments when invoking the C compiler. Previously,
strings.Fields was used to split arguments, and the arguments were
placed later in the command line. (#43078).
Fixes golang/go#41400
Fixes golang/go#43078
NOTE: This change also includes a fix (CL 341929) for a test that was
broken by the original CL. Commit message for the fix is below.
[dev.cmdgo] cmd/link: fix TestBuildForTvOS
This test was broken in CL 334732 on darwin.
The test invokes 'go build' with a CC containing the arguments
-framework CoreFoundation. Previously, the go command split CC on
whitespace, and inserted the arguments after the command line when
running CC directly. Those arguments weren't passed to cgo though,
so cgo ran CC without -framework CoreFoundation (or any of the other
flags).
In CL 334732, we pass CC through to cgo, and cgo splits arguments
using str.SplitQuotedFields. So -framework CoreFoundation actually
gets passed to the C compiler. It appears that -framework flags are
only meant to be used in linking operations, so when cgo invokes clang
with -E (run preprocessor only), clang emits an error that -framework
is unused.
This change fixes the test by moving -framework CoreFoundation out of
CC and into CGO_LDFLAGS.
Change-Id: I2d5d89ddb19c94adef65982a8137b01f037d5c11
Reviewed-on: https://go-review.googlesource.com/c/go/+/334732
Trust: Jay Conrod <jayconrod@google.com>
Trust: Michael Matloob <matloob@golang.org>
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-on: https://go-review.googlesource.com/c/go/+/341936
Reviewed-by: Bryan C. Mills <bcmills@google.com>
2021-07-14 16:57:24 -07:00
|
|
|
func IsDWARFEnabledOnAIXLd(extld []string) (bool, error) {
|
|
|
|
|
name, args := extld[0], extld[1:]
|
|
|
|
|
args = append(args, "-Wl,-V")
|
|
|
|
|
out, err := exec.Command(name, args...).CombinedOutput()
|
2019-02-20 16:39:09 +01:00
|
|
|
if err != nil {
|
|
|
|
|
// The normal output should display ld version and
|
|
|
|
|
// then fails because ".main" is not defined:
|
|
|
|
|
// ld: 0711-317 ERROR: Undefined symbol: .main
|
|
|
|
|
if !bytes.Contains(out, []byte("0711-317")) {
|
|
|
|
|
return false, fmt.Errorf("%s -Wl,-V failed: %v\n%s", extld, err, out)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// gcc -Wl,-V output should be:
|
|
|
|
|
// /usr/bin/ld: LD X.X.X(date)
|
|
|
|
|
// ...
|
|
|
|
|
out = bytes.TrimPrefix(out, []byte("/usr/bin/ld: LD "))
|
|
|
|
|
vers := string(bytes.Split(out, []byte("("))[0])
|
|
|
|
|
subvers := strings.Split(vers, ".")
|
|
|
|
|
if len(subvers) != 3 {
|
|
|
|
|
return false, fmt.Errorf("cannot parse %s -Wl,-V (%s): %v\n", extld, out, err)
|
|
|
|
|
}
|
|
|
|
|
if v, err := strconv.Atoi(subvers[0]); err != nil || v < 7 {
|
|
|
|
|
return false, nil
|
|
|
|
|
} else if v > 7 {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
if v, err := strconv.Atoi(subvers[1]); err != nil || v < 2 {
|
|
|
|
|
return false, nil
|
|
|
|
|
} else if v > 2 {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
if v, err := strconv.Atoi(subvers[2]); err != nil || v < 2 {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|