proc: convert Arch into a struct (#1972)
Replace the interface type Arch with a struct with the same functionality.
This commit is contained in:
parent
85c34e47ee
commit
223e0a57ca
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
102
pkg/proc/arch.go
102
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
|
||||
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 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 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 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 func(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters
|
||||
// DwarfRegisterToString returns the name and value representation of the given register.
|
||||
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.
|
||||
Prologues() []opcodeSeq
|
||||
func (a *Arch) Prologues() []opcodeSeq {
|
||||
return a.prologues
|
||||
}
|
||||
|
||||
// BreakpointInstruction is the instruction that will trigger a breakpoint trap for
|
||||
// the given architecture.
|
||||
BreakpointInstruction() []byte
|
||||
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.
|
||||
BreakInstrMovesPC() bool
|
||||
func (a *Arch) BreakInstrMovesPC() bool {
|
||||
return a.breakInstrMovesPC
|
||||
}
|
||||
|
||||
// BreakpointSize is the size of the breakpoint instruction for the given architecture.
|
||||
BreakpointSize() int
|
||||
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.
|
||||
DerefTLS() bool
|
||||
// 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
|
||||
// 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
|
||||
// RegistersToDwarfRegisters maps hardware registers to DWARF registers.
|
||||
RegistersToDwarfRegisters(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
|
||||
// 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
|
||||
func (a *Arch) DerefTLS() bool {
|
||||
return a.derefTLS
|
||||
}
|
||||
|
||||
// crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_amd64.s.
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()),
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user