mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 18:57:18 +08:00 
			
		
		
		
	 5372588c61
			
		
	
	5372588c61
	
	
	
		
			
			When creating a stack trace we should switch between the goroutine stack and the system stack (where cgo code is executed) as appropriate to reconstruct the logical stacktrace. Goroutines that are currently executing on the system stack will have the SystemStack flag set, frames of the goroutine stack will have a negative FrameOffset (like always) and frames of the system stack will have a positive FrameOffset (which is actually just the CFA value for the frame). Updates #935
		
			
				
	
	
		
			285 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package proc
 | |
| 
 | |
| import (
 | |
| 	"encoding/binary"
 | |
| 
 | |
| 	"github.com/derekparker/delve/pkg/dwarf/frame"
 | |
| 	"github.com/derekparker/delve/pkg/dwarf/op"
 | |
| 	"golang.org/x/arch/x86/x86asm"
 | |
| )
 | |
| 
 | |
| // Arch defines an interface for representing a
 | |
| // CPU architecture.
 | |
| type Arch interface {
 | |
| 	PtrSize() int
 | |
| 	BreakpointInstruction() []byte
 | |
| 	BreakpointSize() int
 | |
| 	DerefTLS() bool
 | |
| 	FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext
 | |
| 	RegSize(uint64) int
 | |
| 	RegistersToDwarfRegisters(Registers) op.DwarfRegisters
 | |
| 	GoroutineToDwarfRegisters(*G) op.DwarfRegisters
 | |
| }
 | |
| 
 | |
| // AMD64 represents the AMD64 CPU architecture.
 | |
| type AMD64 struct {
 | |
| 	ptrSize                 int
 | |
| 	breakInstruction        []byte
 | |
| 	breakInstructionLen     int
 | |
| 	gStructOffset           uint64
 | |
| 	hardwareBreakpointUsage []bool
 | |
| 	goos                    string
 | |
| 
 | |
| 	// crosscall2fn is the DIE of crosscall2, a function used by the go runtime
 | |
| 	// to call C functions. This function in go 1.9 (and previous versions) had
 | |
| 	// a bad frame descriptor which needs to be fixed to generate good stack
 | |
| 	// traces.
 | |
| 	crosscall2fn *Function
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	amd64DwarfIPRegNum uint64 = 16
 | |
| 	amd64DwarfSPRegNum uint64 = 7
 | |
| 	amd64DwarfBPRegNum uint64 = 6
 | |
| )
 | |
| 
 | |
| // AMD64Arch returns an initialized AMD64
 | |
| // struct.
 | |
| func AMD64Arch(goos string) *AMD64 {
 | |
| 	var breakInstr = []byte{0xCC}
 | |
| 
 | |
| 	return &AMD64{
 | |
| 		ptrSize:                 8,
 | |
| 		breakInstruction:        breakInstr,
 | |
| 		breakInstructionLen:     len(breakInstr),
 | |
| 		hardwareBreakpointUsage: make([]bool, 4),
 | |
| 		goos: goos,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // PtrSize returns the size of a pointer
 | |
| // on this architecture.
 | |
| func (a *AMD64) PtrSize() int {
 | |
| 	return a.ptrSize
 | |
| }
 | |
| 
 | |
| // BreakpointInstruction returns the Breakpoint
 | |
| // instruction for this architecture.
 | |
| func (a *AMD64) BreakpointInstruction() []byte {
 | |
| 	return a.breakInstruction
 | |
| }
 | |
| 
 | |
| // BreakpointSize returns the size of the
 | |
| // breakpoint instruction on this architecture.
 | |
| func (a *AMD64) BreakpointSize() int {
 | |
| 	return a.breakInstructionLen
 | |
| }
 | |
| 
 | |
| // If DerefTLS returns true the value of regs.TLS()+GStructOffset() is a
 | |
| // pointer to the G struct
 | |
| func (a *AMD64) DerefTLS() bool {
 | |
| 	return a.goos == "windows"
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	crosscall2SPOffsetBad        = 0x8
 | |
| 	crosscall2SPOffsetWindows    = 0x118
 | |
| 	crosscall2SPOffsetNonWindows = 0x58
 | |
| )
 | |
| 
 | |
| // FixFrameUnwindContext adds default architecture rules to fctxt or returns
 | |
| // the default frame unwind context if fctxt is nil.
 | |
| func (a *AMD64) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext {
 | |
| 	if fctxt == nil {
 | |
| 		// When there's no frame descriptor entry use BP (the frame pointer) instead
 | |
| 		// - return register is [bp + a.PtrSize()] (i.e. [cfa-a.PtrSize()])
 | |
| 		// - cfa is bp + a.PtrSize()*2
 | |
| 		// - bp is [bp] (i.e. [cfa-a.PtrSize()*2])
 | |
| 		// - sp is cfa
 | |
| 
 | |
| 		return &frame.FrameContext{
 | |
| 			RetAddrReg: amd64DwarfIPRegNum,
 | |
| 			Regs: map[uint64]frame.DWRule{
 | |
| 				amd64DwarfIPRegNum: frame.DWRule{
 | |
| 					Rule:   frame.RuleOffset,
 | |
| 					Offset: int64(-a.PtrSize()),
 | |
| 				},
 | |
| 				amd64DwarfBPRegNum: frame.DWRule{
 | |
| 					Rule:   frame.RuleOffset,
 | |
| 					Offset: int64(-2 * a.PtrSize()),
 | |
| 				},
 | |
| 				amd64DwarfSPRegNum: frame.DWRule{
 | |
| 					Rule:   frame.RuleValOffset,
 | |
| 					Offset: 0,
 | |
| 				},
 | |
| 			},
 | |
| 			CFA: frame.DWRule{
 | |
| 				Rule:   frame.RuleCFA,
 | |
| 				Reg:    amd64DwarfBPRegNum,
 | |
| 				Offset: int64(2 * a.PtrSize()),
 | |
| 			},
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if a.crosscall2fn == nil {
 | |
| 		a.crosscall2fn = bi.LookupFunc["crosscall2"]
 | |
| 	}
 | |
| 
 | |
| 	if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End {
 | |
| 		rule := fctxt.CFA
 | |
| 		if rule.Offset == crosscall2SPOffsetBad {
 | |
| 			switch a.goos {
 | |
| 			case "windows":
 | |
| 				rule.Offset += crosscall2SPOffsetWindows
 | |
| 			default:
 | |
| 				rule.Offset += crosscall2SPOffsetNonWindows
 | |
| 			}
 | |
| 		}
 | |
| 		fctxt.CFA = rule
 | |
| 	}
 | |
| 
 | |
| 	// We assume that RBP is the frame pointer and we want to keep it updated,
 | |
| 	// so that we can use it to unwind the stack even when we encounter frames
 | |
| 	// without descriptor entries.
 | |
| 	// If there isn't a rule already we emit one.
 | |
| 	if fctxt.Regs[amd64DwarfBPRegNum].Rule == frame.RuleUndefined {
 | |
| 		fctxt.Regs[amd64DwarfBPRegNum] = frame.DWRule{
 | |
| 			Rule:   frame.RuleFramePointer,
 | |
| 			Reg:    amd64DwarfBPRegNum,
 | |
| 			Offset: 0,
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return fctxt
 | |
| }
 | |
| 
 | |
| // RegSize returns the size (in bytes) of register regnum.
 | |
| // The mapping between hardware registers and DWARF registers is specified
 | |
| // in the System V ABI AMD64 Architecture Processor Supplement page 57,
 | |
| // figure 3.36
 | |
| // https://www.uclibc.org/docs/psABI-x86_64.pdf
 | |
| func (a *AMD64) RegSize(regnum uint64) int {
 | |
| 	// XMM registers
 | |
| 	if regnum > amd64DwarfIPRegNum && regnum <= 32 {
 | |
| 		return 16
 | |
| 	}
 | |
| 	// x87 registers
 | |
| 	if regnum >= 33 && regnum <= 40 {
 | |
| 		return 10
 | |
| 	}
 | |
| 	return 8
 | |
| }
 | |
| 
 | |
| // The mapping between hardware registers and DWARF registers is specified
 | |
| // in the System V ABI AMD64 Architecture Processor Supplement page 57,
 | |
| // figure 3.36
 | |
| // https://www.uclibc.org/docs/psABI-x86_64.pdf
 | |
| 
 | |
| var asm64DwarfToHardware = map[int]x86asm.Reg{
 | |
| 	0:  x86asm.RAX,
 | |
| 	1:  x86asm.RDX,
 | |
| 	2:  x86asm.RCX,
 | |
| 	3:  x86asm.RBX,
 | |
| 	4:  x86asm.RSI,
 | |
| 	5:  x86asm.RDI,
 | |
| 	8:  x86asm.R8,
 | |
| 	9:  x86asm.R9,
 | |
| 	10: x86asm.R10,
 | |
| 	11: x86asm.R11,
 | |
| 	12: x86asm.R12,
 | |
| 	13: x86asm.R13,
 | |
| 	14: x86asm.R14,
 | |
| 	15: x86asm.R15,
 | |
| }
 | |
| 
 | |
| var amd64DwarfToName = map[int]string{
 | |
| 	17: "XMM0",
 | |
| 	18: "XMM1",
 | |
| 	19: "XMM2",
 | |
| 	20: "XMM3",
 | |
| 	21: "XMM4",
 | |
| 	22: "XMM5",
 | |
| 	23: "XMM6",
 | |
| 	24: "XMM7",
 | |
| 	25: "XMM8",
 | |
| 	26: "XMM9",
 | |
| 	27: "XMM10",
 | |
| 	28: "XMM11",
 | |
| 	29: "XMM12",
 | |
| 	30: "XMM13",
 | |
| 	31: "XMM14",
 | |
| 	32: "XMM15",
 | |
| 	33: "ST(0)",
 | |
| 	34: "ST(1)",
 | |
| 	35: "ST(2)",
 | |
| 	36: "ST(3)",
 | |
| 	37: "ST(4)",
 | |
| 	38: "ST(5)",
 | |
| 	39: "ST(6)",
 | |
| 	40: "ST(7)",
 | |
| 	49: "Eflags",
 | |
| 	50: "Es",
 | |
| 	51: "Cs",
 | |
| 	52: "Ss",
 | |
| 	53: "Ds",
 | |
| 	54: "Fs",
 | |
| 	55: "Gs",
 | |
| 	58: "Fs_base",
 | |
| 	59: "Gs_base",
 | |
| 	64: "MXCSR",
 | |
| 	65: "CW",
 | |
| 	66: "SW",
 | |
| }
 | |
| 
 | |
| func maxAmd64DwarfRegister() int {
 | |
| 	max := int(amd64DwarfIPRegNum)
 | |
| 	for i := range asm64DwarfToHardware {
 | |
| 		if i > max {
 | |
| 			max = i
 | |
| 		}
 | |
| 	}
 | |
| 	for i := range amd64DwarfToName {
 | |
| 		if i > max {
 | |
| 			max = i
 | |
| 		}
 | |
| 	}
 | |
| 	return max
 | |
| }
 | |
| 
 | |
| // RegistersToDwarfRegisters converts hardware registers to the format used
 | |
| // by the DWARF expression interpreter.
 | |
| func (a *AMD64) RegistersToDwarfRegisters(regs Registers) op.DwarfRegisters {
 | |
| 	dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1)
 | |
| 
 | |
| 	dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC())
 | |
| 	dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP())
 | |
| 	dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP())
 | |
| 
 | |
| 	for dwarfReg, asmReg := range asm64DwarfToHardware {
 | |
| 		v, err := regs.Get(int(asmReg))
 | |
| 		if err == nil {
 | |
| 			dregs[dwarfReg] = op.DwarfRegisterFromUint64(v)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, reg := range regs.Slice() {
 | |
| 		for dwarfReg, regName := range amd64DwarfToName {
 | |
| 			if regName == reg.Name {
 | |
| 				dregs[dwarfReg] = op.DwarfRegisterFromBytes(reg.Bytes)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return op.DwarfRegisters{Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
 | |
| }
 | |
| 
 | |
| // GoroutineToDwarfRegisters extract the saved DWARF registers from a parked
 | |
| // goroutine in the format used by the DWARF expression interpreter.
 | |
| func (a *AMD64) GoroutineToDwarfRegisters(g *G) op.DwarfRegisters {
 | |
| 	dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1)
 | |
| 	dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(g.PC)
 | |
| 	dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(g.SP)
 | |
| 	dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(g.BP)
 | |
| 	return op.DwarfRegisters{Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
 | |
| }
 |