github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/proc/i386_arch.go (about)

     1  package proc
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/go-delve/delve/pkg/dwarf/frame"
     9  	"github.com/go-delve/delve/pkg/dwarf/op"
    10  	"github.com/go-delve/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  		RegnumToString:                   regnum.I386ToName,
    40  	}
    41  }
    42  
    43  func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext {
    44  	i := bi.Arch
    45  	if i.sigreturnfn == nil {
    46  		i.sigreturnfn = bi.lookupOneFunc("runtime.sigreturn")
    47  	}
    48  
    49  	if fctxt == nil || (i.sigreturnfn != nil && pc >= i.sigreturnfn.Entry && pc < i.sigreturnfn.End) {
    50  		// When there's no frame descriptor entry use BP (the frame pointer) instead
    51  		// - return register is [bp + i.PtrSize()] (i.e. [cfa-i.PtrSize()])
    52  		// - cfa is bp + i.PtrSize()*2
    53  		// - bp is [bp] (i.e. [cfa-i.PtrSize()*2])
    54  		// - sp is cfa
    55  
    56  		// When the signal handler runs it will move the execution to the signal
    57  		// handling stack (installed using the sigaltstack system call).
    58  		// This isn't i proper stack switch: the pointer to g in TLS will still
    59  		// refer to whatever g was executing on that thread before the signal was
    60  		// received.
    61  		// Since go did not execute i stack switch the previous value of sp, pc
    62  		// and bp is not saved inside g.sched, as it normally would.
    63  		// The only way to recover is to either read sp/pc from the signal context
    64  		// parameter (the ucontext_t* parameter) or to unconditionally follow the
    65  		// frame pointer when we get to runtime.sigreturn (which is what we do
    66  		// here).
    67  
    68  		return &frame.FrameContext{
    69  			RetAddrReg: regnum.I386_Eip,
    70  			Regs: map[uint64]frame.DWRule{
    71  				regnum.I386_Eip: {
    72  					Rule:   frame.RuleOffset,
    73  					Offset: int64(-i.PtrSize()),
    74  				},
    75  				regnum.I386_Ebp: {
    76  					Rule:   frame.RuleOffset,
    77  					Offset: int64(-2 * i.PtrSize()),
    78  				},
    79  				regnum.I386_Esp: {
    80  					Rule:   frame.RuleValOffset,
    81  					Offset: 0,
    82  				},
    83  			},
    84  			CFA: frame.DWRule{
    85  				Rule:   frame.RuleCFA,
    86  				Reg:    regnum.I386_Ebp,
    87  				Offset: int64(2 * i.PtrSize()),
    88  			},
    89  		}
    90  	}
    91  
    92  	if i.crosscall2fn == nil {
    93  		i.crosscall2fn = bi.lookupOneFunc("crosscall2")
    94  	}
    95  
    96  	// TODO(chainhelen), need to check whether there is a bad frame descriptor like amd64.
    97  	// crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_386.s.
    98  	if i.crosscall2fn != nil && pc >= i.crosscall2fn.Entry && pc < i.crosscall2fn.End {
    99  		rule := fctxt.CFA
   100  		fctxt.CFA = rule
   101  	}
   102  
   103  	// We assume that EBP is the frame pointer and we want to keep it updated,
   104  	// so that we can use it to unwind the stack even when we encounter frames
   105  	// without descriptor entries.
   106  	// If there isn't i rule already we emit one.
   107  	if fctxt.Regs[regnum.I386_Ebp].Rule == frame.RuleUndefined {
   108  		fctxt.Regs[regnum.I386_Ebp] = frame.DWRule{
   109  			Rule:   frame.RuleFramePointer,
   110  			Reg:    regnum.I386_Ebp,
   111  			Offset: 0,
   112  		}
   113  	}
   114  
   115  	return fctxt
   116  }
   117  
   118  // SwitchStack will use the current frame to determine if it's time to
   119  func i386SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
   120  	if it.frame.Current.Fn == nil {
   121  		if it.systemstack && it.g != nil && it.top {
   122  			it.switchToGoroutineStack()
   123  			return true
   124  		}
   125  		return false
   126  	}
   127  	switch it.frame.Current.Fn.Name {
   128  	case "runtime.asmcgocall", "runtime.cgocallback_gofunc": // TODO(chainhelen), need to support cgo stacktraces.
   129  		return false
   130  	case "runtime.goexit", "runtime.rt0_go":
   131  		// Look for "top of stack" functions.
   132  		it.atend = true
   133  		return true
   134  
   135  	case "runtime.mcall":
   136  		if it.systemstack && it.g != nil {
   137  			it.switchToGoroutineStack()
   138  			return true
   139  		}
   140  		it.atend = true
   141  		return true
   142  
   143  	case "runtime.mstart":
   144  		// Calls to runtime.systemstack will switch to the systemstack then:
   145  		// 1. alter the goroutine stack so that it looks like systemstack_switch
   146  		//    was called
   147  		// 2. alter the system stack so that it looks like the bottom-most frame
   148  		//    belongs to runtime.mstart
   149  		// If we find a runtime.mstart frame on the system stack of a goroutine
   150  		// parked on runtime.systemstack_switch we assume runtime.systemstack was
   151  		// called and continue tracing from the parked position.
   152  
   153  		if it.top || !it.systemstack || it.g == nil {
   154  			return false
   155  		}
   156  		if fn := it.bi.PCToFunc(it.g.PC); fn == nil || fn.Name != "runtime.systemstack_switch" {
   157  			return false
   158  		}
   159  
   160  		it.switchToGoroutineStack()
   161  		return true
   162  
   163  	case "runtime.newstack", "runtime.systemstack":
   164  		if it.systemstack && it.g != nil {
   165  			it.switchToGoroutineStack()
   166  			return true
   167  		}
   168  
   169  		return false
   170  
   171  	default:
   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(uint64(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  }