implemented binding for MCDisassembler.getInstruction

also added new llvm.mc module to act as higher level
python access to the MC section of LLVM (added Instr
and Disassembler classes).
This commit is contained in:
anthony cantor 2013-08-14 00:53:37 -06:00 committed by Siu Kwan Lam
commit 818c9289fc
5 changed files with 176 additions and 13 deletions

View file

@ -3,24 +3,112 @@ import contextlib
import llvm
from llvmpy import api
from llvmpy.api.llvm import MCDisassembler
class Disassembler(llvm.Wrapper):
class Instr:
def __init__(self, mcinst):
self.mcinst = mcinst
if not self.mcinst:
raise llvm.LLVMException("null MCInst argument")
def __repr__(self):
return repr(self.mcinst)
def __len__(self):
return int(self.mcinst.size())
def operands(self):
amt = self.mcinst.getNumOperands()
if amt < 1:
return []
l = []
for i in range(0, amt):
l.append(self.mcinst.getOperand(i))
return l
class BadInstr(Instr):
pass
class Disassembler:
def __init__(self, mcdisasm):
self.mcdisasm = mcdisasm
if not self.mcdisasm:
raise llvm.LLVMException("null MCDisassembler argument")
def __repr__(self):
return repr(self.mcdisasm)
@staticmethod
def new(triple='', cpu='', features=''):
def new_from_target(target, subtargetinfo):
return Disassembler(target.createMCDisassembler(subtargetinfo))
@staticmethod
def new_from_triple(triple='', cpu='', features=''):
if not triple:
triple = api.llvm.sys.getDefaultTargetTriple()
print repr(triple)
with contextlib.closing(BytesIO()) as error:
target = api.llvm.TargetRegistry.lookupTarget(triple, error)
if not target:
raise llvm.LLVMException(error)
raise llvm.LLVMException(error.read())
if not target.hasMCDisassembler():
raise llvm.LLVMException(target, "No disassembler provided for %s." % triple)
sti = target.createMCSubtargetInfo(triple, cpu, features)
if not sti:
raise llvm.LLVMException("Could not create sub target info")
sti = target.createMCSubtargetInfo(triple, cpu, features)
if not sti:
raise llvm.LLVMException("Could not create sub target info")
return target.createMCDisassembler(sti)
return Disassembler.new_from_target(target)
@staticmethod
def new_from_name(name):
name = name.strip()
for target in api.llvm.TargetRegistry.targetsList():
if name == target.getName():
sti = target.createMCSubtargetInfo(name, '', '')
return Disassembler.new_from_target(target, sti)
raise llvm.LLVMException("failed to find target with name %s" % name)
@staticmethod
def x86():
return Disassembler.new_from_name('x86')
@staticmethod
def x86_64():
return Disassembler.new_from_name('x86-64')
@staticmethod
def arm():
return Disassembler.new_from_name('arm')
@staticmethod
def thumb():
return Disassembler.new_from_name('thumb')
#decode some bytes into instructions. yields each instruction
#as it is decoded.
def decode(self, bs):
code = api.llvm.BytesMemoryObject.new(bs)
idx = code.getBase()
while(idx < code.getExtent()):
inst = api.llvm.MCInst.new()
status, size = self.mcdisasm.getInstruction(inst, code, idx)
if status == MCDisassembler.DecodeStatus.Fail:
yield (idx, None)
elif status == MCDisassembler.DecodeStatus.SoftFail:
yield (idx, BadInstr(inst))
else:
yield (idx, Instr(inst))
if size <= 1:
idx += 1
else:
idx += size

View file

@ -30,7 +30,7 @@
#include <llvm/PassRegistry.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/MemoryObject.h>
#include <llvm/MC/MCDisassembler.h>
#include <llvm/ExecutionEngine/MCJIT.h> // to make MCJIT working
#include "auto_pyobject.h"
@ -976,6 +976,22 @@ PyObject* TargetRegistry_targets_list()
"llvm::Target", "llvm::Target");
}
static
PyObject* MCDisassembler_getInstruction(llvm::MCDisassembler *disasm,
llvm::MCInst &instr,
const llvm::MemoryObject &region,
uint64_t address
)
{
uint64_t size;
llvm::MCDisassembler::DecodeStatus status;
size = 0;
status = disasm->getInstruction(instr, size, region, address,
llvm::nulls(), llvm::nulls());
return Py_BuildValue("(i,i)", int(status), size);
}
static
PyObject* llvm_sys_getHostCPUFeatures(PyObject* Features)
{

View file

@ -1,14 +1,42 @@
from binding import *
from ..namespace import llvm
from ..BytesMemoryObject import MemoryObject
from ..Support.raw_ostream import raw_ostream
MCSubtargetInfo = llvm.Class()
MCDisassembler = llvm.Class()
MCInst = llvm.Class()
MCOperand = llvm.Class()
@MCSubtargetInfo
class MCSubtargetInfo:
pass
@MCOperand
class MCOperand:
pass
@MCInst
class MCInst:
_include_ = "llvm/MC/MCInst.h"
new = Constructor()
size = Method(cast(Size_t, int))
getNumOperands = Method(cast(Unsigned, int))
getOperand = Method(const(ref(MCOperand)), cast(int, Unsigned))
@MCDisassembler
class MCDisassembler:
pass
_include_ = "llvm/MC/MCDisassembler.h"
DecodeStatus = Enum('Fail', 'SoftFail', 'Success')
getInstruction = CustomMethod('MCDisassembler_getInstruction',
PyObjectPtr,
ref(MCInst),
ref(MemoryObject),
cast(int, Uint64)
)

View file

@ -46,10 +46,10 @@ class Target:
).require_only(4)
createMCSubtargetInfo = Method(ptr(MCSubtargetInfo),
cast(str, StringRef), #triple
cast(str, StringRef), #cpu
cast(str, StringRef) #features
)
cast(str, StringRef), #triple
cast(str, StringRef), #cpu
cast(str, StringRef) #features
)
createMCDisassembler = Method(ptr(MCDisassembler), ref(MCSubtargetInfo))

View file

@ -0,0 +1,31 @@
import llvm
from llvm import mc
from llvm.mc import Disassembler
from llvmpy import api
llvm.initialize_all_target_components()
def print_instructions(dasm, bs):
for (offset, inst) in dasm.decode(bs):
if inst is None:
print("\t%r=>(bad): 0, []" % (offset))
elif isinstance(inst, mc.BadInstr):
print("\t%r=>(bad)%r: %r, %r" % (offset, inst, len(inst), inst.operands()))
else:
print("\t%r=>%r: %r, %r" % (offset, inst, len(inst), inst.operands()))
print("x86:")
print_instructions(Disassembler.x86(), "\x01\xc3\xc3\xcc\x90")
print("x86-64:")
print_instructions(Disassembler.x86_64(), "\x55\x48\x89\xe8")
#print("arm:")
#code = "\xe9\x2d\x40\x08\xe5\x9f\x00\x0c\xe5\x9f\x10\x0c" + \
# "\xe5\x9f\x20\x0c\xe5\x9f\x30\x0c\xeb\xff\xff\xf6"
#print_instructions(Disassembler.arm(), code)