diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 37bbce03189c24..5f5219673b0487 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -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. diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index 6e06f139b03d31..a0f73bd5153bd7 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -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." @@ -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 @@ -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 @@ -575,7 +581,7 @@ var abbrevs = []dwAbbrev{ }, }, - /* INT CONSTANT */ + /* INT_CONSTANT */ { DW_TAG_constant, DW_CHILDREN_no, @@ -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, @@ -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, @@ -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) diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go index c6f321e3e53153..b2f66daddd5d3a 100644 --- a/src/cmd/internal/obj/dwarf.go +++ b/src/cmd/internal/obj/dwarf.go @@ -12,6 +12,7 @@ import ( "cmd/internal/src" "fmt" "slices" + "strconv" "strings" "sync" ) @@ -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 diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 602d70ddb98295..ee607decf2105a 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -29,6 +29,7 @@ import ( "path" "runtime" "slices" + "strconv" "strings" "sync" ) @@ -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: @@ -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) @@ -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) } diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 2286868567a322..1cbc4ca15ff6b9 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -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") @@ -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) @@ -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") } }