114 lines
2.3 KiB
Go
114 lines
2.3 KiB
Go
package proc
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"rsc.io/x86/x86asm"
|
|
)
|
|
|
|
type ArchInst x86asm.Inst
|
|
|
|
func asmDecode(mem []byte, pc uint64) (*ArchInst, error) {
|
|
inst, err := x86asm.Decode(mem, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
patchPCRel(pc, &inst)
|
|
r := ArchInst(inst)
|
|
return &r, nil
|
|
}
|
|
|
|
func (inst *ArchInst) Size() int {
|
|
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))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
|
|
if inst.Inst == nil {
|
|
return "?"
|
|
}
|
|
|
|
var text string
|
|
|
|
switch flavour {
|
|
case GNUFlavour:
|
|
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst))
|
|
case IntelFlavour:
|
|
fallthrough
|
|
default:
|
|
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst))
|
|
}
|
|
|
|
if inst.IsCall() && inst.DestLoc != nil && inst.DestLoc.Fn != nil {
|
|
text += " " + inst.DestLoc.Fn.Name
|
|
}
|
|
|
|
return text
|
|
}
|
|
|
|
func (inst *AsmInstruction) IsCall() bool {
|
|
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
|
|
}
|
|
|
|
func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers) *Location {
|
|
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
|
|
}
|
|
regs, err := thread.Registers()
|
|
if err != nil {
|
|
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?
|
|
pcbytes, err := thread.readMemory(addr, inst.MemBytes)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
pc = binary.LittleEndian.Uint64(pcbytes)
|
|
default:
|
|
return nil
|
|
}
|
|
|
|
file, line, fn := thread.dbp.PCToLine(pc)
|
|
if fn == nil {
|
|
return nil
|
|
}
|
|
return &Location{PC: pc, File: file, Line: line, Fn: fn}
|
|
}
|