From 900bf7e125511c1bf1698f0a8723ecc0fbdb09ec Mon Sep 17 00:00:00 2001 From: Zxilly Date: Mon, 2 Jun 2025 04:04:02 +0800 Subject: [PATCH 1/4] cmd: emit dwarf for string constants --- src/cmd/compile/internal/gc/obj.go | 21 ++++++++++++----- src/cmd/internal/dwarf/dwarf.go | 37 +++++++++++++++++++++++++++++- src/cmd/internal/obj/dwarf.go | 31 +++++++++++++++++++++---- src/cmd/link/internal/ld/dwarf.go | 22 ++++++++++++++++++ src/runtime/runtime-gdb_test.go | 15 +++++++++++- 5 files changed, 113 insertions(+), 13 deletions(-) diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 37bbce03189c24..b00b2872a720ca 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -176,22 +176,31 @@ 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) } - base.Ctxt.DwarfIntConst(n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v)) + + 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, ir.StringVal(n)) + } } // 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..b1189baf90f6a6 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 referred by string constants. +const ConstStringInfoPrefix = "string$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..20c054ff50e5d6 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, value string) { + s := ctxt.ensureConstInfoSym() + if s == nil { + return + } + typSym := ctxt.Lookup(dwarf.InfoPrefix + dwarf.ConstStringInfoPrefix + 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..cc00f3608c4d08 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" ) @@ -1183,6 +1184,18 @@ func getCompilationDir() string { return "." } +func (d *dwctxt) genConstStringType(name string) { + if d.find(name) != 0 { + return + } + size, err := strconv.Atoi(name[len(dwarf.ConstStringInfoPrefix):]) + if err != nil { + log.Fatalf("error: invalid constant string size %q: %v", name, err) + } + die := d.newdie(&dwtypes, dwarf.DW_ABRV_CONSTANT_STRINGTYPE, name) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(size), 0) +} + func (d *dwctxt) importInfoSymbol(dsym loader.Sym) { d.ldr.SetAttrReachable(dsym, true) d.ldr.SetAttrNotInSymbolTable(dsym, true) @@ -1207,6 +1220,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") } } From 182a289ef3d4d9bdf05ceef4c02d4e71f567583f Mon Sep 17 00:00:00 2001 From: Zxilly Date: Tue, 3 Jun 2025 21:32:33 +0800 Subject: [PATCH 2/4] keep type name --- src/cmd/compile/internal/gc/obj.go | 2 +- src/cmd/internal/dwarf/dwarf.go | 4 ++-- src/cmd/internal/obj/dwarf.go | 4 ++-- src/cmd/link/internal/ld/dwarf.go | 10 +++++++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index b00b2872a720ca..0f2e062277366a 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -199,7 +199,7 @@ func dumpGlobalConst(n *ir.Name) { 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, ir.StringVal(n)) + base.Ctxt.DwarfStringConst(n.Sym().Name, types.TypeSymName(t), ir.StringVal(n)) } } diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index b1189baf90f6a6..a0f73bd5153bd7 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -28,8 +28,8 @@ const InfoPrefix = "go:info." const ConstInfoPrefix = "go:constinfo." // ConstStringInfoPrefix is the prefix for all symbols containing -// DWARF info entries that referred by string constants. -const ConstStringInfoPrefix = "string$const." +// 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. diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go index 20c054ff50e5d6..b2f66daddd5d3a 100644 --- a/src/cmd/internal/obj/dwarf.go +++ b/src/cmd/internal/obj/dwarf.go @@ -419,12 +419,12 @@ func (ctxt *Link) DwarfIntConst(name, typename string, val int64) { // DwarfStringConst creates a link symbol for a string constant with the // given name and value. -func (ctxt *Link) DwarfStringConst(name, value string) { +func (ctxt *Link) DwarfStringConst(name, typename, value string) { s := ctxt.ensureConstInfoSym() if s == nil { return } - typSym := ctxt.Lookup(dwarf.InfoPrefix + dwarf.ConstStringInfoPrefix + strconv.Itoa(len(value))) + typSym := ctxt.Lookup(dwarf.InfoPrefix + dwarf.ConstStringInfoPrefix + typename + "." + strconv.Itoa(len(value))) dwarf.PutStringConst(dwCtxt{ctxt}, s, typSym, ctxt.Pkgpath+"."+name, value) } diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index cc00f3608c4d08..b8e9f52493151a 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1188,12 +1188,16 @@ func (d *dwctxt) genConstStringType(name string) { if d.find(name) != 0 { return } - size, err := strconv.Atoi(name[len(dwarf.ConstStringInfoPrefix):]) + 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 size %q: %v", name, err) + log.Fatalf("error: invalid constant string type name %q: %v", name, err) } die := d.newdie(&dwtypes, dwarf.DW_ABRV_CONSTANT_STRINGTYPE, name) - newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(size), 0) + newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, size, 0) } func (d *dwctxt) importInfoSymbol(dsym loader.Sym) { From a2c54efd5f4c5deca0ecc3b523e0e0af318d58dc Mon Sep 17 00:00:00 2001 From: Zxilly Date: Tue, 3 Jun 2025 23:40:35 +0800 Subject: [PATCH 3/4] fix type name --- src/cmd/link/internal/ld/dwarf.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index b8e9f52493151a..ee607decf2105a 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -286,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: @@ -1196,7 +1199,9 @@ func (d *dwctxt) genConstStringType(name string) { 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) } From cefd445939396fb3492b8d165d71f6014a70ae7d Mon Sep 17 00:00:00 2001 From: Zxilly Date: Fri, 6 Jun 2025 04:18:02 +0800 Subject: [PATCH 4/4] remove type:string --- src/cmd/compile/internal/gc/obj.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 0f2e062277366a..5f5219673b0487 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -191,9 +191,12 @@ func dumpGlobalConst(n *ir.Name) { 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() {