go/src/cmd/internal/dwarf/dwarf.go
Cuong Manh Le 0f8dffd0aa all: use ":" for compiler generated symbols
As it can't appear in user package paths.

There is a hack for handling "go:buildid" and "type:*" on windows/386.

Previously, windows/386 requires underscore prefix on external symbols,
but that's only applied for SHOSTOBJ/SUNDEFEXT or cgo export symbols.
"go.buildid" is STEXT, "type.*" is STYPE, thus they are not prefixed
with underscore.

In external linking mode, the external linker can't resolve them as
external symbols. But we are lucky that they have "." in their name,
so the external linker see them as Forwarder RVA exports. See:

 - https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table
 - https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=ld/pe-dll.c;h=e7b82ba6ffadf74dc1b9ee71dc13d48336941e51;hb=HEAD#l972)

This CL changes "." to ":" in symbols name, so theses symbols can not be
found by external linker anymore. So a hacky way is adding the
underscore prefix for these 2 symbols. I don't have enough knowledge to
verify whether adding the underscore for all STEXT/STYPE symbols are
fine, even if it could be, that would be done in future CL.

Fixes #37762

Change-Id: I92eaaf24c0820926a36e0530fdb07b07af1fcc35
Reviewed-on: https://go-review.googlesource.com/c/go/+/317917
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-08-09 11:28:56 +00:00

1762 lines
46 KiB
Go

// 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
import (
"bytes"
"errors"
"fmt"
"internal/buildcfg"
"os/exec"
"sort"
"strconv"
"strings"
"cmd/internal/objabi"
)
// InfoPrefix is the prefix for all the symbols containing DWARF info entries.
const InfoPrefix = "go:info."
// ConstInfoPrefix is the prefix for all symbols containing DWARF info
// entries that contain constants.
const ConstInfoPrefix = "go:constinfo."
// CUInfoPrefix is the prefix for symbols containing information to
// populate the DWARF compilation unit info entries.
const CUInfoPrefix = "go:cuinfo."
// 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
// Sym represents a symbol.
type Sym interface {
Length(dwarfContext interface{}) int64
}
// A Var represents a local variable or a function parameter.
type Var struct {
Name string
Abbrev int // Either DW_ABRV_AUTO[_LOCLIST] or DW_ABRV_PARAM[_LOCLIST]
IsReturnValue bool
IsInlFormal bool
DictIndex uint16 // index of the dictionary entry describing the type of this variable
StackOffset int32
// 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
}
// 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
}
// This container is used by the PutFunc* variants below when
// creating the DWARF subprogram DIE(s) for a function.
type FnState struct {
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
dictIndexToOffset []int64
}
func EnableLogging(doit bool) {
logDwarf = doit
}
// 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))
i, j := 0, 0
for {
var cur Range
if i < len(in2) && j < len(in1) {
if in2[i].Start < in1[j].Start {
cur = in2[i]
i++
} else {
cur = in1[j]
j++
}
} else if i < len(in2) {
cur = in2[i]
i++
} else if j < len(in1) {
cur = in1[j]
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)
}
}
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)
}
// 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)
}
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
}
// 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)
AddCURelativeAddress(s Sym, t interface{}, ofs int64)
AddSectionOffset(s Sym, size int, t interface{}, ofs int64)
AddDWARFAddrSectionOffset(s Sym, t interface{}, ofs int64)
CurrentOffset(s Sym) int64
RecordDclReference(from Sym, to Sym, dclIdx int, inlIndex int)
RecordChildDieOffsets(s Sym, vars []*Var, offsets []int32)
AddString(s Sym, v string)
AddFileRef(s Sym, f interface{})
Logf(format string, args ...interface{})
}
// 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
}
// 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
}
// 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.
func Uleb128put(ctxt Context, s Sym, v int64) {
b := sevenBitU(v)
if b == nil {
var encbuf [20]byte
b = AppendUleb128(encbuf[:0], uint64(v))
}
ctxt.AddBytes(s, b)
}
// Sleb128put appends v to s using DWARF's signed LEB128 encoding.
func Sleb128put(ctxt Context, s Sym, v int64) {
b := sevenBitS(v)
if b == nil {
var encbuf [20]byte
b = AppendSleb128(encbuf[:0], v)
}
ctxt.AddBytes(s, b)
}
/*
* 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.
*/
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
// 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
DW_AT_go_runtime_type = 0x2904
DW_AT_go_package_name = 0x2905 // Attribute for DW_TAG_compile_unit
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
DW_AT_internal_location = 253 // params and locals; not emitted
)
// Index into the abbrevs table below.
const (
DW_ABRV_NULL = iota
DW_ABRV_COMPUNIT
DW_ABRV_COMPUNIT_TEXTLESS
DW_ABRV_FUNCTION
DW_ABRV_WRAPPER
DW_ABRV_FUNCTION_ABSTRACT
DW_ABRV_FUNCTION_CONCRETE
DW_ABRV_WRAPPER_CONCRETE
DW_ABRV_INLINED_SUBROUTINE
DW_ABRV_INLINED_SUBROUTINE_RANGES
DW_ABRV_VARIABLE
DW_ABRV_INT_CONSTANT
DW_ABRV_AUTO
DW_ABRV_AUTO_LOCLIST
DW_ABRV_AUTO_ABSTRACT
DW_ABRV_AUTO_CONCRETE
DW_ABRV_AUTO_CONCRETE_LOCLIST
DW_ABRV_PARAM
DW_ABRV_PARAM_LOCLIST
DW_ABRV_PARAM_ABSTRACT
DW_ABRV_PARAM_CONCRETE
DW_ABRV_PARAM_CONCRETE_LOCLIST
DW_ABRV_LEXICAL_BLOCK_RANGES
DW_ABRV_LEXICAL_BLOCK_SIMPLE
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
DW_ABRV_DICT_INDEX
DW_NABRV
)
type dwAbbrev struct {
tag uint8
children uint8
attr []dwAttrForm
}
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
if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" {
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.
func Abbrevs() []dwAbbrev {
if abbrevsFinalized {
return abbrevs[:]
}
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
return abbrevs[:]
}
// 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.
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},
{DW_AT_stmt_list, DW_FORM_sec_offset},
{DW_AT_low_pc, DW_FORM_addr},
{DW_AT_ranges, DW_FORM_sec_offset},
{DW_AT_comp_dir, DW_FORM_string},
{DW_AT_producer, DW_FORM_string},
{DW_AT_go_package_name, DW_FORM_string},
},
},
/* 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},
{DW_AT_go_package_name, DW_FORM_string},
},
},
/* FUNCTION */
{
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_decl_file, DW_FORM_data4},
{DW_AT_external, DW_FORM_flag},
},
},
/* 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},
},
},
/* 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},
},
},
/* 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},
},
},
/* 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},
{DW_AT_call_line, DW_FORM_udata_pseudo}, // pseudo-form
},
},
/* 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},
{DW_AT_call_line, DW_FORM_udata_pseudo}, // pseudo-form
},
},
/* 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},
},
},
/* 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},
},
},
/* AUTO */
{
DW_TAG_variable,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_location, DW_FORM_block1},
},
},
/* AUTO_LOCLIST */
{
DW_TAG_variable,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_location, DW_FORM_sec_offset},
},
},
/* AUTO_ABSTRACT */
{
DW_TAG_variable,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
},
},
/* 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},
},
},
/* PARAM */
{
DW_TAG_formal_parameter,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_variable_parameter, DW_FORM_flag},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_location, DW_FORM_block1},
},
},
/* PARAM_LOCLIST */
{
DW_TAG_formal_parameter,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_variable_parameter, DW_FORM_flag},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_location, DW_FORM_sec_offset},
},
},
/* PARAM_ABSTRACT */
{
DW_TAG_formal_parameter,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_variable_parameter, DW_FORM_flag},
{DW_AT_type, DW_FORM_ref_addr},
},
},
/* 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},
},
},
/* LEXICAL_BLOCK_RANGES */
{
DW_TAG_lexical_block,
DW_CHILDREN_yes,
[]dwAttrForm{
{DW_AT_ranges, DW_FORM_sec_offset},
},
},
/* LEXICAL_BLOCK_SIMPLE */
{
DW_TAG_lexical_block,
DW_CHILDREN_yes,
[]dwAttrForm{
{DW_AT_low_pc, DW_FORM_addr},
{DW_AT_high_pc, DW_FORM_addr},
},
},
/* STRUCTFIELD */
{
DW_TAG_member,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_data_member_location, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_embedded_field, DW_FORM_flag},
},
},
/* 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},
{DW_AT_go_runtime_type, DW_FORM_addr},
},
},
/* 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},
{DW_AT_go_runtime_type, DW_FORM_addr},
},
},
/* 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},
{DW_AT_go_runtime_type, DW_FORM_addr},
{DW_AT_go_elem, DW_FORM_ref_addr},
},
},
/* FUNCTYPE */
{
DW_TAG_subroutine_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},
{DW_AT_go_runtime_type, DW_FORM_addr},
},
},
/* 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},
{DW_AT_go_runtime_type, DW_FORM_addr},
},
},
/* 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},
{DW_AT_go_runtime_type, DW_FORM_addr},
{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},
{DW_AT_go_runtime_type, DW_FORM_addr},
},
},
/* 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},
{DW_AT_go_runtime_type, DW_FORM_addr},
{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},
{DW_AT_go_runtime_type, DW_FORM_addr},
},
},
/* 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},
{DW_AT_go_runtime_type, DW_FORM_addr},
},
},
/* TYPEDECL */
{
DW_TAG_typedef,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_type, DW_FORM_ref_addr},
},
},
/* 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},
},
},
}
// GetAbbrev returns the contents of the .debug_abbrev section.
func GetAbbrev() []byte {
abbrevs := Abbrevs()
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))
buf = append(buf, abbrevs[i].children)
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
// 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
}
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
if cls == DW_CLS_PTR { // DW_AT_stmt_list and DW_AT_ranges
ctxt.AddDWARFAddrSectionOffset(s, data, value)
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)
}
// As of DWARF 3 the ref_addr is always 32 bits, unless emitting a large
// (> 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
fallthrough
case DW_FORM_sec_offset: // offset into a DWARF section other than .info
if data == nil {
return fmt.Errorf("dwarf: null reference in %d", abbrev)
}
ctxt.AddDWARFAddrSectionOffset(s, data, value)
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) {
abbrevs := Abbrevs()
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)
}
}
// HasChildren reports whether 'die' uses an abbrev that supports children.
func HasChildren(die *DWDie) bool {
abbrevs := Abbrevs()
return abbrevs[die.Abbrev].children != 0
}
// 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)
}
// 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)
}
// 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) {
ps := ctxt.PtrSize()
// Write ranges.
for _, r := range ranges {
ctxt.AddInt(sym, ps, r.Start)
ctxt.AddInt(sym, ps, r.End)
}
// Write trailer.
ctxt.AddInt(sym, ps, 0)
ctxt.AddInt(sym, ps, 0)
}
// 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)
}
// 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
}
s.dictIndexToOffset = putparamtypes(ctxt, s, scopes, fnabbrev)
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).
fullname = objabi.PathToPrefix(s.Importpath) + "." + s.Name[3:]
}
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)
var ev int64
if s.External {
ev = 1
}
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]]
if found || !scope.Vars[i].IsInAbstract {
continue
}
flattened = append(flattened, scope.Vars[i])
}
}
if len(flattened) > 0 {
sort.Sort(byChildIndex(flattened))
if logDwarf {
ctxt.Logf("putAbstractScope(%v): vars:", s.Info)
for i, v := range flattened {
ctxt.Logf(" %d:%s", i, v.Name)
}
ctxt.Logf("\n")
}
// 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.
func putInlinedFunc(ctxt Context, s *FnState, callIdx int) error {
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 {
ctxt.Logf("putInlinedFunc(callee=%v,abbrev=%d)\n", callee, abbrev)
}
// Abstract origin.
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, callee)
if abbrev == DW_ABRV_INLINED_SUBROUTINE_RANGES {
putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges)
s.PutRanges(ctxt, ic.Ranges)
} 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)
form := int(expandPseudoForm(DW_FORM_udata_pseudo))
putattr(ctxt, s.Info, abbrev, form, DW_CLS_CONSTANT, int64(ic.CallLine), nil)
// 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 {
if !v.IsInAbstract {
continue
}
putvar(ctxt, s, v, callee, abbrev, inlIndex, encbuf[:0])
}
// Children of this inline.
for _, sib := range inlChildren(callIdx, &s.InlCalls) {
err := putInlinedFunc(ctxt, s, sib)
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).
func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool) error {
if logDwarf {
ctxt.Logf("PutConcreteFunc(%v)\n", s.Info)
}
abbrev := DW_ABRV_FUNCTION_CONCRETE
if isWrapper {
abbrev = DW_ABRV_WRAPPER_CONCRETE
}
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})
if isWrapper {
putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0)
}
// Scopes
if err := putPrunedScopes(ctxt, s, abbrev); err != nil {
return err
}
// Inlined subroutines.
for _, sib := range inlChildren(-1, &s.InlCalls) {
err := putInlinedFunc(ctxt, s, sib)
if err != nil {
return err
}
}
Uleb128put(ctxt, s.Info, 0)
return nil
}
// 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).
func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error {
if logDwarf {
ctxt.Logf("PutDefaultFunc(%v)\n", s.Info)
}
abbrev := DW_ABRV_FUNCTION
if isWrapper {
abbrev = DW_ABRV_WRAPPER
}
Uleb128put(ctxt, s.Info, int64(abbrev))
// 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)
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})
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)
}
// Scopes
if err := putPrunedScopes(ctxt, s, abbrev); err != nil {
return err
}
// Inlined subroutines.
for _, sib := range inlChildren(-1, &s.InlCalls) {
err := putInlinedFunc(ctxt, s, sib)
if err != nil {
return err
}
}
Uleb128put(ctxt, s.Info, 0)
return nil
}
// 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
}
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")
}
for _, v := range scopes[curscope].Vars {
putvar(ctxt, s, v, s.Absfn, fnabbrev, -1, encbuf)
}
this := curscope
curscope++
for curscope < int32(len(scopes)) {
scope := scopes[curscope]
if scope.Parent != this {
return curscope
}
if len(scopes[curscope].Vars) == 0 {
curscope = putscope(ctxt, s, scopes, curscope, fnabbrev, encbuf)
continue
}
if len(scope.Ranges) == 1 {
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)
} else {
Uleb128put(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES)
putattr(ctxt, s.Info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, s.Ranges.Length(ctxt), s.Ranges)
s.PutRanges(ctxt, scope.Ranges)
}
curscope = putscope(ctxt, s, scopes, curscope, fnabbrev, encbuf)
Uleb128put(ctxt, s.Info, 0)
}
return curscope
}
// 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")
}
}
// Pick the correct abbrev code for variable or parameter DIE.
func determineVarAbbrev(v *Var, fnabbrev int) (int, bool, bool) {
abbrev := v.Abbrev
// 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 {
case abbrev == DW_ABRV_AUTO_LOCLIST && v.PutLocationList == nil:
missing = true
abbrev = DW_ABRV_AUTO
case abbrev == DW_ABRV_PARAM_LOCLIST && v.PutLocationList == nil:
missing = true
abbrev = DW_ABRV_PARAM
}
// Determine whether to use a concrete variable or regular variable DIE.
concrete := true
switch fnabbrev {
case DW_ABRV_FUNCTION, DW_ABRV_WRAPPER:
concrete = false
break
case DW_ABRV_FUNCTION_CONCRETE, DW_ABRV_WRAPPER_CONCRETE:
// 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
}
case DW_ABRV_INLINED_SUBROUTINE, DW_ABRV_INLINED_SUBROUTINE_RANGES:
default:
panic("should never happen")
}
// Select proper abbrev based on concrete/non-concrete
if concrete {
abbrev = concreteVarAbbrev(abbrev)
}
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 {
var isReturn int64
if v.IsReturnValue {
isReturn = 1
}
putattr(ctxt, info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil)
}
// Line
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)
}
// 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)
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)
}
}
if abbrevUsesLoclist(abbrev) {
putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, s.Loc.Length(ctxt), s.Loc)
v.PutLocationList(s.Loc, s.StartPC)
} else {
loc := encbuf[:0]
switch {
case missing:
break // no location
case v.StackOffset == 0:
loc = append(loc, DW_OP_call_frame_cfa)
default:
loc = append(loc, DW_OP_fbreg)
loc = AppendSleb128(loc, int64(v.StackOffset))
}
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
}
// Var has no children => no terminator
}
// 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] }
// 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.
func IsDWARFEnabledOnAIXLd(extld []string) (bool, error) {
name, args := extld[0], extld[1:]
args = append(args, "-Wl,-V")
out, err := exec.Command(name, args...).CombinedOutput()
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
}