1
0
mirror of https://github.com/beego/bee.git synced 2024-11-15 03:20:54 +00:00
bee/vendor/github.com/derekparker/delve/pkg/proc/disasm_amd64.go

190 lines
4.8 KiB
Go
Raw Normal View History

2017-03-19 22:45:54 +00:00
package proc
import (
"encoding/binary"
2018-10-13 13:45:53 +00:00
"golang.org/x/arch/x86/x86asm"
2017-03-19 22:45:54 +00:00
)
var maxInstructionLength uint64 = 15
2018-10-13 13:45:53 +00:00
type archInst x86asm.Inst
2017-03-19 22:45:54 +00:00
2018-10-13 13:45:53 +00:00
func asmDecode(mem []byte, pc uint64) (*archInst, error) {
2017-03-19 22:45:54 +00:00
inst, err := x86asm.Decode(mem, 64)
if err != nil {
return nil, err
}
patchPCRel(pc, &inst)
2018-10-13 13:45:53 +00:00
r := archInst(inst)
2017-03-19 22:45:54 +00:00
return &r, nil
}
2018-10-13 13:45:53 +00:00
func (inst *archInst) Size() int {
2017-03-19 22:45:54 +00:00
return inst.Len
}
// converts PC relative arguments to absolute addresses
func patchPCRel(pc uint64, inst *x86asm.Inst) {
for i := range inst.Args {
rel, isrel := inst.Args[i].(x86asm.Rel)
if isrel {
inst.Args[i] = x86asm.Imm(int64(pc) + int64(rel) + int64(inst.Len))
}
}
}
2018-10-13 13:45:53 +00:00
// Text will return the assembly instructions in human readable format according to
// the flavour specified.
func (inst *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string {
2017-03-19 22:45:54 +00:00
if inst.Inst == nil {
return "?"
}
var text string
switch flavour {
case GNUFlavour:
2018-10-13 13:45:53 +00:00
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
case GoFlavour:
text = x86asm.GoSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
2017-03-19 22:45:54 +00:00
case IntelFlavour:
fallthrough
default:
2018-10-13 13:45:53 +00:00
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
2017-03-19 22:45:54 +00:00
}
return text
}
2018-10-13 13:45:53 +00:00
// IsCall returns true if the instruction is a CALL or LCALL instruction.
2017-03-19 22:45:54 +00:00
func (inst *AsmInstruction) IsCall() bool {
2018-10-13 13:45:53 +00:00
if inst.Inst == nil {
return false
}
2017-03-19 22:45:54 +00:00
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
}
2018-10-13 13:45:53 +00:00
func resolveCallArg(inst *archInst, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
2017-03-19 22:45:54 +00:00
if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
return nil
}
var pc uint64
var err error
switch arg := inst.Args[0].(type) {
case x86asm.Imm:
pc = uint64(arg)
case x86asm.Reg:
if !currentGoroutine || regs == nil {
return nil
}
pc, err = regs.Get(int(arg))
if err != nil {
return nil
}
case x86asm.Mem:
if !currentGoroutine || regs == nil {
return nil
}
if arg.Segment != 0 {
return nil
}
base, err1 := regs.Get(int(arg.Base))
index, err2 := regs.Get(int(arg.Index))
if err1 != nil || err2 != nil {
return nil
}
addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
//TODO: should this always be 64 bits instead of inst.MemBytes?
2018-10-13 13:45:53 +00:00
pcbytes := make([]byte, inst.MemBytes)
_, err := mem.ReadMemory(pcbytes, addr)
2017-03-19 22:45:54 +00:00
if err != nil {
return nil
}
pc = binary.LittleEndian.Uint64(pcbytes)
default:
return nil
}
2018-10-13 13:45:53 +00:00
file, line, fn := bininfo.PCToLine(pc)
2017-03-19 22:45:54 +00:00
if fn == nil {
return nil
}
return &Location{PC: pc, File: file, Line: line, Fn: fn}
}
type instrseq []x86asm.Op
// 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
// instruction is added by load_g_cx in the same file and is either 1 or 2
// MOVs.
var prologues []instrseq
func init() {
var tinyStacksplit = instrseq{x86asm.CMP, x86asm.JBE}
var smallStacksplit = instrseq{x86asm.LEA, x86asm.CMP, x86asm.JBE}
var bigStacksplit = instrseq{x86asm.MOV, x86asm.CMP, x86asm.JE, x86asm.LEA, x86asm.SUB, x86asm.CMP, x86asm.JBE}
var unixGetG = instrseq{x86asm.MOV}
var windowsGetG = instrseq{x86asm.MOV, x86asm.MOV}
prologues = make([]instrseq, 0, 2*3)
for _, getG := range []instrseq{unixGetG, windowsGetG} {
for _, stacksplit := range []instrseq{tinyStacksplit, smallStacksplit, bigStacksplit} {
prologue := make(instrseq, 0, len(getG)+len(stacksplit))
prologue = append(prologue, getG...)
prologue = append(prologue, stacksplit...)
prologues = append(prologues, prologue)
}
}
}
2018-10-13 13:45:53 +00:00
// firstPCAfterPrologueDisassembly returns the address of the first
// instruction after the prologue for function fn by disassembling fn and
// matching the instructions against known split-stack prologue patterns.
// If sameline is set firstPCAfterPrologueDisassembly will always return an
// address associated with the same line as fn.Entry
func firstPCAfterPrologueDisassembly(p Process, fn *Function, sameline bool) (uint64, error) {
var mem MemoryReadWriter = p.CurrentThread()
breakpoints := p.Breakpoints()
bi := p.BinInfo()
text, err := disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End, false)
2017-03-19 22:45:54 +00:00
if err != nil {
return fn.Entry, err
}
if len(text) <= 0 {
return fn.Entry, nil
}
for _, prologue := range prologues {
if len(prologue) >= len(text) {
continue
}
if checkPrologue(text, prologue) {
r := &text[len(prologue)]
if sameline {
if r.Loc.Line != text[0].Loc.Line {
return fn.Entry, nil
}
}
return r.Loc.PC, nil
}
}
return fn.Entry, nil
}
func checkPrologue(s []AsmInstruction, prologuePattern instrseq) bool {
line := s[0].Loc.Line
for i, op := range prologuePattern {
if s[i].Inst.Op != op || s[i].Loc.Line != line {
return false
}
}
return true
}