forked from go-delve/delve
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
delve: support linux-arm64 native debug(go-delve#118)
* delve now can be built to arm64-arch and running on linux-arm64 OS. * arm64 general-purpose registers have completed. * arm64 disasm has completed. Co-authored-by: tykcd996 <tang.yuke@zte.com.cn> Co-authored-by: hengwu0 <wu.heng@zte.com.cn>
- Loading branch information
1 parent
ba07d6e
commit 625e2a6
Showing
30 changed files
with
12,226 additions
and
390 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,10 @@ os: | |
- linux | ||
- osx | ||
|
||
arch: | ||
- amd64 | ||
- arm64 | ||
|
||
go: | ||
- tip | ||
- 1.13.x | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,299 @@ | ||
package proc | ||
|
||
import ( | ||
"encoding/binary" | ||
|
||
"github.com/go-delve/delve/pkg/dwarf/frame" | ||
"github.com/go-delve/delve/pkg/dwarf/op" | ||
"golang.org/x/arch/x86/x86asm" | ||
) | ||
|
||
// 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 | ||
amd64DwarfBPRegNum uint64 = 6 | ||
) | ||
|
||
var amd64BreakInstruction = []byte{0xCC} | ||
|
||
// AMD64Arch returns an initialized AMD64 | ||
// struct. | ||
func AMD64Arch(goos string) *AMD64 { | ||
return &AMD64{ | ||
goos: goos, | ||
} | ||
} | ||
|
||
// PtrSize returns the size of a pointer | ||
// on this architecture. | ||
func (a *AMD64) PtrSize() int { | ||
return 8 | ||
} | ||
|
||
// MinInstructionLength returns the min lenth | ||
// of the instruction | ||
func (a *AMD64) MinInstructionLength() int { | ||
return 1 | ||
} | ||
|
||
// BreakpointInstruction returns the Breakpoint | ||
// instruction for this architecture. | ||
func (a *AMD64) BreakpointInstruction() []byte { | ||
return amd64BreakInstruction | ||
} | ||
|
||
// 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 { | ||
if a.sigreturnfn == nil { | ||
a.sigreturnfn = bi.LookupFunc["runtime.sigreturn"] | ||
} | ||
|
||
if fctxt == nil || (a.sigreturnfn != nil && pc >= a.sigreturnfn.Entry && pc < a.sigreturnfn.End) { | ||
// When there's no frame descriptor entry use BP (the frame pointer) instead | ||
// - return register is [bp + a.PtrSize()] (i.e. [cfa-a.PtrSize()]) | ||
// - cfa is bp + a.PtrSize()*2 | ||
// - bp is [bp] (i.e. [cfa-a.PtrSize()*2]) | ||
// - sp is cfa | ||
|
||
// When the signal handler runs it will move the execution to the signal | ||
// handling stack (installed using the sigaltstack system call). | ||
// This isn't a proper stack switch: the pointer to g in TLS will still | ||
// refer to whatever g was executing on that thread before the signal was | ||
// received. | ||
// Since go did not execute a stack switch the previous value of sp, pc | ||
// and bp is not saved inside g.sched, as it normally would. | ||
// The only way to recover is to either read sp/pc from the signal context | ||
// parameter (the ucontext_t* parameter) or to unconditionally follow the | ||
// frame pointer when we get to runtime.sigreturn (which is what we do | ||
// here). | ||
|
||
return &frame.FrameContext{ | ||
RetAddrReg: amd64DwarfIPRegNum, | ||
Regs: map[uint64]frame.DWRule{ | ||
amd64DwarfIPRegNum: frame.DWRule{ | ||
Rule: frame.RuleOffset, | ||
Offset: int64(-a.PtrSize()), | ||
}, | ||
amd64DwarfBPRegNum: frame.DWRule{ | ||
Rule: frame.RuleOffset, | ||
Offset: int64(-2 * a.PtrSize()), | ||
}, | ||
amd64DwarfSPRegNum: frame.DWRule{ | ||
Rule: frame.RuleValOffset, | ||
Offset: 0, | ||
}, | ||
}, | ||
CFA: frame.DWRule{ | ||
Rule: frame.RuleCFA, | ||
Reg: amd64DwarfBPRegNum, | ||
Offset: int64(2 * a.PtrSize()), | ||
}, | ||
} | ||
} | ||
|
||
if a.crosscall2fn == nil { | ||
a.crosscall2fn = bi.LookupFunc["crosscall2"] | ||
} | ||
|
||
if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End { | ||
rule := fctxt.CFA | ||
if rule.Offset == crosscall2SPOffsetBad { | ||
switch a.goos { | ||
case "windows": | ||
rule.Offset += crosscall2SPOffsetWindows | ||
default: | ||
rule.Offset += crosscall2SPOffsetNonWindows | ||
} | ||
} | ||
fctxt.CFA = rule | ||
} | ||
|
||
// We assume that RBP is the frame pointer and we want to keep it updated, | ||
// so that we can use it to unwind the stack even when we encounter frames | ||
// without descriptor entries. | ||
// If there isn't a rule already we emit one. | ||
if fctxt.Regs[amd64DwarfBPRegNum].Rule == frame.RuleUndefined { | ||
fctxt.Regs[amd64DwarfBPRegNum] = frame.DWRule{ | ||
Rule: frame.RuleFramePointer, | ||
Reg: amd64DwarfBPRegNum, | ||
Offset: 0, | ||
} | ||
} | ||
|
||
return fctxt | ||
} | ||
|
||
// RegSize returns the size (in bytes) of register regnum. | ||
// The mapping between hardware registers and DWARF registers is specified | ||
// in the System V ABI AMD64 Architecture Processor Supplement page 57, | ||
// figure 3.36 | ||
// https://www.uclibc.org/docs/psABI-x86_64.pdf | ||
func (a *AMD64) RegSize(regnum uint64) int { | ||
// XMM registers | ||
if regnum > amd64DwarfIPRegNum && regnum <= 32 { | ||
return 16 | ||
} | ||
// x87 registers | ||
if regnum >= 33 && regnum <= 40 { | ||
return 10 | ||
} | ||
return 8 | ||
} | ||
|
||
// The mapping between hardware registers and DWARF registers is specified | ||
// in the System V ABI AMD64 Architecture Processor Supplement page 57, | ||
// figure 3.36 | ||
// https://www.uclibc.org/docs/psABI-x86_64.pdf | ||
|
||
var amd64DwarfToHardware = map[int]x86asm.Reg{ | ||
0: x86asm.RAX, | ||
1: x86asm.RDX, | ||
2: x86asm.RCX, | ||
3: x86asm.RBX, | ||
4: x86asm.RSI, | ||
5: x86asm.RDI, | ||
8: x86asm.R8, | ||
9: x86asm.R9, | ||
10: x86asm.R10, | ||
11: x86asm.R11, | ||
12: x86asm.R12, | ||
13: x86asm.R13, | ||
14: x86asm.R14, | ||
15: x86asm.R15, | ||
} | ||
|
||
var amd64DwarfToName = map[int]string{ | ||
17: "XMM0", | ||
18: "XMM1", | ||
19: "XMM2", | ||
20: "XMM3", | ||
21: "XMM4", | ||
22: "XMM5", | ||
23: "XMM6", | ||
24: "XMM7", | ||
25: "XMM8", | ||
26: "XMM9", | ||
27: "XMM10", | ||
28: "XMM11", | ||
29: "XMM12", | ||
30: "XMM13", | ||
31: "XMM14", | ||
32: "XMM15", | ||
33: "ST(0)", | ||
34: "ST(1)", | ||
35: "ST(2)", | ||
36: "ST(3)", | ||
37: "ST(4)", | ||
38: "ST(5)", | ||
39: "ST(6)", | ||
40: "ST(7)", | ||
49: "Eflags", | ||
50: "Es", | ||
51: "Cs", | ||
52: "Ss", | ||
53: "Ds", | ||
54: "Fs", | ||
55: "Gs", | ||
58: "Fs_base", | ||
59: "Gs_base", | ||
64: "MXCSR", | ||
65: "CW", | ||
66: "SW", | ||
} | ||
|
||
func maxAmd64DwarfRegister() int { | ||
max := int(amd64DwarfIPRegNum) | ||
for i := range amd64DwarfToHardware { | ||
if i > max { | ||
max = i | ||
} | ||
} | ||
for i := range amd64DwarfToName { | ||
if i > max { | ||
max = i | ||
} | ||
} | ||
return max | ||
} | ||
|
||
// RegistersToDwarfRegisters converts hardware registers to the format used | ||
// by the DWARF expression interpreter. | ||
func (a *AMD64) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { | ||
dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1) | ||
|
||
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC()) | ||
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP()) | ||
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP()) | ||
|
||
for dwarfReg, asmReg := range amd64DwarfToHardware { | ||
v, err := regs.Get(int(asmReg)) | ||
if err == nil { | ||
dregs[dwarfReg] = op.DwarfRegisterFromUint64(v) | ||
} | ||
} | ||
|
||
for _, reg := range regs.Slice(true) { | ||
for dwarfReg, regName := range amd64DwarfToName { | ||
if regName == reg.Name { | ||
dregs[dwarfReg] = op.DwarfRegisterFromBytes(reg.Bytes) | ||
} | ||
} | ||
} | ||
|
||
return op.DwarfRegisters{ | ||
StaticBase: staticBase, | ||
Regs: dregs, | ||
ByteOrder: binary.LittleEndian, | ||
PCRegNum: amd64DwarfIPRegNum, | ||
SPRegNum: amd64DwarfSPRegNum, | ||
BPRegNum: amd64DwarfBPRegNum, | ||
} | ||
} | ||
|
||
// 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 uint64) op.DwarfRegisters { | ||
dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1) | ||
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) | ||
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) | ||
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) | ||
|
||
return op.DwarfRegisters{ | ||
StaticBase: staticBase, | ||
Regs: dregs, | ||
ByteOrder: binary.LittleEndian, | ||
PCRegNum: amd64DwarfIPRegNum, | ||
SPRegNum: amd64DwarfSPRegNum, | ||
BPRegNum: amd64DwarfBPRegNum, | ||
} | ||
} |
Oops, something went wrong.