mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 09:46:56 +08:00
proc: catch panics when reading debug info for stripped executables (#3678)
Adds a recover to the functions reading debug info from the go runtime data structures for stripped executables. This function is best-effort and can sometimes fail in weird ways, instead of crashing recover any panic and turn them into errors. Fixes #3650
This commit is contained in:
committed by
GitHub
parent
fdb732b54e
commit
40b58f9643
@ -1455,63 +1455,10 @@ func loadBinaryInfoElf(bi *BinaryInfo, image *Image, path string, addr uint64, w
|
|||||||
if len(bi.Images) <= 1 {
|
if len(bi.Images) <= 1 {
|
||||||
fmt.Fprintln(os.Stderr, "Warning: no debug info found, some functionality will be missing such as stack traces and variable evaluation.")
|
fmt.Fprintln(os.Stderr, "Warning: no debug info found, some functionality will be missing such as stack traces and variable evaluation.")
|
||||||
}
|
}
|
||||||
cu := &compileUnit{}
|
err := loadBinaryInfoGoRuntimeElf(bi, image, path, elfFile)
|
||||||
cu.image = image
|
|
||||||
symTable, symTabAddr, err := readPcLnTableElf(elfFile, path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not read debug info (%v) and could not read go symbol table (%v)", dwerr, err)
|
return fmt.Errorf("could not read debug info (%v) and could not read go symbol table (%v)", dwerr, err)
|
||||||
}
|
}
|
||||||
image.symTable = symTable
|
|
||||||
noPtrSectionData, err := elfFile.Section(".noptrdata").Data()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
md, err := parseModuleData(noPtrSectionData, symTabAddr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
roDataAddr := elfFile.Section(".rodata").Addr
|
|
||||||
goFuncVal, err := findGoFuncVal(md, roDataAddr, bi.Arch.ptrSize)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
prog := gosym.ProgContaining(elfFile, goFuncVal)
|
|
||||||
inlFuncs := make(map[string]*Function)
|
|
||||||
for _, f := range image.symTable.Funcs {
|
|
||||||
fnEntry := f.Entry + image.StaticBase
|
|
||||||
if prog != nil {
|
|
||||||
inlCalls, err := image.symTable.GetInlineTree(&f, goFuncVal, prog.Vaddr, prog.ReaderAt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, inlfn := range inlCalls {
|
|
||||||
newInlinedCall := InlinedCall{cu: cu, LowPC: fnEntry + uint64(inlfn.ParentPC)}
|
|
||||||
if fn, ok := inlFuncs[inlfn.Name]; ok {
|
|
||||||
fn.InlinedCalls = append(fn.InlinedCalls, newInlinedCall)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
inlFuncs[inlfn.Name] = &Function{
|
|
||||||
Name: inlfn.Name,
|
|
||||||
Entry: 0, End: 0,
|
|
||||||
cu: cu,
|
|
||||||
InlinedCalls: []InlinedCall{
|
|
||||||
newInlinedCall,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn := Function{Name: f.Name, Entry: fnEntry, End: f.End + image.StaticBase, cu: cu}
|
|
||||||
bi.Functions = append(bi.Functions, fn)
|
|
||||||
}
|
|
||||||
for i := range inlFuncs {
|
|
||||||
bi.Functions = append(bi.Functions, *inlFuncs[i])
|
|
||||||
}
|
|
||||||
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
|
||||||
for f := range image.symTable.Files {
|
|
||||||
bi.Sources = append(bi.Sources, f)
|
|
||||||
}
|
|
||||||
sort.Strings(bi.Sources)
|
|
||||||
bi.Sources = uniq(bi.Sources)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
image.sepDebugCloser = sepFile
|
image.sepDebugCloser = sepFile
|
||||||
@ -1917,63 +1864,10 @@ func loadBinaryInfoMacho(bi *BinaryInfo, image *Image, path string, entryPoint u
|
|||||||
if len(bi.Images) <= 1 {
|
if len(bi.Images) <= 1 {
|
||||||
fmt.Fprintln(os.Stderr, "Warning: no debug info found, some functionality will be missing such as stack traces and variable evaluation.")
|
fmt.Fprintln(os.Stderr, "Warning: no debug info found, some functionality will be missing such as stack traces and variable evaluation.")
|
||||||
}
|
}
|
||||||
symTable, symTabAddr, err := readPcLnTableMacho(exe, path)
|
err := loadBinaryInfoGoRuntimeMacho(bi, image, path, exe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not read debug info (%v) and could not read go symbol table (%v)", dwerr, err)
|
return fmt.Errorf("could not read debug info (%v) and could not read go symbol table (%v)", dwerr, err)
|
||||||
}
|
}
|
||||||
image.symTable = symTable
|
|
||||||
cu := &compileUnit{}
|
|
||||||
cu.image = image
|
|
||||||
noPtrSectionData, err := exe.Section("__noptrdata").Data()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
md, err := parseModuleData(noPtrSectionData, symTabAddr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
roDataAddr := exe.Section("__rodata").Addr
|
|
||||||
goFuncVal, err := findGoFuncVal(md, roDataAddr, bi.Arch.ptrSize)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
seg := gosym.SegmentContaining(exe, goFuncVal)
|
|
||||||
inlFuncs := make(map[string]*Function)
|
|
||||||
for _, f := range image.symTable.Funcs {
|
|
||||||
fnEntry := f.Entry + image.StaticBase
|
|
||||||
if seg != nil {
|
|
||||||
inlCalls, err := image.symTable.GetInlineTree(&f, goFuncVal, seg.Addr, seg.ReaderAt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, inlfn := range inlCalls {
|
|
||||||
newInlinedCall := InlinedCall{cu: cu, LowPC: fnEntry + uint64(inlfn.ParentPC)}
|
|
||||||
if fn, ok := inlFuncs[inlfn.Name]; ok {
|
|
||||||
fn.InlinedCalls = append(fn.InlinedCalls, newInlinedCall)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
inlFuncs[inlfn.Name] = &Function{
|
|
||||||
Name: inlfn.Name,
|
|
||||||
Entry: 0, End: 0,
|
|
||||||
cu: cu,
|
|
||||||
InlinedCalls: []InlinedCall{
|
|
||||||
newInlinedCall,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn := Function{Name: f.Name, Entry: fnEntry, End: f.End + image.StaticBase, cu: cu}
|
|
||||||
bi.Functions = append(bi.Functions, fn)
|
|
||||||
}
|
|
||||||
for i := range inlFuncs {
|
|
||||||
bi.Functions = append(bi.Functions, *inlFuncs[i])
|
|
||||||
}
|
|
||||||
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
|
||||||
for f := range image.symTable.Files {
|
|
||||||
bi.Sources = append(bi.Sources, f)
|
|
||||||
}
|
|
||||||
sort.Strings(bi.Sources)
|
|
||||||
bi.Sources = uniq(bi.Sources)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
debugInfoBytes, err := godwarf.GetDebugSectionMacho(exe, "info")
|
debugInfoBytes, err := godwarf.GetDebugSectionMacho(exe, "info")
|
||||||
@ -2127,6 +2021,132 @@ func (bi *BinaryInfo) macOSDebugFrameBugWorkaround() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GO RUNTIME INFO ////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// loadBinaryInfoGoRuntimeElf loads information from the Go runtime sections
|
||||||
|
// of an ELF binary, it is only called when debug info has been stripped.
|
||||||
|
func loadBinaryInfoGoRuntimeElf(bi *BinaryInfo, image *Image, path string, elfFile *elf.File) (err error) {
|
||||||
|
// This is a best-effort procedure, it can go wrong in unexpected ways, so
|
||||||
|
// recover all panics.
|
||||||
|
defer func() {
|
||||||
|
ierr := recover()
|
||||||
|
if ierr != nil {
|
||||||
|
err = fmt.Errorf("error loading binary info from Go runtime: %v", ierr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
cu := &compileUnit{}
|
||||||
|
cu.image = image
|
||||||
|
symTable, symTabAddr, err := readPcLnTableElf(elfFile, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
image.symTable = symTable
|
||||||
|
noPtrSectionData, err := elfFile.Section(".noptrdata").Data()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
md, err := parseModuleData(noPtrSectionData, symTabAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
roDataAddr := elfFile.Section(".rodata").Addr
|
||||||
|
goFuncVal, err := findGoFuncVal(md, roDataAddr, bi.Arch.ptrSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
prog := gosym.ProgContaining(elfFile, goFuncVal)
|
||||||
|
var progAddr uint64
|
||||||
|
var progReaderAt io.ReaderAt
|
||||||
|
if prog != nil {
|
||||||
|
progAddr = prog.Vaddr
|
||||||
|
progReaderAt = prog.ReaderAt
|
||||||
|
}
|
||||||
|
return loadBinaryInfoGoRuntimeCommon(bi, image, cu, goFuncVal, progAddr, progReaderAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadBinaryInfoGoRuntimeMacho loads information from the Go runtime sections
|
||||||
|
// of an Macho-o binary, it is only called when debug info has been stripped.
|
||||||
|
func loadBinaryInfoGoRuntimeMacho(bi *BinaryInfo, image *Image, path string, exe *macho.File) (err error) {
|
||||||
|
// This is a best-effort procedure, it can go wrong in unexpected ways, so
|
||||||
|
// recover all panics.
|
||||||
|
defer func() {
|
||||||
|
ierr := recover()
|
||||||
|
if ierr != nil {
|
||||||
|
err = fmt.Errorf("error loading binary info from Go runtime: %v", ierr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
cu := &compileUnit{}
|
||||||
|
cu.image = image
|
||||||
|
symTable, symTabAddr, err := readPcLnTableMacho(exe, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
image.symTable = symTable
|
||||||
|
noPtrSectionData, err := exe.Section("__noptrdata").Data()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
md, err := parseModuleData(noPtrSectionData, symTabAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
roDataAddr := exe.Section("__rodata").Addr
|
||||||
|
goFuncVal, err := findGoFuncVal(md, roDataAddr, bi.Arch.ptrSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
seg := gosym.SegmentContaining(exe, goFuncVal)
|
||||||
|
var segAddr uint64
|
||||||
|
var segReaderAt io.ReaderAt
|
||||||
|
if seg != nil {
|
||||||
|
segAddr = seg.Addr
|
||||||
|
segReaderAt = seg.ReaderAt
|
||||||
|
}
|
||||||
|
return loadBinaryInfoGoRuntimeCommon(bi, image, cu, goFuncVal, segAddr, segReaderAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadBinaryInfoGoRuntimeCommon(bi *BinaryInfo, image *Image, cu *compileUnit, goFuncVal uint64, goFuncSegAddr uint64, goFuncReader io.ReaderAt) error {
|
||||||
|
inlFuncs := make(map[string]*Function)
|
||||||
|
for _, f := range image.symTable.Funcs {
|
||||||
|
fnEntry := f.Entry + image.StaticBase
|
||||||
|
if goFuncReader != nil {
|
||||||
|
inlCalls, err := image.symTable.GetInlineTree(&f, goFuncVal, goFuncSegAddr, goFuncReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, inlfn := range inlCalls {
|
||||||
|
newInlinedCall := InlinedCall{cu: cu, LowPC: fnEntry + uint64(inlfn.ParentPC)}
|
||||||
|
if fn, ok := inlFuncs[inlfn.Name]; ok {
|
||||||
|
fn.InlinedCalls = append(fn.InlinedCalls, newInlinedCall)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
inlFuncs[inlfn.Name] = &Function{
|
||||||
|
Name: inlfn.Name,
|
||||||
|
Entry: 0, End: 0,
|
||||||
|
cu: cu,
|
||||||
|
InlinedCalls: []InlinedCall{
|
||||||
|
newInlinedCall,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn := Function{Name: f.Name, Entry: fnEntry, End: f.End + image.StaticBase, cu: cu}
|
||||||
|
bi.Functions = append(bi.Functions, fn)
|
||||||
|
}
|
||||||
|
for i := range inlFuncs {
|
||||||
|
bi.Functions = append(bi.Functions, *inlFuncs[i])
|
||||||
|
}
|
||||||
|
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
||||||
|
for f := range image.symTable.Files {
|
||||||
|
bi.Sources = append(bi.Sources, f)
|
||||||
|
}
|
||||||
|
sort.Strings(bi.Sources)
|
||||||
|
bi.Sources = uniq(bi.Sources)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Do not call this function directly it isn't able to deal correctly with package paths
|
// Do not call this function directly it isn't able to deal correctly with package paths
|
||||||
func (bi *BinaryInfo) findType(name string) (godwarf.Type, error) {
|
func (bi *BinaryInfo) findType(name string) (godwarf.Type, error) {
|
||||||
name = strings.ReplaceAll(name, "interface{", "interface {")
|
name = strings.ReplaceAll(name, "interface{", "interface {")
|
||||||
|
|||||||
Reference in New Issue
Block a user