mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 18:57:18 +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:
		 Alessandro Arzilli
					Alessandro Arzilli
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						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