github.com/undoio/delve@v1.9.0/pkg/proc/i386_arch.go (about)

     1  package proc
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/undoio/delve/pkg/dwarf/frame"
     9  	"github.com/undoio/delve/pkg/dwarf/op"
    10  	"github.com/undoio/delve/pkg/dwarf/regnum"
    11  )
    12  
    13  var i386BreakInstruction = []byte{0xCC}
    14  
    15  // I386Arch returns an initialized I386Arch
    16  // struct.
    17  func I386Arch(goos string) *Arch {
    18  	return &Arch{
    19  		Name:                             "386",
    20  		ptrSize:                          4,
    21  		maxInstructionLength:             15,
    22  		breakpointInstruction:            i386BreakInstruction,
    23  		altBreakpointInstruction:         []byte{0xcd, 0x03},
    24  		breakInstrMovesPC:                true,
    25  		derefTLS:                         false,
    26  		prologues:                        prologuesI386,
    27  		fixFrameUnwindContext:            i386FixFrameUnwindContext,
    28  		switchStack:                      i386SwitchStack,
    29  		regSize:                          i386RegSize,
    30  		RegistersToDwarfRegisters:        i386RegistersToDwarfRegisters,
    31  		addrAndStackRegsToDwarfRegisters: i386AddrAndStackRegsToDwarfRegisters,
    32  		DwarfRegisterToString:            i386DwarfRegisterToString,
    33  		inhibitStepInto:                  i386InhibitStepInto,
    34  		asmDecode:                        i386AsmDecode,
    35  		PCRegNum:                         regnum.I386_Eip,
    36  		SPRegNum:                         regnum.I386_Esp,
    37  		asmRegisters:                     i386AsmRegisters,
    38  		RegisterNameToDwarf:              nameToDwarfFunc(regnum.I386NameToDwarf),
    39  	}
    40  }
    41  
    42  func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext {
    43  	i := bi.Arch
    44  	if i.sigreturnfn == nil {
    45  		i.sigreturnfn = bi.LookupFunc["runtime.sigreturn"]
    46  	}
    47  
    48  	if fctxt == nil || (i.sigreturnfn != nil && pc >= i.sigreturnfn.Entry && pc < i.sigreturnfn.End) {
    49  		// When there's no frame descriptor entry use BP (the frame pointer) instead
    50  		// - return register is [bp + i.PtrSize()] (i.e. [cfa-i.PtrSize()])
    51  		// - cfa is bp + i.PtrSize()*2
    52  		// - bp is [bp] (i.e. [cfa-i.PtrSize()*2])
    53  		// - sp is cfa
    54  
    55  		// When the signal handler runs it will move the execution to the signal
    56  		// handling stack (installed using the sigaltstack system call).
    57  		// This isn't i proper stack switch: the pointer to g in TLS will still
    58  		// refer to whatever g was executing on that thread before the signal was
    59  		// received.
    60  		// Since go did not execute i stack switch the previous value of sp, pc
    61  		// and bp is not saved inside g.sched, as it normally would.
    62  		// The only way to recover is to either read sp/pc from the signal context
    63  		// parameter (the ucontext_t* parameter) or to unconditionally follow the
    64  		// frame pointer when we get to runtime.sigreturn (which is what we do
    65  		// here).
    66  
    67  		return &frame.FrameContext{
    68  			RetAddrReg: regnum.I386_Eip,
    69  			Regs: map[uint64]frame.DWRule{
    70  				regnum.I386_Eip: frame.DWRule{
    71  					Rule:   frame.RuleOffset,
    72  					Offset: int64(-i.PtrSize()),
    73  				},
    74  				regnum.I386_Ebp: frame.DWRule{
    75  					Rule:   frame.RuleOffset,
    76  					Offset: int64(-2 * i.PtrSize()),
    77  				},
    78  				regnum.I386_Esp: frame.DWRule{
    79  					Rule:   frame.RuleValOffset,
    80  					Offset: 0,
    81  				},
    82  			},
    83  			CFA: frame.DWRule{
    84  				Rule:   frame.RuleCFA,
    85  				Reg:    regnum.I386_Ebp,
    86  				Offset: int64(2 * i.PtrSize()),
    87  			},
    88  		}
    89  	}
    90  
    91  	if i.crosscall2fn == nil {
    92  		i.crosscall2fn = bi.LookupFunc["crosscall2"]
    93  	}
    94  
    95  	// TODO(chainhelen), need to check whether there is a bad frame descriptor like amd64.
    96  	// crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_386.s.
    97  	if i.crosscall2fn != nil && pc >= i.crosscall2fn.Entry && pc < i.crosscall2fn.End {
    98  		rule := fctxt.CFA
    99  		fctxt.CFA = rule
   100  	}
   101  
   102  	// We assume that EBP is the frame pointer and we want to keep it updated,
   103  	// so that we can use it to unwind the stack even when we encounter frames
   104  	// without descriptor entries.
   105  	// If there isn't i rule already we emit one.
   106  	if fctxt.Regs[regnum.I386_Ebp].Rule == frame.RuleUndefined {
   107  		fctxt.Regs[regnum.I386_Ebp] = frame.DWRule{
   108  			Rule:   frame.RuleFramePointer,
   109  			Reg:    regnum.I386_Ebp,
   110  			Offset: 0,
   111  		}
   112  	}
   113  
   114  	return fctxt
   115  }
   116  
   117  // SwitchStack will use the current frame to determine if it's time to
   118  func i386SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
   119  	if it.frame.Current.Fn == nil {
   120  		if it.systemstack && it.g != nil && it.top {
   121  			it.switchToGoroutineStack()
   122  			return true
   123  		}
   124  		return false
   125  	}
   126  	switch it.frame.Current.Fn.Name {
   127  	case "runtime.asmcgocall", "runtime.cgocallback_gofunc": // TODO(chainhelen), need to support cgo stacktraces.
   128  		return false
   129  	case "runtime.goexit", "runtime.rt0_go", "runtime.mcall":
   130  		// Look for "top of stack" functions.
   131  		it.atend = true
   132  		return true
   133  
   134  	case "runtime.mstart":
   135  		// Calls to runtime.systemstack will switch to the systemstack then:
   136  		// 1. alter the goroutine stack so that it looks like systemstack_switch
   137  		//    was called
   138  		// 2. alter the system stack so that it looks like the bottom-most frame
   139  		//    belongs to runtime.mstart
   140  		// If we find a runtime.mstart frame on the system stack of a goroutine
   141  		// parked on runtime.systemstack_switch we assume runtime.systemstack was
   142  		// called and continue tracing from the parked position.
   143  
   144  		if it.top || !it.systemstack || it.g == nil {
   145  			return false
   146  		}
   147  		if fn := it.bi.PCToFunc(it.g.PC); fn == nil || fn.Name != "runtime.systemstack_switch" {
   148  			return false
   149  		}
   150  
   151  		it.switchToGoroutineStack()
   152  		return true
   153  
   154  	default:
   155  		if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.throw" && it.frame.Current.Fn.Name != "runtime.fatalthrow" {
   156  			// The runtime switches to the system stack in multiple places.
   157  			// This usually happens through a call to runtime.systemstack but there
   158  			// are functions that switch to the system stack manually (for example
   159  			// runtime.morestack).
   160  			// Since we are only interested in printing the system stack for cgo
   161  			// calls we switch directly to the goroutine stack if we detect that the
   162  			// function at the top of the stack is a runtime function.
   163  			//
   164  			// The function "runtime.throw" is deliberately excluded from this
   165  			// because it can end up in the stack during a cgo call and switching to
   166  			// the goroutine stack will exclude all the C functions from the stack
   167  			// trace.
   168  			it.switchToGoroutineStack()
   169  			return true
   170  		}
   171  
   172  		return false
   173  	}
   174  }
   175  
   176  // RegSize returns the size (in bytes) of register regnum.
   177  // The mapping between hardware registers and DWARF registers is specified
   178  // in the System V ABI Intel386 Architecture Processor Supplement page 25,
   179  // table 2.14
   180  // https://www.uclibc.org/docs/psABI-i386.pdf
   181  func i386RegSize(regnum uint64) int {
   182  	// XMM registers
   183  	if regnum >= 21 && regnum <= 36 {
   184  		return 16
   185  	}
   186  	// x87 registers
   187  	if regnum >= 11 && regnum <= 18 {
   188  		return 10
   189  	}
   190  	return 4
   191  }
   192  
   193  func i386RegistersToDwarfRegisters(staticBase uint64, regs Registers) *op.DwarfRegisters {
   194  	dregs := initDwarfRegistersFromSlice(regnum.I386MaxRegNum(), regs, regnum.I386NameToDwarf)
   195  	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0)
   196  	dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.I386NameToDwarf))
   197  
   198  	return dr
   199  }
   200  
   201  func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
   202  	dregs := make([]*op.DwarfRegister, regnum.I386_Eip+1)
   203  	dregs[regnum.I386_Eip] = op.DwarfRegisterFromUint64(pc)
   204  	dregs[regnum.I386_Esp] = op.DwarfRegisterFromUint64(sp)
   205  	dregs[regnum.I386_Ebp] = op.DwarfRegisterFromUint64(bp)
   206  
   207  	return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0)
   208  }
   209  
   210  func i386DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
   211  	name = regnum.I386ToName(j)
   212  
   213  	if reg == nil {
   214  		return name, false, ""
   215  	}
   216  
   217  	switch n := strings.ToLower(name); n {
   218  	case "eflags":
   219  		return name, false, eflagsDescription.Describe(reg.Uint64Val, 32)
   220  
   221  	case "tw", "fop":
   222  		return name, true, fmt.Sprintf("%#04x", reg.Uint64Val)
   223  
   224  	default:
   225  		if reg.Bytes != nil && strings.HasPrefix(n, "xmm") {
   226  			return name, true, formatSSEReg(name, reg.Bytes)
   227  		} else if reg.Bytes != nil && strings.HasPrefix(n, "st(") {
   228  			return name, true, formatX87Reg(reg.Bytes)
   229  		} else if reg.Bytes == nil || (reg.Bytes != nil && len(reg.Bytes) <= 8) {
   230  			return name, false, fmt.Sprintf("%#016x", reg.Uint64Val)
   231  		} else {
   232  			return name, false, fmt.Sprintf("%#x", reg.Bytes)
   233  		}
   234  	}
   235  }
   236  
   237  // i386InhibitStepInto returns whether StepBreakpoint can be set at pc.
   238  // When cgo or pie on 386 linux, compiler will insert more instructions (ex: call __x86.get_pc_thunk.).
   239  // StepBreakpoint shouldn't be set on __x86.get_pc_thunk and skip it.
   240  // See comments on stacksplit in $GOROOT/src/cmd/internal/obj/x86/obj6.go for generated instructions details.
   241  func i386InhibitStepInto(bi *BinaryInfo, pc uint64) bool {
   242  	if bi.SymNames != nil && bi.SymNames[pc] != nil &&
   243  		strings.HasPrefix(bi.SymNames[pc].Name, "__x86.get_pc_thunk.") {
   244  		return true
   245  	}
   246  	return false
   247  }