From 818c9289fc93bc030e44bd268c1e5e65c3c1256a Mon Sep 17 00:00:00 2001 From: anthony cantor Date: Wed, 14 Aug 2013 00:53:37 -0600 Subject: [PATCH] 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). --- llvm/mc/__init__.py | 102 +++++++++++++++++++++++++-- llvmpy/include/llvm_binding/extra.h | 18 ++++- llvmpy/src/MC/__init__.py | 30 +++++++- llvmpy/src/Support/TargetRegistry.py | 8 +-- test/example-disassemble.py | 31 ++++++++ 5 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 test/example-disassemble.py diff --git a/llvm/mc/__init__.py b/llvm/mc/__init__.py index 237d5c9..9219bf5 100644 --- a/llvm/mc/__init__.py +++ b/llvm/mc/__init__.py @@ -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 diff --git a/llvmpy/include/llvm_binding/extra.h b/llvmpy/include/llvm_binding/extra.h index 6d8b863..7911b21 100644 --- a/llvmpy/include/llvm_binding/extra.h +++ b/llvmpy/include/llvm_binding/extra.h @@ -30,7 +30,7 @@ #include #include #include - +#include #include // 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 ®ion, + 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) { diff --git a/llvmpy/src/MC/__init__.py b/llvmpy/src/MC/__init__.py index 0c7b4c9..276f4f9 100644 --- a/llvmpy/src/MC/__init__.py +++ b/llvmpy/src/MC/__init__.py @@ -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) + + +) \ No newline at end of file diff --git a/llvmpy/src/Support/TargetRegistry.py b/llvmpy/src/Support/TargetRegistry.py index 55bc8b8..be89289 100644 --- a/llvmpy/src/Support/TargetRegistry.py +++ b/llvmpy/src/Support/TargetRegistry.py @@ -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)) diff --git a/test/example-disassemble.py b/test/example-disassemble.py new file mode 100644 index 0000000..3bf7230 --- /dev/null +++ b/test/example-disassemble.py @@ -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) + + + +