Skip to content

cmd: emit dwarf for string constants #73940

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions src/cmd/compile/internal/gc/obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,22 +176,34 @@ func dumpGlobalConst(n *ir.Name) {
return
}
// only export integer constants for now
if !t.IsInteger() {
if !t.IsInteger() && !t.IsString() {
return
}
v := n.Val()
if t.IsUntyped() {
// Export untyped integers as int (if they fit).
t = types.Types[types.TINT]
if ir.ConstOverflow(v, t) {
return
if t.IsInteger() {
// Export untyped integers as int (if they fit).
t = types.Types[types.TINT]
if ir.ConstOverflow(v, t) {
return
}
} else {
t = types.Types[types.TSTRING]
}
} else {
// If the type of the constant is an instantiated generic, we need to emit
// that type so the linker knows about it. See issue 51245.
_ = reflectdata.TypeLinksym(t)
if t.IsInteger() {
// If the type of the constant is an instantiated generic, we need to emit
// that type so the linker knows about it. See issue 51245.
_ = reflectdata.TypeLinksym(t)
}
// For const string, type:string isn't the real type.
}

if t.IsInteger() {
base.Ctxt.DwarfIntConst(n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v))
} else if t.IsString() {
base.Ctxt.DwarfStringConst(n.Sym().Name, types.TypeSymName(t), ir.StringVal(n))
}
base.Ctxt.DwarfIntConst(n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v))
}

// addGCLocals adds gcargs, gclocals, gcregs, and stack object symbols to Ctxt.Data.
Expand Down
37 changes: 36 additions & 1 deletion src/cmd/internal/dwarf/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const InfoPrefix = "go:info."
// entries that contain constants.
const ConstInfoPrefix = "go:constinfo."

// ConstStringInfoPrefix is the prefix for all symbols containing
// DWARF info entries that are referred by string constants.
const ConstStringInfoPrefix = "$const."

// CUInfoPrefix is the prefix for symbols containing information to
// populate the DWARF compilation unit info entries.
const CUInfoPrefix = "go:cuinfo."
Expand Down Expand Up @@ -336,6 +340,7 @@ const (
DW_ABRV_INLINED_SUBROUTINE_RANGES
DW_ABRV_VARIABLE
DW_ABRV_INT_CONSTANT
DW_ABRV_STRING_CONSTANT
DW_ABRV_LEXICAL_BLOCK_RANGES
DW_ABRV_LEXICAL_BLOCK_SIMPLE
DW_ABRV_STRUCTFIELD
Expand All @@ -354,6 +359,7 @@ const (
DW_ABRV_BARE_PTRTYPE // only for void*, no DW_AT_type attr to please gdb 6.
DW_ABRV_SLICETYPE
DW_ABRV_STRINGTYPE
DW_ABRV_CONSTANT_STRINGTYPE
DW_ABRV_STRUCTTYPE
DW_ABRV_TYPEDECL
DW_ABRV_DICT_INDEX
Expand Down Expand Up @@ -575,7 +581,7 @@ var abbrevs = []dwAbbrev{
},
},

/* INT CONSTANT */
/* INT_CONSTANT */
{
DW_TAG_constant,
DW_CHILDREN_no,
Expand All @@ -586,6 +592,17 @@ var abbrevs = []dwAbbrev{
},
},

/* STRING_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_block1},
},
},

/* LEXICAL_BLOCK_RANGES */
{
DW_TAG_lexical_block,
Expand Down Expand Up @@ -807,6 +824,16 @@ var abbrevs = []dwAbbrev{
},
},

/* CONSTANT_STRINGTYPE */
{
DW_TAG_string_type,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_byte_size, DW_FORM_udata},
},
},

/* STRUCTTYPE */
{
DW_TAG_structure_type,
Expand Down Expand Up @@ -1031,6 +1058,14 @@ func PutIntConst(ctxt Context, info, typ Sym, name string, val int64) {
putattr(ctxt, info, DW_ABRV_INT_CONSTANT, DW_FORM_sdata, DW_CLS_CONSTANT, val, nil)
}

// PutStringConst writes a DIE for a string constant
func PutStringConst(ctxt Context, info, typ Sym, name string, val string) {
Uleb128put(ctxt, info, DW_ABRV_STRING_CONSTANT)
putattr(ctxt, info, DW_ABRV_STRING_CONSTANT, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
putattr(ctxt, info, DW_ABRV_STRING_CONSTANT, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, typ)
putattr(ctxt, info, DW_ABRV_STRING_CONSTANT, DW_FORM_block1, DW_CLS_BLOCK, int64(len(val)), []byte(val))
}

// PutGlobal writes a DIE for a global variable.
func PutGlobal(ctxt Context, info, typ, gvar Sym, name string) {
Uleb128put(ctxt, info, DW_ABRV_VARIABLE)
Expand Down
31 changes: 26 additions & 5 deletions src/cmd/internal/obj/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"cmd/internal/src"
"fmt"
"slices"
"strconv"
"strings"
"sync"
)
Expand Down Expand Up @@ -393,18 +394,38 @@ func (ctxt *Link) populateDWARF(curfn Func, s *LSym) {
ctxt.generateDebugLinesSymbol(s, lines)
}

// DwarfIntConst creates a link symbol for an integer constant with the
// given name, type and value.
func (ctxt *Link) DwarfIntConst(name, typename string, val int64) {
// ensureConstInfoSym ensures that the DWARF constant info symbol exists
func (ctxt *Link) ensureConstInfoSym() *LSym {
myimportpath := ctxt.Pkgpath
if myimportpath == "" {
return
return nil
}
s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) {
s.Type = objabi.SDWARFCONST
ctxt.Data = append(ctxt.Data, s)
})
dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val)
return s
}

// DwarfIntConst creates a link symbol for an integer constant with the
// given name, type and value.
func (ctxt *Link) DwarfIntConst(name, typename string, val int64) {
s := ctxt.ensureConstInfoSym()
if s == nil {
return
}
dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), ctxt.Pkgpath+"."+name, val)
}

// DwarfStringConst creates a link symbol for a string constant with the
// given name and value.
func (ctxt *Link) DwarfStringConst(name, typename, value string) {
s := ctxt.ensureConstInfoSym()
if s == nil {
return
}
typSym := ctxt.Lookup(dwarf.InfoPrefix + dwarf.ConstStringInfoPrefix + typename + "." + strconv.Itoa(len(value)))
dwarf.PutStringConst(dwCtxt{ctxt}, s, typSym, ctxt.Pkgpath+"."+name, value)
}

// DwarfGlobal creates a link symbol containing a DWARF entry for
Expand Down
35 changes: 33 additions & 2 deletions src/cmd/link/internal/ld/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"path"
"runtime"
"slices"
"strconv"
"strings"
"sync"
)
Expand Down Expand Up @@ -285,13 +286,16 @@ func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string) *dwarf.DWD
die.Link = parent.Child
parent.Child = die

newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name)

// Sanity check: all DIEs created in the linker should be named.
if name == "" {
panic("nameless DWARF DIE")
}

// for constant string types, we emit the nams later since it didn't use symbol name as DW_AT_name
if abbrev != dwarf.DW_ABRV_CONSTANT_STRINGTYPE {
newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name)
}

var st sym.SymKind
switch abbrev {
case dwarf.DW_ABRV_FUNCTYPEPARAM, dwarf.DW_ABRV_FUNCTYPEOUTPARAM, dwarf.DW_ABRV_DOTDOTDOT, dwarf.DW_ABRV_STRUCTFIELD, dwarf.DW_ABRV_ARRAYRANGE:
Expand Down Expand Up @@ -1183,6 +1187,24 @@ func getCompilationDir() string {
return "."
}

func (d *dwctxt) genConstStringType(name string) {
if d.find(name) != 0 {
return
}
i := strings.LastIndex(name, ".")
if i < 0 {
log.Fatalf("error: invalid constant string type name %q", name)
}
size, err := strconv.ParseInt(name[i+1:], 10, 64)
if err != nil {
log.Fatalf("error: invalid constant string type name %q: %v", name, err)
}
atname := name[len(dwarf.ConstStringInfoPrefix):i]
die := d.newdie(&dwtypes, dwarf.DW_ABRV_CONSTANT_STRINGTYPE, name)
newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(atname)), atname)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, size, 0)
}

func (d *dwctxt) importInfoSymbol(dsym loader.Sym) {
d.ldr.SetAttrReachable(dsym, true)
d.ldr.SetAttrNotInSymbolTable(dsym, true)
Expand All @@ -1207,6 +1229,15 @@ func (d *dwctxt) importInfoSymbol(dsym loader.Sym) {
// symbol name here?
sn := d.ldr.SymName(rsym)
tn := sn[len(dwarf.InfoPrefix):]

// If the symbol is a constant string type, we generate it
// These types do not exist in go,
// but can tell gdb how to interpret the corresponding values
if strings.HasPrefix(tn, dwarf.ConstStringInfoPrefix) {
d.genConstStringType(tn)
continue
}

ts := d.ldr.Lookup("type:"+tn, 0)
d.defgotype(ts)
}
Expand Down
15 changes: 14 additions & 1 deletion src/runtime/runtime-gdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,9 @@ package main
const aConstant int = 42
const largeConstant uint64 = ^uint64(0)
const minusOne int64 = -1
const typedS string = "typed string"
const untypedS = "untyped string"
const nulS string = "\x00str"

func main() {
println("hello world")
Expand Down Expand Up @@ -700,6 +703,9 @@ func TestGdbConst(t *testing.T) {
"-ex", "print main.minusOne",
"-ex", "print 'runtime.mSpanInUse'",
"-ex", "print 'runtime._PageSize'",
"-ex", "print main.typedS",
"-ex", "print main.untypedS",
"-ex", "print main.nulS",
filepath.Join(dir, "a.exe"),
}
gdbArgsFixup(args)
Expand All @@ -711,7 +717,14 @@ func TestGdbConst(t *testing.T) {

sgot := strings.ReplaceAll(string(got), "\r\n", "\n")

if !strings.Contains(sgot, "\n$1 = 42\n$2 = 18446744073709551615\n$3 = -1\n$4 = 1 '\\001'\n$5 = 8192") {
if !strings.Contains(sgot, `$1 = 42
$2 = 18446744073709551615
$3 = -1
$4 = 1 '\001'
$5 = 8192
$6 = "typed string"
$7 = "untyped string"
$8 = "\000str"`) {
t.Fatalf("output mismatch")
}
}
Expand Down