From 61daf80125be89ffd3be1c55d0e8b6847b7bc14a Mon Sep 17 00:00:00 2001 From: immanuwell Date: Sun, 9 Aug 2026 13:17:32 +0400 Subject: [PATCH] fix: restore absolute DWARF source paths Signed-off-by: immanuwell --- internal/dwarf/tree.go | 29 +++++++++++++++++- internal/dwarf/tree_test.go | 59 +++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/internal/dwarf/tree.go b/internal/dwarf/tree.go index 9d77759..5875448 100644 --- a/internal/dwarf/tree.go +++ b/internal/dwarf/tree.go @@ -6,6 +6,8 @@ import ( "fmt" "io" "os" + "path" + "slices" "strings" "github.com/davecgh/go-spew/spew" @@ -70,7 +72,7 @@ func newDWARFTreeReader(fileReader io.ReaderAt) (*Tree, error) { return nil, fmt.Errorf("failed to create line reader: %w", err) } - n = newNode(tree, entry, lr.Files()) + n = newNode(tree, entry, normalizeCompileUnitFiles(entry, lr.Files())) tree.root = n cur = n } else { @@ -89,6 +91,31 @@ func newDWARFTreeReader(fileReader io.ReaderAt) (*Tree, error) { return tree, nil } +func normalizeCompileUnitFiles(entry *dwarf.Entry, files []*dwarf.LineFile) []*dwarf.LineFile { + name, _ := entry.Val(dwarf.AttrName).(string) + compDir, _ := entry.Val(dwarf.AttrCompDir).(string) + if !path.IsAbs(name) || compDir == "" { + return files + } + + // debug/dwarf joins DWARF 5 directory and file entries even when the file + // name is absolute. Restore the compilation unit path when that happened. + joinedName := path.Join(compDir, name) + for i, file := range files { + if file == nil || file.Name != joinedName { + continue + } + + normalized := slices.Clone(files) + normalizedFile := *file + normalizedFile.Name = name + normalized[i] = &normalizedFile + return normalized + } + + return files +} + type Tree struct { root *Node index map[dwarf.Offset]*Node diff --git a/internal/dwarf/tree_test.go b/internal/dwarf/tree_test.go index d838225..bc52079 100644 --- a/internal/dwarf/tree_test.go +++ b/internal/dwarf/tree_test.go @@ -5,9 +5,68 @@ package dwarf import ( "debug/dwarf" + "slices" "testing" ) +func TestNormalizeCompileUnitFiles(t *testing.T) { + tests := []struct { + name string + unitName string + compDir string + fileName string + want string + }{ + { + name: "joined absolute path", + unitName: "/src/basic.c", + compDir: "/go", + fileName: "/go/src/basic.c", + want: "/src/basic.c", + }, + { + name: "relative unit path", + unitName: "src/basic.c", + compDir: "/go", + fileName: "/go/src/basic.c", + want: "/go/src/basic.c", + }, + { + name: "correct absolute path", + unitName: "/src/basic.c", + compDir: "/go", + fileName: "/src/basic.c", + want: "/src/basic.c", + }, + { + name: "unrelated file", + unitName: "/src/basic.c", + compDir: "/go", + fileName: "/go/src/header.h", + want: "/go/src/header.h", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + entry := &dwarf.Entry{Field: []dwarf.Field{ + {Attr: dwarf.AttrName, Val: tt.unitName}, + {Attr: dwarf.AttrCompDir, Val: tt.compDir}, + }} + file := &dwarf.LineFile{Name: tt.fileName} + files := []*dwarf.LineFile{file} + + got := normalizeCompileUnitFiles(entry, files) + if got[0].Name != tt.want { + t.Fatalf("normalized file name = %q, want %q", got[0].Name, tt.want) + } + if file.Name != tt.fileName || !slices.Equal(files, []*dwarf.LineFile{file}) { + t.Fatalf("normalizeCompileUnitFiles mutated its input") + } + }) + } +} + func TestNodeLocationsUseCompilationUnitFiles(t *testing.T) { firstUnitFiles := []*dwarf.LineFile{{Name: "first.c"}} secondUnitFiles := []*dwarf.LineFile{{Name: "second.c"}}