Skip to content

Commit 43b8053

Browse files
authored
Parse and propagate the name of the kernel relocation symbol (google#675)
* Extend ObjTool to propagate the original mapping name. For kernel mappings, the original mapping file name (not the name under which we found the actual symbols file) is necessary to correctly compute the kernel relocation offset. Preserve that name through ObjTool. Tested: unit. * Preserve DSO name at decode time. * Use DSO consistently instead of baseName * Handle empty File correctly. * Clarify merging / mapping semantics for DSO field. * Switch extra field from DSO to a more narrow KernelRelocationOffset. * Wording.
1 parent 513e8ac commit 43b8053

17 files changed

+78
-34
lines changed

driver/driver.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,10 @@ type MappingSources map[string][]struct {
137137
type ObjTool interface {
138138
// Open opens the named object file. If the object is a shared
139139
// library, start/limit/offset are the addresses where it is mapped
140-
// into memory in the address space being inspected.
141-
Open(file string, start, limit, offset uint64) (ObjFile, error)
140+
// into memory in the address space being inspected. If the object
141+
// is a linux kernel, relocationSymbol is the name of the symbol
142+
// corresponding to the start address.
143+
Open(file string, start, limit, offset uint64, relocationSymbol string) (ObjFile, error)
142144

143145
// Disasm disassembles the named object file, starting at
144146
// the start address and stopping at (before) the end address.
@@ -232,8 +234,8 @@ type internalObjTool struct {
232234
ObjTool
233235
}
234236

235-
func (o *internalObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
236-
f, err := o.ObjTool.Open(file, start, limit, offset)
237+
func (o *internalObjTool) Open(file string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
238+
f, err := o.ObjTool.Open(file, start, limit, offset, relocationSymbol)
237239
if err != nil {
238240
return nil, err
239241
}

internal/binutils/binutils.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ func (bu *Binutils) Disasm(file string, start, end uint64, intelSyntax bool) ([]
284284
}
285285

286286
// Open satisfies the plugin.ObjTool interface.
287-
func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
287+
func (bu *Binutils) Open(name string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
288288
b := bu.get()
289289

290290
// Make sure file is a supported executable.
@@ -316,7 +316,7 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi
316316

317317
// Match against supported file types.
318318
if elfMagic == elf.ELFMAG {
319-
f, err := b.openELF(name, start, limit, offset)
319+
f, err := b.openELF(name, start, limit, offset, relocationSymbol)
320320
if err != nil {
321321
return nil, fmt.Errorf("error reading ELF file %s: %v", name, err)
322322
}
@@ -425,7 +425,7 @@ func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.Obj
425425
return b.openMachOCommon(name, of, start, limit, offset)
426426
}
427427

428-
func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
428+
func (b *binrep) openELF(name string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
429429
ef, err := elfOpen(name)
430430
if err != nil {
431431
return nil, fmt.Errorf("error parsing %s: %v", name, err)

internal/binutils/binutils_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ func TestObjFile(t *testing.T) {
346346
} {
347347
t.Run(tc.desc, func(t *testing.T) {
348348
bu := &Binutils{}
349-
f, err := bu.Open(filepath.Join("testdata", "exe_linux_64"), tc.start, tc.limit, tc.offset)
349+
f, err := bu.Open(filepath.Join("testdata", "exe_linux_64"), tc.start, tc.limit, tc.offset, "")
350350
if err != nil {
351351
t.Fatalf("Open: unexpected error %v", err)
352352
}
@@ -416,7 +416,7 @@ func TestMachoFiles(t *testing.T) {
416416
} {
417417
t.Run(tc.desc, func(t *testing.T) {
418418
bu := &Binutils{}
419-
f, err := bu.Open(filepath.Join("testdata", tc.file), tc.start, tc.limit, tc.offset)
419+
f, err := bu.Open(filepath.Join("testdata", tc.file), tc.start, tc.limit, tc.offset, "")
420420
if err != nil {
421421
t.Fatalf("Open: unexpected error %v", err)
422422
}
@@ -505,7 +505,7 @@ func TestPEFile(t *testing.T) {
505505
} {
506506
t.Run(tc.desc, func(t *testing.T) {
507507
bu := &Binutils{}
508-
f, err := bu.Open(filepath.Join("testdata", "exe_windows_64.exe"), tc.start, tc.limit, tc.offset)
508+
f, err := bu.Open(filepath.Join("testdata", "exe_windows_64.exe"), tc.start, tc.limit, tc.offset, "")
509509
if err != nil {
510510
t.Fatalf("Open: unexpected error %v", err)
511511
}
@@ -544,7 +544,7 @@ func TestOpenMalformedELF(t *testing.T) {
544544
// Test that opening a malformed ELF file will report an error containing
545545
// the word "ELF".
546546
bu := &Binutils{}
547-
_, err := bu.Open(filepath.Join("testdata", "malformed_elf"), 0, 0, 0)
547+
_, err := bu.Open(filepath.Join("testdata", "malformed_elf"), 0, 0, 0, "")
548548
if err == nil {
549549
t.Fatalf("Open: unexpected success")
550550
}
@@ -558,7 +558,7 @@ func TestOpenMalformedMachO(t *testing.T) {
558558
// Test that opening a malformed Mach-O file will report an error containing
559559
// the word "Mach-O".
560560
bu := &Binutils{}
561-
_, err := bu.Open(filepath.Join("testdata", "malformed_macho"), 0, 0, 0)
561+
_, err := bu.Open(filepath.Join("testdata", "malformed_macho"), 0, 0, 0, "")
562562
if err == nil {
563563
t.Fatalf("Open: unexpected success")
564564
}
@@ -818,7 +818,7 @@ func TestELFObjAddr(t *testing.T) {
818818
} {
819819
t.Run(tc.desc, func(t *testing.T) {
820820
b := binrep{}
821-
o, err := b.openELF(name, tc.start, tc.limit, tc.offset)
821+
o, err := b.openELF(name, tc.start, tc.limit, tc.offset, "")
822822
if (err != nil) != tc.wantOpenError {
823823
t.Errorf("openELF got error %v, want any error=%v", err, tc.wantOpenError)
824824
}

internal/driver/cli.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
9898
// Recognize first argument as an executable or buildid override.
9999
if len(args) > 1 {
100100
arg0 := args[0]
101-
if file, err := o.Obj.Open(arg0, 0, ^uint64(0), 0); err == nil {
101+
if file, err := o.Obj.Open(arg0, 0, ^uint64(0), 0, ""); err == nil {
102102
file.Close()
103103
execName = arg0
104104
args = args[1:]

internal/driver/driver_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1580,7 +1580,7 @@ func TestSymbolzAfterMerge(t *testing.T) {
15801580

15811581
type mockObjTool struct{}
15821582

1583-
func (*mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
1583+
func (*mockObjTool) Open(file string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
15841584
return &mockFile{file, "abcdef", 0}, nil
15851585
}
15861586

internal/driver/fetch.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -420,12 +420,14 @@ mapping:
420420
fileNames = append(fileNames, filepath.Join(path, m.File))
421421
}
422422
for _, name := range fileNames {
423-
if f, err := obj.Open(name, m.Start, m.Limit, m.Offset); err == nil {
423+
if f, err := obj.Open(name, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol); err == nil {
424424
defer f.Close()
425425
fileBuildID := f.BuildID()
426426
if m.BuildID != "" && m.BuildID != fileBuildID {
427427
ui.PrintErr("Ignoring local file " + name + ": build-id mismatch (" + m.BuildID + " != " + fileBuildID + ")")
428428
} else {
429+
// Explicitly do not update KernelRelocationSymbol --
430+
// the new local file name is most likely missing it.
429431
m.File = name
430432
continue mapping
431433
}
@@ -449,6 +451,8 @@ mapping:
449451
if execName, buildID := s.ExecName, s.BuildID; execName != "" || buildID != "" {
450452
m := p.Mapping[0]
451453
if execName != "" {
454+
// Explicitly do not update KernelRelocationSymbol --
455+
// the source override is most likely missing it.
452456
m.File = execName
453457
}
454458
if buildID != "" {

internal/driver/fetch_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ type testObj struct {
147147
home string
148148
}
149149

150-
func (o testObj) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
150+
func (o testObj) Open(file string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
151151
switch file {
152152
case "/alternate/architecture/binary":
153153
return testFile{file, "abcde10001"}, nil

internal/driver/webui_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func (f fakeObj) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
181181

182182
type fakeObjTool struct{}
183183

184-
func (obj fakeObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
184+
func (obj fakeObjTool) Open(file string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
185185
return fakeObj{}, nil
186186
}
187187

internal/plugin/plugin.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,10 @@ type MappingSources map[string][]struct {
109109
type ObjTool interface {
110110
// Open opens the named object file. If the object is a shared
111111
// library, start/limit/offset are the addresses where it is mapped
112-
// into memory in the address space being inspected.
113-
Open(file string, start, limit, offset uint64) (ObjFile, error)
112+
// into memory in the address space being inspected. If the object
113+
// is a linux kernel, relocationSymbol is the name of the symbol
114+
// corresponding to the start address.
115+
Open(file string, start, limit, offset uint64, relocationSymbol string) (ObjFile, error)
114116

115117
// Disasm disassembles the named object file, starting at
116118
// the start address and stopping at (before) the end address.

internal/report/report.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ func symbolsFromBinaries(prof *profile.Profile, g *graph.Graph, rx *regexp.Regex
526526
}
527527
}
528528

529-
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset)
529+
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol)
530530
if err != nil {
531531
fmt.Printf("%v\n", err)
532532
continue

internal/report/source.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ func (sp *sourcePrinter) objectFile(m *profile.Mapping) plugin.ObjFile {
744744
if object, ok := sp.objects[m.File]; ok {
745745
return object // May be nil if we detected an error earlier.
746746
}
747-
object, err := sp.objectTool.Open(m.File, m.Start, m.Limit, m.Offset)
747+
object, err := sp.objectTool.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol)
748748
if err != nil {
749749
object = nil
750750
}

internal/symbolizer/symbolizer.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force b
328328
if m.BuildID != "" {
329329
name += fmt.Sprintf(" (build ID %s)", m.BuildID)
330330
}
331-
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset)
331+
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol)
332332
if err != nil {
333333
ui.PrintErr("Local symbolization failed for ", name, ": ", err)
334334
missingBinaries = true

internal/symbolizer/symbolizer_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ func frame(fname, file string, line int) plugin.Frame {
263263

264264
type mockObjTool struct{}
265265

266-
func (mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
266+
func (mockObjTool) Open(file string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
267267
return mockObjFile{frames: mockAddresses}, nil
268268
}
269269

profile/encode.go

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package profile
1717
import (
1818
"errors"
1919
"sort"
20+
"strings"
2021
)
2122

2223
func (p *Profile) decoder() []decoder {
@@ -252,6 +253,14 @@ func (p *Profile) postDecode() error {
252253
} else {
253254
mappings[m.ID] = m
254255
}
256+
257+
// If this a main linux kernel mapping with a relocation symbol suffix
258+
// ("[kernel.kallsyms]_text"), extract said suffix.
259+
// It is fairly hacky to handle at this level, but the alternatives appear even worse.
260+
if strings.HasPrefix(m.File, "[kernel.kallsyms]") {
261+
m.KernelRelocationSymbol = strings.ReplaceAll(m.File, "[kernel.kallsyms]", "")
262+
}
263+
255264
}
256265

257266
functions := make(map[uint64]*Function, len(p.Function))

profile/merge.go

+11-10
Original file line numberDiff line numberDiff line change
@@ -303,16 +303,17 @@ func (pm *profileMerger) mapMapping(src *Mapping) mapInfo {
303303
return mi
304304
}
305305
m := &Mapping{
306-
ID: uint64(len(pm.p.Mapping) + 1),
307-
Start: src.Start,
308-
Limit: src.Limit,
309-
Offset: src.Offset,
310-
File: src.File,
311-
BuildID: src.BuildID,
312-
HasFunctions: src.HasFunctions,
313-
HasFilenames: src.HasFilenames,
314-
HasLineNumbers: src.HasLineNumbers,
315-
HasInlineFrames: src.HasInlineFrames,
306+
ID: uint64(len(pm.p.Mapping) + 1),
307+
Start: src.Start,
308+
Limit: src.Limit,
309+
Offset: src.Offset,
310+
File: src.File,
311+
KernelRelocationSymbol: src.KernelRelocationSymbol,
312+
BuildID: src.BuildID,
313+
HasFunctions: src.HasFunctions,
314+
HasFilenames: src.HasFilenames,
315+
HasLineNumbers: src.HasLineNumbers,
316+
HasInlineFrames: src.HasInlineFrames,
316317
}
317318
pm.p.Mapping = append(pm.p.Mapping, m)
318319

profile/profile.go

+9
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,15 @@ type Mapping struct {
106106

107107
fileX int64
108108
buildIDX int64
109+
110+
// Name of the kernel relocation symbol ("_text" or "_stext"), extracted from File.
111+
// For linux kernel mappings generated by some tools, correct symbolization depends
112+
// on knowing which of the two possible relocation symbols was used for `Start`.
113+
// This is given to us as a suffix in `File` (e.g. "[kernel.kallsyms]_stext").
114+
//
115+
// Note, this public field is not persisted in the proto. For the purposes of
116+
// copying / merging / hashing profiles, it is considered subsumed by `File`.
117+
KernelRelocationSymbol string
109118
}
110119

111120
// Location corresponds to Profile.Location

profile/profile_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,16 @@ var cpuM = []*Mapping{
249249
HasLineNumbers: true,
250250
HasInlineFrames: true,
251251
},
252+
{
253+
ID: 5,
254+
Start: 0xffff000010080000,
255+
Limit: 0xffffffffffffffff,
256+
File: "[kernel.kallsyms]_text",
257+
HasFunctions: true,
258+
HasFilenames: true,
259+
HasLineNumbers: true,
260+
HasInlineFrames: true,
261+
},
252262
}
253263

254264
var cpuF = []*Function{
@@ -1434,6 +1444,13 @@ func TestSetMain(t *testing.T) {
14341444
}
14351445
}
14361446

1447+
func TestParseKernelRelocation(t *testing.T) {
1448+
src := testProfile1.Copy()
1449+
if src.Mapping[len(src.Mapping)-1].KernelRelocationSymbol != "_text" {
1450+
t.Errorf("got %s for Mapping.KernelRelocationSymbol", src.Mapping[0].KernelRelocationSymbol)
1451+
}
1452+
}
1453+
14371454
// parallel runs n copies of fn in parallel.
14381455
func parallel(n int, fn func()) {
14391456
var wg sync.WaitGroup

0 commit comments

Comments
 (0)