pkg/proc: fix for file reference handling with DWARF 5 compilation units (#2327)

Add a helper method for collecting line table file references that
does the correct thing for DWARF 5 vs DWARF 4 (in the latter case you
have an implicit 0 entry which is the comp dir, whereas in the former
case you do not). This is to avoid out-of-bounds errors when examining
the file table section of a DWARF 5 compilation unit's line table.

Included is a new linux/amd-only test that includes a precompiled C
object file with a DWARF-5 section that triggers the bug in question.

Fixes #2319
This commit is contained in:
Than McIntosh 2021-01-29 12:23:52 -05:00 committed by GitHub
parent 7548542795
commit dceffacb89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 138 additions and 18 deletions

@ -1,19 +1,19 @@
Tests skipped by each supported backend: Tests skipped by each supported backend:
* 386 skipped = 2.1% (3/145) * 386 skipped = 2.1% (3/146)
* 1 broken * 1 broken
* 2 broken - cgo stacktraces * 2 broken - cgo stacktraces
* arm64 skipped = 2.1% (3/145) * arm64 skipped = 2.1% (3/146)
* 2 broken * 2 broken
* 1 broken - global variable symbolication * 1 broken - global variable symbolication
* darwin/lldb skipped = 0.69% (1/145) * darwin/lldb skipped = 0.68% (1/146)
* 1 upstream issue * 1 upstream issue
* freebsd skipped = 7.6% (11/145) * freebsd skipped = 7.5% (11/146)
* 11 broken * 11 broken
* linux/386/pie skipped = 0.69% (1/145) * linux/386/pie skipped = 0.68% (1/146)
* 1 broken * 1 broken
* pie skipped = 0.69% (1/145) * pie skipped = 0.68% (1/146)
* 1 upstream issue - https://github.com/golang/go/issues/29322 * 1 upstream issue - https://github.com/golang/go/issues/29322
* windows skipped = 1.4% (2/145) * windows skipped = 1.4% (2/146)
* 1 broken * 1 broken
* 1 upstream issue * 1 upstream issue

@ -0,0 +1,57 @@
Note:
-----
cfile-linux-amd64.syso was generated from the C source file that appears below.
Build with Clang version 10:
$ clang-10 -O -gdwarf-5 -c cfile.c -o cfile.syso
The DWARF of interest is for the function "qtop". Triggering the bug
requires that the source file in question appears as the first item
in the DWARF line table file section, e.g.
$ llvm-dwarfdump-10 --debug-line cfile.syso
....
standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0
standard_opcode_lengths[DW_LNS_set_isa] = 1
include_directories[ 0] = "/ssd2/go1/src/tmp/dlvbug"
file_names[ 0]:
name: "cfile.c"
dir_index: 0
md5_checksum: ...
// ------------------------begin source code for cfile.c----------------
#include <stdlib.h>
#include <string.h>
int glob = 99;
inline int qleaf(int lx, int ly, int *lv)
{
lv[lx&3] += 3;
return lv[ly&3];
}
int qmid(int mx, int my, int *lv, int *mv)
{
mv[mx&3] += qleaf(mx, my, lv);
return mv[my&3];
}
int qtop(int mx, int my)
{
int mv[64], lv[66], n = (mx < 64 ? 64 : mx);
memset(&mv[0], 9, sizeof(mv));
memset(&lv[0], 11, sizeof(mv));
return qmid(mx, my, lv, mv) + qleaf(mx, my, lv);
}
void Cfunc(int x) {
glob += qtop(x, 43);
}

@ -0,0 +1,5 @@
#include "textflag.h"
TEXT ·cfunc(SB),$0
JMP Cfunc(SB)

Binary file not shown.

@ -0,0 +1,7 @@
package main
func cfunc()
func main() {
cfunc()
}

@ -1911,17 +1911,12 @@ func (bi *BinaryInfo) loadDebugInfoMapsInlinedCalls(ctxt *loadDebugInfoMapsConte
reader.SkipChildren() reader.SkipChildren()
continue continue
} }
if cu.lineInfo == nil { callfile, cferr := cu.filePath(int(callfileidx), entry)
bi.logger.Warnf("reading debug_info: inlined call on a compilation unit without debug_line section at %#x", entry.Offset) if cferr != nil {
bi.logger.Warnf("%v", cferr)
reader.SkipChildren() reader.SkipChildren()
continue continue
} }
if int(callfileidx-1) >= len(cu.lineInfo.FileNames) {
bi.logger.Warnf("reading debug_info: CallFile (%d) of inlined call does not exist in compile unit file table at %#x", callfileidx, entry.Offset)
reader.SkipChildren()
continue
}
callfile := cu.lineInfo.FileNames[callfileidx-1].Path
fn.InlinedCalls = append(fn.InlinedCalls, InlinedCall{ fn.InlinedCalls = append(fn.InlinedCalls, InlinedCall{
cu: cu, cu: cu,
@ -2082,3 +2077,25 @@ func (bi *BinaryInfo) ListPackagesBuildInfo(includeFiles bool) []*PackageBuildIn
sort.Slice(r, func(i, j int) bool { return r[i].ImportPath < r[j].ImportPath }) sort.Slice(r, func(i, j int) bool { return r[i].ImportPath < r[j].ImportPath })
return r return r
} }
// cuFilePath takes a compilation unit "cu" and a file index reference
// "fileidx" and returns the corresponding file name entry from the
// DWARF line table associated with the unit; "entry" is the offset of
// the attribute where the file reference originated, for logging
// purposes. Return value is the file string and an error value; error
// will be non-nil if the file could not be recovered, perhaps due to
// malformed DWARF.
func (cu *compileUnit) filePath(fileidx int, entry *dwarf.Entry) (string, error) {
if cu.lineInfo == nil {
return "", fmt.Errorf("reading debug_info: file reference within a compilation unit without debug_line section at %#x", entry.Offset)
}
// File numbering is slightly different before and after DWARF 5;
// account for this here. See section 6.2.4 of the DWARF 5 spec.
if cu.Version < 5 {
fileidx--
}
if fileidx < 0 || fileidx >= len(cu.lineInfo.FileNames) {
return "", fmt.Errorf("reading debug_info: file index (%d) out of range in compile unit file table at %#x", fileidx, entry.Offset)
}
return cu.lineInfo.FileNames[fileidx].Path, nil
}

@ -4965,3 +4965,31 @@ func TestStepOutPreservesGoroutine(t *testing.T) {
} }
}) })
} }
func TestIssue2319(t *testing.T) {
// Check to make sure we don't crash on startup when the target is
// a binary with a mix of DWARF-5 C++ compilation units and
// DWARF-4 Go compilation units.
// Require CGO, since we need to use the external linker for this test.
protest.MustHaveCgo(t)
// The test fixture uses linux/amd64 assembly and a *.syso file
// that is linux/amd64, so skip for other architectures.
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skipf("skipping since not linux/amd64")
}
// Skip unless on 1.14 or later. The test fixture uses a *.syso
// file, which in 1.13 is not loaded unless we're in internal
// linking mode (we need external linking here).
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) {
t.Skip("test contains fixture that is specific to go 1.14+")
}
fixture := protest.BuildFixture("issue2319/", protest.BuildModeExternalLinker)
// Load up the binary and make sure there are no crashes.
bi := proc.NewBinaryInfo("linux", "amd64")
assertNoError(bi.LoadBinaryInfo(fixture.Path, 0, nil), t, "LoadBinaryInfo")
}

@ -377,7 +377,9 @@ func (it *stackIterator) appendInlineCalls(frames []Stackframe, frame Stackframe
if !okname || !okfileidx || !okline { if !okname || !okfileidx || !okline {
break break
} }
if fileidx-1 < 0 || fileidx-1 >= int64(len(frame.Current.Fn.cu.lineInfo.FileNames)) { var e *dwarf.Entry
filepath, fileErr := frame.Current.Fn.cu.filePath(int(fileidx), e)
if fileErr != nil {
break break
} }
@ -400,7 +402,7 @@ func (it *stackIterator) appendInlineCalls(frames []Stackframe, frame Stackframe
lastpc: frame.lastpc, lastpc: frame.lastpc,
}) })
frame.Call.File = frame.Current.Fn.cu.lineInfo.FileNames[fileidx-1].Path frame.Call.File = filepath
frame.Call.Line = int(line) frame.Call.Line = int(line)
} }

@ -64,7 +64,7 @@ func FindFixturesDir() string {
type BuildFlags uint32 type BuildFlags uint32
const ( const (
// LinkStrip enables '-ldflas="-s"'. // LinkStrip enables '-ldflags="-s"'.
LinkStrip BuildFlags = 1 << iota LinkStrip BuildFlags = 1 << iota
// EnableCGOOptimization will build CGO code with optimizations. // EnableCGOOptimization will build CGO code with optimizations.
EnableCGOOptimization EnableCGOOptimization
@ -76,6 +76,7 @@ const (
EnableDWZCompression EnableDWZCompression
BuildModePIE BuildModePIE
BuildModePlugin BuildModePlugin
BuildModeExternalLinker
AllNonOptimized AllNonOptimized
) )
@ -141,6 +142,9 @@ func BuildFixture(name string, flags BuildFlags) Fixture {
if flags&BuildModePlugin != 0 { if flags&BuildModePlugin != 0 {
buildFlags = append(buildFlags, "-buildmode=plugin") buildFlags = append(buildFlags, "-buildmode=plugin")
} }
if flags&BuildModeExternalLinker != 0 {
buildFlags = append(buildFlags, "-ldflags=-linkmode=external")
}
if ver.IsDevel() || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 11, Rev: -1}) { if ver.IsDevel() || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 11, Rev: -1}) {
if flags&EnableDWZCompression != 0 { if flags&EnableDWZCompression != 0 {
buildFlags = append(buildFlags, "-ldflags=-compressdwarf=false") buildFlags = append(buildFlags, "-ldflags=-compressdwarf=false")