diff --git a/pkg/proc/amd64_arch.go b/pkg/proc/amd64_arch.go index f7da8e49..f09a3d17 100644 --- a/pkg/proc/amd64_arch.go +++ b/pkg/proc/amd64_arch.go @@ -12,23 +12,6 @@ import ( "github.com/go-delve/delve/pkg/dwarf/op" ) -// AMD64 represents the AMD64 CPU architecture. -type AMD64 struct { - gStructOffset uint64 - 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 - - // sigreturnfn is the DIE of runtime.sigreturn, the return trampoline for - // the signal handler. See comment in FixFrameUnwindContext for a - // description of why this is needed. - sigreturnfn *Function -} - const ( amd64DwarfIPRegNum uint64 = 16 amd64DwarfSPRegNum uint64 = 7 @@ -39,51 +22,28 @@ var amd64BreakInstruction = []byte{0xCC} // AMD64Arch returns an initialized AMD64 // struct. -func AMD64Arch(goos string) *AMD64 { - return &AMD64{ - goos: goos, +func AMD64Arch(goos string) *Arch { + return &Arch{ + Name: "amd64", + ptrSize: 8, + maxInstructionLength: 15, + breakpointInstruction: amd64BreakInstruction, + breakInstrMovesPC: true, + derefTLS: goos == "windows", + prologues: prologuesAMD64, + fixFrameUnwindContext: amd64FixFrameUnwindContext, + switchStack: amd64SwitchStack, + regSize: amd64RegSize, + RegistersToDwarfRegisters: amd64RegistersToDwarfRegisters, + addrAndStackRegsToDwarfRegisters: amd64AddrAndStackRegsToDwarfRegisters, + DwarfRegisterToString: amd64DwarfRegisterToString, + inhibitStepInto: func(*BinaryInfo, uint64) bool { return false }, + asmDecode: amd64AsmDecode, } } -// PtrSize returns the size of a pointer -// on this architecture. -func (a *AMD64) PtrSize() int { - return 8 -} - -// MaxInstructionLength returns the maximum length of an instruction. -func (a *AMD64) MaxInstructionLength() int { - return 15 -} - -// BreakpointInstruction returns the Breakpoint -// instruction for this architecture. -func (a *AMD64) BreakpointInstruction() []byte { - return amd64BreakInstruction -} - -// BreakInstrMovesPC returns whether the -// breakpoint instruction will change the value -// of PC after being executed -func (a *AMD64) BreakInstrMovesPC() bool { - return true -} - -// BreakpointSize returns the size of the -// breakpoint instruction on this architecture. -func (a *AMD64) BreakpointSize() int { - return len(amd64BreakInstruction) -} - -// DerefTLS returns true if the value of regs.TLS()+GStructOffset() is a -// pointer to the G struct -func (a *AMD64) DerefTLS() bool { - return a.goos == "windows" -} - -// 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 { +func amd64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext { + a := bi.Arch if a.sigreturnfn == nil { a.sigreturnfn = bi.LookupFunc["runtime.sigreturn"] } @@ -138,7 +98,7 @@ func (a *AMD64) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi * if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End { rule := fctxt.CFA if rule.Offset == crosscall2SPOffsetBad { - switch a.goos { + switch bi.GOOS { case "windows": rule.Offset += crosscall2SPOffsetWindows default: @@ -168,10 +128,7 @@ func (a *AMD64) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi * // switch happens. const amd64cgocallSPOffsetSaveSlot = 0x28 -// SwitchStack will use the current frame to determine if it's time to -// switch between the system stack and the goroutine stack or vice versa. -// Sets it.atend when the top of the stack is reached. -func (a *AMD64) SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool { +func amd64SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool { if it.frame.Current.Fn == nil { return false } @@ -282,12 +239,12 @@ func (a *AMD64) SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool { } } -// RegSize returns the size (in bytes) of register regnum. +// amd64RegSize 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 { +func amd64RegSize(regnum uint64) int { // XMM registers if regnum > amd64DwarfIPRegNum && regnum <= 32 { return 16 @@ -387,9 +344,7 @@ func maxAmd64DwarfRegister() int { return max } -// RegistersToDwarfRegisters converts hardware registers to the format used -// by the DWARF expression interpreter. -func (a *AMD64) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { +func amd64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1) for _, reg := range regs.Slice(true) { @@ -408,9 +363,7 @@ func (a *AMD64) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op. } } -// AddrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in -// PC, SP, and BP registers in the format used by the DWARF expression interpreter. -func (a *AMD64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { +func amd64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1) dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) @@ -426,8 +379,7 @@ func (a *AMD64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint } } -// DwarfRegisterToString returns the name and value representation of the given register. -func (a *AMD64) DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { +func amd64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { name, ok := amd64DwarfToName[i] if !ok { name = fmt.Sprintf("unknown%d", i) @@ -559,9 +511,3 @@ func formatX87Reg(b []byte) string { return fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f) } - -// InhibitStepInto returns whether StepBreakpoint can be set at pc. -// Always return false on amd64. -func (a *AMD64) InhibitStepInto(bi *BinaryInfo, pc uint64) bool { - return false -} diff --git a/pkg/proc/amd64_disasm.go b/pkg/proc/amd64_disasm.go index 67ba7083..cbacd726 100644 --- a/pkg/proc/amd64_disasm.go +++ b/pkg/proc/amd64_disasm.go @@ -8,16 +8,10 @@ import ( "golang.org/x/arch/x86/x86asm" ) -// AsmDecode decodes the assembly instruction starting at mem[0:] into asmInst. -// It assumes that the Loc and AtPC fields of asmInst have already been filled. -func (a *AMD64) AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error { +func amd64AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error { return x86AsmDecode(asmInst, mem, regs, memrw, bi, 64) } -func (a *AMD64) Prologues() []opcodeSeq { - return prologuesAMD64 -} - // Possible stacksplit prologues are inserted by stacksplit in // $GOROOT/src/cmd/internal/obj/x86/obj6.go. // The stacksplit prologue will always begin with loading curg in CX, this diff --git a/pkg/proc/arch.go b/pkg/proc/arch.go index 4ff7c64f..17e726a7 100644 --- a/pkg/proc/arch.go +++ b/pkg/proc/arch.go @@ -5,47 +5,87 @@ import ( "github.com/go-delve/delve/pkg/dwarf/op" ) -// Arch defines an interface for representing a -// CPU architecture. -type Arch interface { - // PtrSize returns the size of a pointer for the architecture. - PtrSize() int - // MaxInstructionLength is the maximum size in bytes of an instruction. - MaxInstructionLength() int - // AsmDecode decodes the assembly instruction starting at mem[0:] into asmInst. +// Arch represents a CPU architecture. +type Arch struct { + Name string // architecture name + + ptrSize int + maxInstructionLength int + prologues []opcodeSeq + breakpointInstruction []byte + breakInstrMovesPC bool + derefTLS bool + + // asmDecode decodes the assembly instruction starting at mem[0:] into asmInst. // It assumes that the Loc and AtPC fields of asmInst have already been filled. - AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error - // Prologues returns a list of stack split prologues - // that are inserted at function entry. - Prologues() []opcodeSeq - // BreakpointInstruction is the instruction that will trigger a breakpoint trap for - // the given architecture. - BreakpointInstruction() []byte - // BreakInstrMovesPC is true if hitting the breakpoint instruction advances the - // instruction counter by the size of the breakpoint instruction. - BreakInstrMovesPC() bool - // BreakpointSize is the size of the breakpoint instruction for the given architecture. - BreakpointSize() int - // DerefTLS is true if the G struct stored in the TLS section is a pointer - // and the address must be dereferenced to find to actual G struct. - DerefTLS() bool - // FixFrameUnwindContext applies architecture specific rules for unwinding a stack frame + asmDecode func(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error + // fixFrameUnwindContext applies architecture specific rules for unwinding a stack frame // on the given arch. - FixFrameUnwindContext(*frame.FrameContext, uint64, *BinaryInfo) *frame.FrameContext - // SwitchStack will use the current frame to determine if it's time to + fixFrameUnwindContext func(*frame.FrameContext, uint64, *BinaryInfo) *frame.FrameContext + // switchStack will use the current frame to determine if it's time to // switch between the system stack and the goroutine stack or vice versa. - SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool - // RegSize returns the size (in bytes) of register regnum. - RegSize(uint64) int + switchStack func(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool + // regSize returns the size (in bytes) of register regnum. + regSize func(uint64) int // RegistersToDwarfRegisters maps hardware registers to DWARF registers. - RegistersToDwarfRegisters(uint64, Registers) op.DwarfRegisters - // AddrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in + RegistersToDwarfRegisters func(uint64, Registers) op.DwarfRegisters + // addrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in // PC, SP, and BP registers in the format used by the DWARF expression interpreter. - AddrAndStackRegsToDwarfRegisters(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters + addrAndStackRegsToDwarfRegisters func(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters // DwarfRegisterToString returns the name and value representation of the given register. - DwarfRegisterToString(int, *op.DwarfRegister) (string, bool, string) - // InhibitStepInto returns whether StepBreakpoint can be set at pc. - InhibitStepInto(bi *BinaryInfo, pc uint64) bool + DwarfRegisterToString func(int, *op.DwarfRegister) (string, bool, string) + // inhibitStepInto returns whether StepBreakpoint can be set at pc. + inhibitStepInto func(bi *BinaryInfo, pc uint64) bool + + // 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 + + // sigreturnfn is the DIE of runtime.sigreturn, the return trampoline for + // the signal handler. See comment in FixFrameUnwindContext for a + // description of why this is needed. + sigreturnfn *Function +} + +// PtrSize returns the size of a pointer for the architecture. +func (a *Arch) PtrSize() int { + return a.ptrSize +} + +// MaxInstructionLength is the maximum size in bytes of an instruction. +func (a *Arch) MaxInstructionLength() int { + return a.maxInstructionLength +} + +// Prologues returns a list of stack split prologues +// that are inserted at function entry. +func (a *Arch) Prologues() []opcodeSeq { + return a.prologues +} + +// BreakpointInstruction is the instruction that will trigger a breakpoint trap for +// the given architecture. +func (a *Arch) BreakpointInstruction() []byte { + return a.breakpointInstruction +} + +// BreakInstrMovesPC is true if hitting the breakpoint instruction advances the +// instruction counter by the size of the breakpoint instruction. +func (a *Arch) BreakInstrMovesPC() bool { + return a.breakInstrMovesPC +} + +// BreakpointSize is the size of the breakpoint instruction for the given architecture. +func (a *Arch) BreakpointSize() int { + return len(a.breakpointInstruction) +} + +// DerefTLS is true if the G struct stored in the TLS section is a pointer +// and the address must be dereferenced to find to actual G struct. +func (a *Arch) DerefTLS() bool { + return a.derefTLS } // crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_amd64.s. diff --git a/pkg/proc/arm64_arch.go b/pkg/proc/arm64_arch.go index 63b36a9e..62085973 100644 --- a/pkg/proc/arm64_arch.go +++ b/pkg/proc/arm64_arch.go @@ -12,23 +12,6 @@ import ( "golang.org/x/arch/arm64/arm64asm" ) -// ARM64 represents the ARM64 CPU architecture. -type ARM64 struct { - gStructOffset uint64 - 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 - - // sigreturnfn is the DIE of runtime.sigreturn, the return trampoline for - // the signal handler. See comment in FixFrameUnwindContext for a - // description of why this is needed. - sigreturnfn *Function -} - const ( arm64DwarfIPRegNum uint64 = 32 arm64DwarfSPRegNum uint64 = 31 @@ -40,50 +23,28 @@ var arm64BreakInstruction = []byte{0x0, 0x0, 0x20, 0xd4} // ARM64Arch returns an initialized ARM64 // struct. -func ARM64Arch(goos string) *ARM64 { - return &ARM64{ - goos: goos, +func ARM64Arch(goos string) *Arch { + return &Arch{ + Name: "arm64", + ptrSize: 8, + maxInstructionLength: 4, + breakpointInstruction: arm64BreakInstruction, + breakInstrMovesPC: false, + derefTLS: false, + prologues: prologuesARM64, + fixFrameUnwindContext: arm64FixFrameUnwindContext, + switchStack: arm64SwitchStack, + regSize: arm64RegSize, + RegistersToDwarfRegisters: arm64RegistersToDwarfRegisters, + addrAndStackRegsToDwarfRegisters: arm64AddrAndStackRegsToDwarfRegisters, + DwarfRegisterToString: arm64DwarfRegisterToString, + inhibitStepInto: func(*BinaryInfo, uint64) bool { return false }, + asmDecode: arm64AsmDecode, } } -// PtrSize returns the size of a pointer -// on this architecture. -func (a *ARM64) PtrSize() int { - return 8 -} - -// MaxInstructionLength returns the maximum length of an instruction. -func (a *ARM64) MaxInstructionLength() int { - return 4 -} - -// BreakpointInstruction returns the Breakpoint -// instruction for this architecture. -func (a *ARM64) BreakpointInstruction() []byte { - return arm64BreakInstruction -} - -// BreakInstrMovesPC returns whether the -// breakpoint instruction will change the value -// of PC after being executed -func (a *ARM64) BreakInstrMovesPC() bool { - return false -} - -// BreakpointSize returns the size of the -// breakpoint instruction on this architecture. -func (a *ARM64) BreakpointSize() int { - return len(arm64BreakInstruction) -} - -// Always return false for now. -func (a *ARM64) DerefTLS() bool { - return false -} - -// FixFrameUnwindContext adds default architecture rules to fctxt or returns -// the default frame unwind context if fctxt is nil. -func (a *ARM64) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext { +func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext { + a := bi.Arch if a.sigreturnfn == nil { a.sigreturnfn = bi.LookupFunc["runtime.sigreturn"] } @@ -138,7 +99,7 @@ func (a *ARM64) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi * if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End { rule := fctxt.CFA if rule.Offset == crosscall2SPOffsetBad { - switch a.goos { + switch bi.GOOS { case "windows": rule.Offset += crosscall2SPOffsetWindows default: @@ -174,7 +135,7 @@ const arm64cgocallSPOffsetSaveSlot = 0x8 const prevG0schedSPOffsetSaveSlot = 0x10 const spAlign = 16 -func (a *ARM64) SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool { +func arm64SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool { if it.frame.Current.Fn != nil { switch it.frame.Current.Fn.Name { case "runtime.asmcgocall", "runtime.cgocallback_gofunc", "runtime.sigpanic": @@ -270,7 +231,7 @@ func (a *ARM64) SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) return false } -func (a *ARM64) RegSize(regnum uint64) int { +func arm64RegSize(regnum uint64) int { // fp registers if regnum >= 64 && regnum <= 95 { return 16 @@ -361,9 +322,7 @@ func maxArm64DwarfRegister() int { return max } -// RegistersToDwarfRegisters converts hardware registers to the format used -// by the DWARF expression interpreter. -func (a *ARM64) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { +func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { dregs := make([]*op.DwarfRegister, maxArm64DwarfRegister()+1) dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC()) @@ -391,9 +350,7 @@ func (a *ARM64) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op. } } -// AddrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in -// PC, SP, and BP registers in the format used by the DWARF expression interpreter. -func (a *ARM64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { +func arm64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { dregs := make([]*op.DwarfRegister, arm64DwarfIPRegNum+1) dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) dregs[arm64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) @@ -411,7 +368,7 @@ func (a *ARM64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint } } -func (a *ARM64) DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { +func arm64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { // see arm64DwarfToHardware table for explanation switch { case i <= 30: @@ -465,9 +422,3 @@ func (a *ARM64) DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string } return name, false, fmt.Sprintf("%#x", reg.Bytes) } - -// InhibitStepInto returns whether StepBreakpoint can be set at pc. -// Always return false on arm64. -func (a *ARM64) InhibitStepInto(bi *BinaryInfo, pc uint64) bool { - return false -} diff --git a/pkg/proc/arm64_disasm.go b/pkg/proc/arm64_disasm.go index 2f3ba529..156931d7 100644 --- a/pkg/proc/arm64_disasm.go +++ b/pkg/proc/arm64_disasm.go @@ -8,9 +8,7 @@ import ( "golang.org/x/arch/arm64/arm64asm" ) -// AsmDecode decodes the assembly instruction starting at mem[0:] into asmInst. -// It assumes that the Loc and AtPC fields of asmInst have already been filled. -func (a *ARM64) AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error { +func arm64AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error { asmInst.Size = 4 asmInst.Bytes = mem[:asmInst.Size] @@ -35,10 +33,6 @@ func (a *ARM64) AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, m return nil } -func (a *ARM64) Prologues() []opcodeSeq { - return prologuesARM64 -} - func resolveCallArgARM64(inst *arm64asm.Inst, instAddr uint64, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location { if inst.Op != arm64asm.BL && inst.Op != arm64asm.BLR { return nil diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index 14a82004..b8f89489 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -43,7 +43,7 @@ const ( // includes both the executable and also any loaded libraries). type BinaryInfo struct { // Architecture of this binary. - Arch Arch + Arch *Arch // GOOS operating system this binary is executing on. GOOS string diff --git a/pkg/proc/core/core.go b/pkg/proc/core/core.go index 18b8cbe0..7da7c981 100644 --- a/pkg/proc/core/core.go +++ b/pkg/proc/core/core.go @@ -313,11 +313,6 @@ func (t *Thread) RestoreRegisters(proc.Registers) error { return ErrChangeRegisterCore } -// Arch returns the architecture the target is built for and executing on. -func (t *Thread) Arch() proc.Arch { - return t.p.bi.Arch -} - // BinInfo returns information about the binary. func (t *Thread) BinInfo() *proc.BinaryInfo { return t.p.bi diff --git a/pkg/proc/core/core_test.go b/pkg/proc/core/core_test.go index 97859694..df4b08d0 100644 --- a/pkg/proc/core/core_test.go +++ b/pkg/proc/core/core_test.go @@ -185,7 +185,7 @@ func withCoreFile(t *testing.T, name, args string) *proc.Target { return p } -func logRegisters(t *testing.T, regs proc.Registers, arch proc.Arch) { +func logRegisters(t *testing.T, regs proc.Registers, arch *proc.Arch) { dregs := arch.RegistersToDwarfRegisters(0, regs) for i, reg := range dregs.Regs { if reg == nil { diff --git a/pkg/proc/disasm.go b/pkg/proc/disasm.go index b5a795b6..1f5f59f1 100644 --- a/pkg/proc/disasm.go +++ b/pkg/proc/disasm.go @@ -139,7 +139,7 @@ func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *Breakpoint inst.Breakpoint = atbp inst.AtPC = (regs != nil) && (curpc == pc) - bi.Arch.AsmDecode(&inst, mem, regs, memrw, bi) + bi.Arch.asmDecode(&inst, mem, regs, memrw, bi) r = append(r, inst) diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go index 85b651a6..d2270ab8 100644 --- a/pkg/proc/gdbserial/gdbserver.go +++ b/pkg/proc/gdbserial/gdbserver.go @@ -1303,11 +1303,6 @@ func (t *Thread) RestoreRegisters(savedRegs proc.Registers) error { return t.writeRegisters() } -// Arch will return the CPU architecture for the target. -func (t *Thread) Arch() proc.Arch { - return t.p.bi.Arch -} - // BinInfo will return information on the binary being debugged. func (t *Thread) BinInfo() *proc.BinaryInfo { return t.p.bi diff --git a/pkg/proc/i386_arch.go b/pkg/proc/i386_arch.go index c48342d7..dcb6950e 100644 --- a/pkg/proc/i386_arch.go +++ b/pkg/proc/i386_arch.go @@ -8,23 +8,6 @@ import ( "strings" ) -// I386 represents the Intel386 CPU architecture. -type I386 struct { - gStructOffset uint64 - 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 - - // sigreturnfn is the DIE of runtime.sigreturn, the return trampoline for - // the signal handler. See comment in FixFrameUnwindContext for a - // description of why this is needed. - sigreturnfn *Function -} - const ( i386DwarfIPRegNum uint64 = 8 i386DwarfSPRegNum uint64 = 4 @@ -35,50 +18,28 @@ var i386BreakInstruction = []byte{0xCC} // I386Arch returns an initialized I386Arch // struct. -func I386Arch(goos string) *I386 { - return &I386{ - goos: goos, +func I386Arch(goos string) *Arch { + return &Arch{ + Name: "386", + ptrSize: 4, + maxInstructionLength: 15, + breakpointInstruction: i386BreakInstruction, + breakInstrMovesPC: true, + derefTLS: false, + prologues: prologuesI386, + fixFrameUnwindContext: i386FixFrameUnwindContext, + switchStack: i386SwitchStack, + regSize: i386RegSize, + RegistersToDwarfRegisters: i386RegistersToDwarfRegisters, + addrAndStackRegsToDwarfRegisters: i386AddrAndStackRegsToDwarfRegisters, + DwarfRegisterToString: i386DwarfRegisterToString, + inhibitStepInto: i386InhibitStepInto, + asmDecode: i386AsmDecode, } } -// PtrSize returns the size of a pointer -// on this architecture. -func (i *I386) PtrSize() int { - return 4 -} - -// MaxInstructionLength returns the maximum length of an instruction. -func (i *I386) MaxInstructionLength() int { - return 15 -} - -// BreakpointInstruction returns the Breakpoint -// instruction for this architecture. -func (i *I386) BreakpointInstruction() []byte { - return i386BreakInstruction -} - -// BreakInstrMovesPC returns whether the -// breakpoint instruction will change the value -// of PC after being executed -func (i *I386) BreakInstrMovesPC() bool { - return true -} - -// BreakpointSize returns the size of the -// breakpoint instruction on this architecture. -func (i *I386) BreakpointSize() int { - return len(i386BreakInstruction) -} - -// TODO, Not sure, always return false for now. Need to test on windows. -func (i *I386) DerefTLS() bool { - return false -} - -// FixFrameUnwindContext adds default architecture rules to fctxt or returns -// the default frame unwind context if fctxt is nil. -func (i *I386) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext { +func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext { + i := bi.Arch if i.sigreturnfn == nil { i.sigreturnfn = bi.LookupFunc["runtime.sigreturn"] } @@ -153,9 +114,7 @@ func (i *I386) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *B } // SwitchStack will use the current frame to determine if it's time to -// switch between the system stack and the goroutine stack or vice versa. -// Sets it.atend when the top of the stack is reached. -func (i *I386) SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool { +func i386SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool { if it.frame.Current.Fn == nil { return false } @@ -214,7 +173,7 @@ func (i *I386) SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool { // in the System V ABI Intel386 Architecture Processor Supplement page 25, // table 2.14 // https://www.uclibc.org/docs/psABI-i386.pdf -func (i *I386) RegSize(regnum uint64) int { +func i386RegSize(regnum uint64) int { // XMM registers if regnum >= 21 && regnum <= 36 { return 16 @@ -293,7 +252,7 @@ func maxI386DwarfRegister() int { return max } -func (i *I386) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { +func i386RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { dregs := make([]*op.DwarfRegister, maxI386DwarfRegister()+1) for _, reg := range regs.Slice(true) { @@ -312,9 +271,7 @@ func (i *I386) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.D } } -// AddrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in -// PC, SP, and BP registers in the format used by the DWARF expression interpreter. -func (i *I386) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { +func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { dregs := make([]*op.DwarfRegister, i386DwarfIPRegNum+1) dregs[i386DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) dregs[i386DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) @@ -330,7 +287,7 @@ func (i *I386) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint6 } } -func (i *I386) DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { +func i386DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { name, ok := i386DwarfToName[j] if !ok { name = fmt.Sprintf("unknown%d", j) @@ -356,11 +313,11 @@ func (i *I386) DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, } } -// InhibitStepInto returns whether StepBreakpoint can be set at pc. +// i386InhibitStepInto returns whether StepBreakpoint can be set at pc. // When cgo or pie on 386 linux, compiler will insert more instructions (ex: call __x86.get_pc_thunk.). // StepBreakpoint shouldn't be set on __x86.get_pc_thunk and skip it. // See comments on stacksplit in $GOROOT/src/cmd/internal/obj/x86/obj6.go for generated instructions details. -func (i *I386) InhibitStepInto(bi *BinaryInfo, pc uint64) bool { +func i386InhibitStepInto(bi *BinaryInfo, pc uint64) bool { if bi.SymNames != nil && bi.SymNames[pc] != nil && strings.HasPrefix(bi.SymNames[pc].Name, "__x86.get_pc_thunk.") { return true diff --git a/pkg/proc/i386_disasm.go b/pkg/proc/i386_disasm.go index 00ea32fa..6803375d 100644 --- a/pkg/proc/i386_disasm.go +++ b/pkg/proc/i386_disasm.go @@ -8,18 +8,10 @@ import ( "golang.org/x/arch/x86/x86asm" ) -// AsmDecode decodes the assembly instruction starting at mem[0:] into asmInst. -// It assumes that the Loc and AtPC fields of asmInst have already been filled. -func (i *I386) AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error { +func i386AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error { return x86AsmDecode(asmInst, mem, regs, memrw, bi, 32) } -// Prologues returns a list of stack split prologues -// that are inserted at function entry. -func (i *I386) Prologues() []opcodeSeq { - return prologuesI386 -} - // Possible stacksplit prologues are inserted by stacksplit in // $GOROOT/src/cmd/internal/obj/x86/obj6.go. // If 386 on linux when pie, the stacksplit prologue beigin with `call __x86.get_pc_thunk.` sometime. diff --git a/pkg/proc/native/threads.go b/pkg/proc/native/threads.go index aca7fcfe..352e2d5a 100644 --- a/pkg/proc/native/threads.go +++ b/pkg/proc/native/threads.go @@ -94,12 +94,6 @@ func (t *Thread) Location() (*proc.Location, error) { return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil } -// Arch returns the architecture the binary is -// compiled for and executing on. -func (t *Thread) Arch() proc.Arch { - return t.dbp.bi.Arch -} - // BinInfo returns information on the binary. func (t *Thread) BinInfo() *proc.BinaryInfo { return t.dbp.bi @@ -124,7 +118,7 @@ func (t *Thread) SetCurrentBreakpoint(adjustPC bool) error { // of PC after being executed we should look for breakpoints // with bp.Addr == PC and there is no need to call SetPC // after finding one. - adjustPC = adjustPC && t.Arch().BreakInstrMovesPC() + adjustPC = adjustPC && t.BinInfo().Arch.BreakInstrMovesPC() if bp, ok := t.dbp.FindBreakpoint(pc, adjustPC); ok { if adjustPC { diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index 44709e28..27be3c76 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -127,7 +127,7 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) { so := g.variable.bi.PCToImage(g.PC) return newStackIterator( bi, g.variable.mem, - bi.Arch.AddrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP, g.LR), + bi.Arch.addrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP, g.LR), g.stackhi, stkbar, g.stkbarPos, g, opts), nil } @@ -246,7 +246,7 @@ func (it *stackIterator) Next() bool { } if it.opts&StacktraceSimple == 0 { - if it.bi.Arch.SwitchStack(it, &callFrameRegs) { + if it.bi.Arch.switchStack(it, &callFrameRegs) { return true } } @@ -268,7 +268,7 @@ func (it *stackIterator) switchToGoroutineStack() { it.pc = it.g.PC it.regs.Reg(it.regs.SPRegNum).Uint64Val = it.g.SP it.regs.AddReg(it.regs.BPRegNum, op.DwarfRegisterFromUint64(it.g.BP)) - if _, ok := it.bi.Arch.(*ARM64); ok { + if it.bi.Arch.Name == "arm64" { it.regs.Reg(it.regs.LRRegNum).Uint64Val = it.g.LR } } @@ -414,9 +414,9 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin fde, err := it.bi.frameEntries.FDEForPC(it.pc) var framectx *frame.FrameContext if _, nofde := err.(*frame.ErrNoFDEForPC); nofde { - framectx = it.bi.Arch.FixFrameUnwindContext(nil, it.pc, it.bi) + framectx = it.bi.Arch.fixFrameUnwindContext(nil, it.pc, it.bi) } else { - framectx = it.bi.Arch.FixFrameUnwindContext(fde.EstablishFrame(it.pc), it.pc, it.bi) + framectx = it.bi.Arch.fixFrameUnwindContext(fde.EstablishFrame(it.pc), it.pc, it.bi) } cfareg, err := it.executeFrameRegRule(0, framectx.CFA, 0) @@ -455,7 +455,7 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin } } - if _, ok := it.bi.Arch.(*ARM64); ok { + if it.bi.Arch.Name == "arm64" { if ret == 0 && it.regs.Regs[it.regs.LRRegNum] != nil { ret = it.regs.Regs[it.regs.LRRegNum].Uint64Val } @@ -515,7 +515,7 @@ func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, c } func (it *stackIterator) readRegisterAt(regnum uint64, addr uint64) (*op.DwarfRegister, error) { - buf := make([]byte, it.bi.Arch.RegSize(regnum)) + buf := make([]byte, it.bi.Arch.regSize(regnum)) _, err := it.mem.ReadMemory(buf, uintptr(addr)) if err != nil { return nil, err diff --git a/pkg/proc/target.go b/pkg/proc/target.go index a871b00e..d728ad4c 100644 --- a/pkg/proc/target.go +++ b/pkg/proc/target.go @@ -152,8 +152,7 @@ func (t *Target) SupportsFunctionCalls() bool { if ok, _ := t.Process.Recorded(); ok { return false } - _, ok := t.Process.BinInfo().Arch.(*AMD64) - return ok + return t.Process.BinInfo().Arch.Name == "amd64" } // ClearAllGCache clears the internal Goroutine cache. diff --git a/pkg/proc/target_exec.go b/pkg/proc/target_exec.go index 1ee879c7..67231798 100644 --- a/pkg/proc/target_exec.go +++ b/pkg/proc/target_exec.go @@ -770,7 +770,7 @@ func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) er pc := instr.DestLoc.PC // Skip InhibitStepInto functions for different arch. - if dbp.BinInfo().Arch.InhibitStepInto(dbp.BinInfo(), pc) { + if dbp.BinInfo().Arch.inhibitStepInto(dbp.BinInfo(), pc) { return nil } diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index 4422835c..1d2068f7 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -23,7 +23,6 @@ type Thread interface { // RestoreRegisters restores saved registers RestoreRegisters(Registers) error - Arch() Arch BinInfo() *BinaryInfo StepInstruction() error // Blocked returns true if the thread is blocked diff --git a/pkg/proc/types.go b/pkg/proc/types.go index 023dd0b8..84457d1d 100644 --- a/pkg/proc/types.go +++ b/pkg/proc/types.go @@ -46,7 +46,7 @@ type runtimeTypeDIE struct { kind int64 } -func pointerTo(typ godwarf.Type, arch Arch) godwarf.Type { +func pointerTo(typ godwarf.Type, arch *Arch) godwarf.Type { return &godwarf.PtrType{ CommonType: godwarf.CommonType{ ByteSize: int64(arch.PtrSize()), diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 073306b0..114739a7 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -432,7 +432,7 @@ func getGVariable(thread Thread) (*Variable, error) { } } - return newGVariable(thread, uintptr(gaddr), thread.Arch().DerefTLS()) + return newGVariable(thread, uintptr(gaddr), thread.BinInfo().Arch.DerefTLS()) } func newGVariable(thread Thread, gaddr uintptr, deref bool) (*Variable, error) { @@ -446,7 +446,7 @@ func newGVariable(thread Thread, gaddr uintptr, deref bool) (*Variable, error) { if deref { typ = &godwarf.PtrType{ CommonType: godwarf.CommonType{ - ByteSize: int64(thread.Arch().PtrSize()), + ByteSize: int64(thread.BinInfo().Arch.PtrSize()), Name: "", ReflectKind: reflect.Ptr, Offset: 0, @@ -1305,7 +1305,7 @@ func convertToEface(srcv, dstv *Variable) error { return dstv.writeEmptyInterface(typeAddr, srcv) } -func readStringInfo(mem MemoryReadWriter, arch Arch, addr uintptr) (uintptr, int64, error) { +func readStringInfo(mem MemoryReadWriter, arch *Arch, addr uintptr) (uintptr, int64, error) { // string data structure is always two ptrs in size. Addr, followed by len // http://research.swtch.com/godata diff --git a/service/api/conversions.go b/service/api/conversions.go index 8756500c..0c0a205b 100644 --- a/service/api/conversions.go +++ b/service/api/conversions.go @@ -329,7 +329,7 @@ func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig { } // ConvertRegisters converts proc.Register to api.Register for a slice. -func ConvertRegisters(in op.DwarfRegisters, arch proc.Arch, floatingPoint bool) (out []Register) { +func ConvertRegisters(in op.DwarfRegisters, arch *proc.Arch, floatingPoint bool) (out []Register) { out = make([]Register, 0, len(in.Regs)) for i := range in.Regs { reg := in.Reg(uint64(i))