diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..fd5be7b --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,22 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.10" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: doc/conf.py + +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: requirements.txt diff --git a/README.rst b/README.rst index f06bc4a..f9318af 100644 --- a/README.rst +++ b/README.rst @@ -2,14 +2,10 @@ Amoco ===== -.. image:: https://travis-ci.org/bdcht/amoco.svg?branch=release - :target: https://travis-ci.org/bdcht/amoco - .. image:: http://readthedocs.org/projects/amoco/badge/?version=latest :target: http://amoco.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status - +-----------+--------------------------------------------------+ | Status: | Under Development | +-----------+--------------------------------------------------+ @@ -56,7 +52,7 @@ User documentation and API can be found at `http://amoco.readthedocs.io/en/latest/index.html` .. image:: https://github.com/bdcht/amoco/blob/release/doc/gui_load.png - :width: 100% + :width: 800 Todo ==== @@ -92,6 +88,13 @@ Please see `LICENSE`_. Changelog ========= +- `v2.9.9`_ + + * add ppc32 (e200) architecture + * add support for COFF executable format + * add parser for gdb trace files + * improve structs subpackage + - `v2.9.8`_ * update to PySide6 (Qt6) @@ -394,6 +397,7 @@ Changelog .. _sqlalchemy: http://www.sqlalchemy.org .. _QDarkStyleSheet: https://github.com/ColinDuquesnoy/QDarkStyleSheet .. _LICENSE: https://github.com/bdcht/amoco/blob/release/LICENSE +.. _v2.9.9: https://github.com/bdcht/amoco/releases/tag/v2.9.9 .. _v2.9.8: https://github.com/bdcht/amoco/releases/tag/v2.9.8 .. _v2.9.7: https://github.com/bdcht/amoco/releases/tag/v2.9.7 .. _v2.9.6: https://github.com/bdcht/amoco/releases/tag/v2.9.6 diff --git a/amoco/arch/ppc32/__init__.py b/amoco/arch/ppc32/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/amoco/arch/ppc32/asm.py b/amoco/arch/ppc32/asm.py new file mode 100644 index 0000000..5158c5e --- /dev/null +++ b/amoco/arch/ppc32/asm.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2022 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +from .env import * + +from amoco.cas.utils import * + +# ------------------------------------------------------------------------------ +# helpers and decorators : +def _push_(fmap, _x): + fmap[sp] = fmap[sp] - _x.length + fmap[mem(sp, _x.size)] = _x + + +def _pop_(fmap, _l): + fmap[_l] = fmap(mem(sp, _l.size)) + fmap[sp] = fmap[sp] + _l.length + + +def __npc(i_xxx): + def npc(ins, fmap): + fmap[pc] = fmap(pc) + ins.length + i_xxx(ins, fmap) + + return npc + + +def trap(ins, fmap, trapname): + fmap.internals["trap"] = trapname + + diff --git a/amoco/arch/ppc32/cpu.py b/amoco/arch/ppc32/cpu.py new file mode 100644 index 0000000..ca977d9 --- /dev/null +++ b/amoco/arch/ppc32/cpu.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from amoco.arch.ppc32.asm import * + +# expose "microarchitecture" (instructions semantics) +uarch = dict(filter(lambda kv: kv[0].startswith("i_"), locals().items())) + +# import specifications: +from amoco.arch.core import instruction, disassembler + +instruction_ppc32 = type("instruction_ppc32", (instruction,), {}) +instruction_ppc32.set_uarch(uarch) + +from amoco.arch.ppc32.formats import PPC_full + +instruction_ppc32.set_formatter(PPC_full) + +# define disassembler: +from amoco.arch.ppc32 import spec_booke as spec + +endian = lambda: -1 + +disassemble = disassembler([spec], iclass=instruction_ppc32,endian=endian) + + +def PC(): + return pc + +def get_data_endian(): + return -1 # BE diff --git a/amoco/arch/ppc32/cpu_e200.py b/amoco/arch/ppc32/cpu_e200.py new file mode 100644 index 0000000..75b9a97 --- /dev/null +++ b/amoco/arch/ppc32/cpu_e200.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from amoco.arch.ppc32.e200.asm import * + +# expose "microarchitecture" (instructions semantics) +uarch = dict(filter(lambda kv: kv[0].startswith("i_"), locals().items())) + +# import specifications: +from amoco.arch.core import instruction, disassembler + +instruction_e200 = type("instruction_e200", (instruction,), {}) +instruction_e200.set_uarch(uarch) + +from amoco.arch.ppc32.e200.formats import PPC_full + +instruction_e200.set_formatter(PPC_full) + +# define disassembler: +from amoco.arch.ppc32.e200 import spec_vle + +endian = lambda: -1 + +disassemble = disassembler([spec_vle], iclass=instruction_e200,endian=endian) + + +def PC(): + return pc + +def get_data_endian(): + return -1 # BE diff --git a/amoco/arch/ppc32/e200/__init__.py b/amoco/arch/ppc32/e200/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/amoco/arch/ppc32/e200/asm.py b/amoco/arch/ppc32/e200/asm.py new file mode 100644 index 0000000..cf2ebc3 --- /dev/null +++ b/amoco/arch/ppc32/e200/asm.py @@ -0,0 +1,499 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2020 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +from .env import * + +from amoco.cas.utils import * + +# ------------------------------------------------------------------------------ +# helpers and decorators : + +def __npc(i_xxx): + def pcnpc(ins, fmap): + fmap[pc] = fmap[npc] + fmap[npc] = fmap[npc] + ins.length + i_xxx(ins, fmap) + return pcnpc + +# i_xxx is the translation of MIPS-R3000 instruction xxx. +# ------------------------------------------------------------------------------ + +@__npc +def i_ADD(ins, fmap): + dst, src1, src2 = ins.operands + _v = fmap(src1+src2) + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + + +@__npc +def i_SUB(ins, fmap): + dst, src1, src2 = ins.operands + _v = fmap(src1-src2) + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + +@__npc +def i_SUBU(ins, fmap): + dst, src1, src2 = ins.operands + _v = fmap(src1-src2) + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + +@__npc +def i_AND(ins, fmap): + dst, src1, src2 = ins.operands + _v = fmap(src1&src2) + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + +@__npc +def i_OR(ins, fmap): + dst, src1, src2 = ins.operands + _v = fmap(src1|src2) + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + +@__npc +def i_NOR(ins, fmap): + dst, src1, src2 = ins.operands + _v = fmap(~(src1|src2)) + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + +@__npc +def i_XOR(ins, fmap): + dst, src1, src2 = ins.operands + _v = fmap(src1^src2) + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + +i_ADDI = i_ADDIU = i_ADDU = i_ADD +i_SUBU = i_SUB +i_ANDI = i_AND +i_ORI = i_OR +i_XORI = i_XOR + +def i_J(ins,fmap): + t = ins.operands[0] + target = composer([cst(0,2),t,fmap(pc)[28:32]]) + _v = fmap(npc) + fmap.update_delayed() + fmap[pc] = _v + fmap[npc] = target + +def i_JAL(ins,fmap): + t = ins.operands[0] + target = composer([cst(0,2),t,fmap(pc)[28:32]]) + _v = fmap(npc) + fmap.update_delayed() + fmap[pc] = _v + fmap[ra] = _v+4 + fmap[npc] = target + +@__npc +def i_JALR(ins,fmap): + rd, rs = ins.operands + _v = fmap(npc) + target = fmap(rs) + fmap.update_delayed() + if rd is not zero: + fmap[rd] = _v + fmap[npc] = target + +@__npc +def i_JR(ins,fmap): + rs = ins.operands[0] + target = fmap(rs) + fmap.update_delayed() + fmap[npc] = target + +@__npc +def i_BEQ(ins,fmap): + rs, rt, offset = ins.operands + cond = (fmap(rs)==fmap(rt)) + fmap.update_delayed() + fmap[npc] = tst(cond,fmap(npc-4)+offset,fmap(npc)) + +@__npc +def i_BNE(ins,fmap): + rs, rt, offset = ins.operands + cond = (fmap(rs)!=fmap(rt)) + fmap.update_delayed() + fmap[npc] = tst(cond,fmap(npc-4)+offset,fmap(npc)) + +@__npc +def i_BGEZ(ins,fmap): + rs, offset = ins.operands + cond = fmap(rs.bit(31))==bit0 + fmap.update_delayed() + fmap[npc] = tst(cond,fmap(npc-4)+offset,fmap(npc)) + +@__npc +def i_BGEZAL(ins,fmap): + rs, offset = ins.operands + cond = fmap(rs.bit(31))==bit0 + fmap.update_delayed() + fmap[ra] = fmap(npc) + fmap[npc] = tst(cond,fmap(npc-4)+offset,fmap(npc)) + +@__npc +def i_BGTZ(ins,fmap): + rs, offset = ins.operands + cond = fmap( (rs.bit(31)==bit0) & (rs!=zero) ) + fmap.update_delayed() + fmap[npc] = tst(cond,fmap(npc-4)+offset,fmap(npc)) + +@__npc +def i_BLEZ(ins,fmap): + rs, offset = ins.operands + cond = fmap( (rs.bit(31)==bit1) | (rs==zero) ) + fmap.update_delayed() + fmap[npc] = tst(cond,fmap(npc-4)+offset,fmap(npc)) + +@__npc +def i_BLTZ(ins,fmap): + rs, offset = ins.operands + cond = fmap(rs.bit(31))==bit1 + fmap.update_delayed() + fmap[npc] = tst(cond,fmap(npc-4)+offset,fmap(npc)) + +@__npc +def i_BLTZAL(ins,fmap): + rs, offset = ins.operands + cond = fmap(rs.bit(31))==bit1 + fmap.update_delayed() + fmap[ra] = fmap(npc) + fmap[npc] = tst(cond,fmap(npc-4)+offset,fmap(npc)) + +@__npc +def i_BREAK(ins,fmap): + ext("BREAK").call(fmap,code=ins.code) + +@__npc +def i_CFC(ins, fmap): + rt, rd = ins.operands + ext("CFC%d"%(ins.z)).call(fmap,rt=rt,rd=rd) + +@__npc +def i_MFC(ins, fmap): + rt, rd = ins.operands + ext("MFC%d"%(ins.z)).call(fmap,rt=rt,rd=rd) + +@__npc +def i_COP(ins, fmap): + fun = ins.cofun + ext("COP%d"%(ins.z)).call(fmap,cofun=fun) + +@__npc +def i_CTC(ins, fmap): + rt, rd = ins.operands + ext("CTC%d"%(ins.z)).call(fmap,rd=rd,rt=rt) + +@__npc +def i_MTC(ins, fmap): + rt, rd = ins.operands + ext("MTC%d"%(ins.z)).call(fmap,rd=rd,rt=rt) + +@__npc +def i_LWC(ins, fmap): + rt, base, offset = ins.operands + data = mem(base+offset,32) + ext("LWC%d"%(ins.z)).call(fmap,rt=rt,data=data) + +@__npc +def i_SWC(ins, fmap): + rt, base, offset = ins.operands + addr = fmap(base+offset) + ext("SWC%d"%(ins.z)).call(fmap,rt=rt,addr=addr) + +@__npc +def i_DIV(ins, fmap): + rs, rt = ins.operands + if fmap(rt!=zero): + _v_hi = fmap(rs/rt) + _v_lo = fmap(rs%rt) + fmap.update_delayed() + fmap[HI] = _v_hi + fmap[LO] = _v_lo + else: + fmap.update_delayed() + fmap[HI] = top(32) + fmap[LO] = top(32) + +i_DIVU = i_DIV + +@__npc +def i_MULT(ins, fmap): + rs, rt = ins.operands + res = fmap(rs**rt) + fmap.update_delayed() + fmap[LO] = res[0:32] + fmap[HI] = res[32:64] + +i_MULTU = i_MULT + +@__npc +def i_LH(ins, fmap): + dst, base, src = ins.operands + addr = base+src + _v = fmap(mem(addr,16)).signextend(32) + fmap.update_delayed() + if dst is not zero: + fmap.delayed(dst, _v) + +@__npc +def i_LHU(ins, fmap): + dst, base, src = ins.operands + addr = base+src + _v = fmap(mem(addr,16)).zeroextend(32) + fmap.update_delayed() + if dst is not zero: + fmap.delayed(dst, _v) + +@__npc +def i_LUI(ins, fmap): + dst, src1 = ins.operands + _v = src1.zeroextend(32)<<16 + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + +@__npc +def i_LW(ins, fmap): + dst, base, src = ins.operands + addr = base+src + _v = fmap(mem(addr,32)) + fmap.update_delayed() + if dst is not zero: + fmap.delayed(dst, _v) + +@__npc +def i_LWL(ins, fmap): + dst, base, src = ins.operands + addr = base+src + _v_b3 = fmap(mem(addr,8)) + cond1 = (addr%4)!=0 + _v_b2 = fmap(tst(cond1,mem(addr-1,8),dst[16:24])) + addr = addr - 1 + cond2 = cond1 & ((addr%4)!=0) + _v_b1 = fmap(tst(cond2,mem(addr-1,8),dst[8:16])) + _v_b0 = fmap(dst[0:8]) + fmap.update_delayed() + if dst is not zero: + _v = comp(dst.size) + _v[24:32] = _v_b3 + _v[16:24] = _v_b2 + _v[ 8:16] = _v_b1 + _v[ 0: 8] = _v_b0 + fmap.delayed(dst, _v.simplify()) + +@__npc +def i_LWR(ins, fmap): + dst, base, src = ins.operands + addr = base+src + _v_b0 = fmap(mem(addr,8)) + addr = addr + 1 + cond1 = (addr%4)!=0 + _v_b1 = fmap(tst(cond1,mem(addr,8),dst[8:16])) + addr = addr + 1 + cond2 = cond1 & ((addr%4)!=0) + _v_b2 = fmap(tst(cond2,mem(addr,8),dst[16:24])) + _v_b3 = fmap(dst[24:32]) + fmap.update_delayed() + if dst is not zero: + _v = comp(dst.size) + _v[0 : 8] = _v_b0 + _v[8 :16] = _v_b1 + _v[16:24] = _v_b2 + _v[24:32] = _v_b3 + fmap.delayed(dst, _v.simplify()) + +@__npc +def i_SWL(ins, fmap): + src, base, off = ins.operands + addr = fmap(base+off) + val = fmap(src) + fmap.update_delayed() + fmap[mem(addr,8)] = val[24:32] + cond1 = (addr%4)!=0 + fmap[mem(addr-1,8)] = tst(cond1,val[16:24],fmap[mem(addr-1,8)]) + addr = addr - 1 + cond2 = cond1 & ((addr%4)!=0) + fmap[mem(addr-1,8)] = tst(cond2,val[8:16],fmap[mem(addr-1,8)]) + +@__npc +def i_SWR(ins, fmap): + src, base, off = ins.operands + addr = fmap(base+off) + val = fmap(src) + fmap.update_delayed() + fmap[mem(addr,8)] = val[0:8] + addr = addr + 1 + cond1 = (addr%4)!=0 + fmap[mem(addr,8)] = tst(cond1,val[8:16],fmap[mem(addr,8)]) + addr = addr + 1 + cond2 = cond1 & ((addr%4)!=0) + fmap[mem(addr,8)] = tst(cond2,val[16:24],fmap[mem(addr,8)]) + +@__npc +def i_LB(ins, fmap): + dst, base, src = ins.operands + addr = base+src + _v = fmap(mem(addr,8)).signextend(32) + fmap.update_delayed() + if dst is not zero: + fmap.delay(dst, _v) + +@__npc +def i_LBU(ins, fmap): + dst, base, src = ins.operands + addr = base+src + _v = fmap(mem(addr,8)).zeroextend(32) + fmap.update_delayed() + if dst is not zero: + fmap.delay(dst, _v) + +@__npc +def i_MFHI(ins,fmap): + dst = ins.operands[0] + _v = fmap(HI) + fmap.update_delayed() + fmap[dst] = _v + +@__npc +def i_MFLO(ins,fmap): + dst = ins.operands[0] + _v = fmap(LO) + fmap.update_delayed() + fmap[dst] = _v + +@__npc +def i_MTHI(ins,fmap): + src = ins.operands[0] + _v = fmap(src) + fmap.update_delayed() + fmap[HI] = _v + +@__npc +def i_MTLO(ins,fmap): + src = ins.operands[0] + _v = fmap(src) + fmap.update_delayed() + fmap[LO] = _v + +@__npc +def i_SB(ins, fmap): + src, base, off = ins.operands + addr = fmap(base+off) + fmap.update_delayed() + fmap[mem(addr,8)] = fmap(src[0:8]) + +@__npc +def i_SH(ins, fmap): + src, base, off = ins.operands + addr = fmap(base+off) + fmap.update_delayed() + fmap[mem(addr,16)] = fmap(src[0:16]) + +@__npc +def i_SW(ins, fmap): + src, base, off = ins.operands + addr = fmap(base+off) + fmap.update_delayed() + fmap[mem(addr,32)] = fmap(src) + +@__npc +def i_SLL(ins, fmap): + rt, rd, sa = ins.operands + res = fmap(rt<>src2).unsigned() + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + +@__npc +def i_SRLV(ins, fmap): + dst, src1, src2 = ins.operands + _v = fmap(src1>>src2).unsigned() + fmap.update_delayed() + if dst is not zero: + fmap[dst] = _v + +@__npc +def i_SYSCALL(ins, fmap): + ext("SYSCALL").call(fmap,code=ins.code) diff --git a/amoco/arch/ppc32/e200/env.py b/amoco/arch/ppc32/e200/env.py new file mode 100644 index 0000000..6f960dd --- /dev/null +++ b/amoco/arch/ppc32/e200/env.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2022 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +# import expressions: +from amoco.cas.expressions import * + +# registers : +# ----------- + +GPR = [reg("r%d"%i,32) for i in range(32)] + +XER = reg("XER", 32) +SO = slc(XER,31,1,"SO") +OV = slc(XER,30,1,"OV") +CA = slc(XER,29,1,"CA") +NoB = slc(XER,0,7,"NoB") + +FPR = [reg("f%d"%i,32) for i in range(32)] +FPSCR = reg("FPSCR", 32) + +CR = reg("CR",32) +MSR = reg("MSR",32) +CTR = reg("CTR",32) +LR = reg("lr",32) + +CR0 = slc(CR,28,4,"CR0") +CR1 = slc(CR,24,4,"CR1") +CR2 = slc(CR,20,4,"CR2") +CR3 = slc(CR,16,4,"CR3") +CR4 = slc(CR,12,4,"CR4") +CR5 = slc(CR, 8,4,"CR5") +CR6 = slc(CR, 4,4,"CR6") +CR7 = slc(CR, 0,4,"CR7") + +LT = slc(CR0,3,1,"LT") +GT = slc(CR0,2,1,"GT") +EQ = slc(CR0,1,1,"EQ") +SO_ = slc(CR0,0,1,"SO") + + +pc = reg("pc",32) +sp = reg("sp",32) + +is_reg_pc(pc) +is_reg_stack(sp) +is_reg_flags(CR0) + +R = GPR + +# list of registers used by emulator views: +registers = R+[SO,CR0,LR,pc] + diff --git a/amoco/arch/ppc32/e200/formats.py b/amoco/arch/ppc32/e200/formats.py new file mode 100644 index 0000000..eb396bd --- /dev/null +++ b/amoco/arch/ppc32/e200/formats.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +from .env import * +from amoco.cas.expressions import regtype +from amoco.arch.core import Formatter, Token + +def mnemo(i): + mn = i.mnemonic.lower() + return [(Token.Mnemonic, "{: <12}".format(mn))] + +def deref(base,off): + return "+%d(%s)" % (off, base) + +def opers(i): + s = [] + for op in i.operands: + if op._is_cst: + if i.misc["imm_ref"] is not None: + s.append((Token.Address, "%s" % (i.misc["imm_ref"]))) + elif op.sf: + s.append((Token.Constant, "%+d" % op.value)) + else: + s.append((Token.Constant, op.__str__())) + elif op._is_reg: + s.append((Token.Register, op.__str__())) + s.append((Token.Literal, ", ")) + if len(s) > 0: + s.pop() + return s + +def opers_mem(i): + s = [] + op = i.operands[0] + s.append((Token.Register, op.__str__())) + s.append((Token.Literal, ", ")) + s.append((Token.Memory,deref(*(i.operands[1:])))) + return s + +def opers_adr(i): + s = opers(i) + if i.misc["imm_ref"] is None and i.address is not None: + imm_ref = i.address + 4 + i.operands[-1] + s[-1] = (Token.Address, "%s" % (imm_ref)) + return s + +def opers_rel(i): + s = opers(i) + if i.misc["imm_ref"] is None and i.address is not None: + imm_ref = composer([cst(0,2),i.operands[-1],i.address[28:32]]) + s[-1] = (Token.Address, "%s" % (imm_ref)) + return s + + +format_default = (mnemo, opers) + +MIPS_full_formats = { + "mips1_loadstore": (mnemo, opers_mem), + "mips1_jump_abs": (mnemo, opers), + "mips1_jump_rel": (mnemo, opers_rel), + "mips1_branch": (mnemo, opers_adr), +} + +MIPS_full = Formatter(MIPS_full_formats) +MIPS_full.default = format_default + + +def MIPS_synthetic(null, i, toks=False): + s = MIPS_full(i, toks) + return MIPS_Synthetic_renaming(s, i) + + +def MIPS_Synthetic_renaming(s, i): + # TODO + return s diff --git a/amoco/arch/ppc32/e200/spec_e200.py b/amoco/arch/ppc32/e200/spec_e200.py new file mode 100644 index 0000000..837adca --- /dev/null +++ b/amoco/arch/ppc32/e200/spec_e200.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2022 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +from . import env + +# import generic PowerPC ISA (book E): + +from amoco.arch.ppc32.spec import * + diff --git a/amoco/arch/ppc32/env.py b/amoco/arch/ppc32/env.py new file mode 100644 index 0000000..e06915d --- /dev/null +++ b/amoco/arch/ppc32/env.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2022 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +# import expressions: +from amoco.cas.expressions import * + +# registers : +# ----------- + +# general purpose: +GPR = [reg("r%d"%i,32) for i in range(32)] + +# Integer Exception register: +XER = reg("XER", 32) +# XER flags: +SO = slc(XER,31,1,"SO") +OV = slc(XER,30,1,"OV") +CA = slc(XER,29,1,"CA") +NoB = slc(XER,0,7,"NoB") + +# floating-point registers: +FPR = [reg("f%d"%i,64) for i in range(32)] +FPSCR = reg("FPSCR", 32) + +CR = reg("CR",32) # control register +MSR = reg("MSR",32) +CTR = reg("CTR",32) # counter register +LR = reg("lr",32) # link register + +CR0 = slc(CR,28,4,"CR0") +CR1 = slc(CR,24,4,"CR1") +CR2 = slc(CR,20,4,"CR2") +CR3 = slc(CR,16,4,"CR3") +CR4 = slc(CR,12,4,"CR4") +CR5 = slc(CR, 8,4,"CR5") +CR6 = slc(CR, 4,4,"CR6") +CR7 = slc(CR, 0,4,"CR7") + +LT = slc(CR0,3,1,"LT") +GT = slc(CR0,2,1,"GT") +EQ = slc(CR0,1,1,"EQ") +SO_ = slc(CR0,0,1,"SO") + +PID = reg("PID",32) + +pc = reg("pc",32) +sp = reg("sp",32) + +is_reg_pc(pc) +is_reg_stack(sp) +is_reg_flags(CR0) + +R = GPR + +# list of registers used by emulator views: +registers = R+[SO,CR0,LR,pc] + +def DCREG(indx): + return reg("DCRN_%d"%indx,32) +def SPREG(indx): + return D_SPREG.get(indx,reg("SPREG#%d"%indx,32)) + +D_SPREG = { + 58: reg("CSRR0",32), + 59: reg("CSRR1",32), + 9: CTR, + 316: reg("DAC1",32), + 317: reg("DAC2",32), + 308: reg("DBCR0",32), + 309: reg("DBCR1",32), + 310: reg("DBCR2",32), + 304: reg("DBSR",32), + 61: reg("DEAR",32), + 22: reg("DEC",32), + 54: reg("DECAR",32), + 318: reg("DVC1",32), + 319: reg("DVC2",32), + 62: reg("ESR",32), + 63: reg("IVPR",32), + 312: reg("IAC1",32), + 313: reg("IAC2",32), + 314: reg("IAC3",32), + 315: reg("IAC4",32), + 400: reg("IVOR0",32), + 401: reg("IVOR1",32), + 402: reg("IVOR2",32), + 403: reg("IVOR3",32), + 404: reg("IVOR4",32), + 405: reg("IVOR5",32), + 406: reg("IVOR6",32), + 407: reg("IVOR7",32), + 408: reg("IVOR8",32), + 409: reg("IVOR9",32), + 410: reg("IVOR10",32), + 411: reg("IVOR11",32), + 412: reg("IVOR12",32), + 413: reg("IVOR13",32), + 414: reg("IVOR14",32), + 415: reg("IVOR15",32), + 8: LR, + 48: PID, + 286: reg("PIR",32), + 287: reg("PVR",32), + 1: XER, +} diff --git a/amoco/arch/ppc32/formats.py b/amoco/arch/ppc32/formats.py new file mode 100644 index 0000000..80a0681 --- /dev/null +++ b/amoco/arch/ppc32/formats.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +from .env import * +from amoco.cas.expressions import regtype +from amoco.arch.core import Formatter, Token + +def mnemo(i): + mn = i.mnemonic.lower() + return [(Token.Mnemonic, "{: <12}".format(mn))] + +def deref(base,off): + return "+%d(%s)" % (off, base) + +def opers(i): + s = [] + for op in i.operands: + if op._is_cst: + if i.misc["imm_ref"] is not None: + s.append((Token.Address, "%s" % (i.misc["imm_ref"]))) + elif op.sf: + s.append((Token.Constant, "%+d" % op.value)) + else: + s.append((Token.Constant, op.__str__())) + elif op._is_reg: + s.append((Token.Register, op.__str__())) + s.append((Token.Literal, ", ")) + if len(s) > 0: + s.pop() + return s + +def opers_mem(i): + s = [] + op = i.operands[0] + s.append((Token.Register, op.__str__())) + s.append((Token.Literal, ", ")) + s.append((Token.Memory,deref(*(i.operands[1:])))) + return s + +def opers_adr(i): + s = opers(i) + if i.misc["imm_ref"] is None and i.address is not None: + imm_ref = i.address + 4 + i.operands[-1] + s[-1] = (Token.Address, "%s" % (imm_ref)) + return s + +def opers_rel(i): + s = opers(i) + if i.misc["imm_ref"] is None and i.address is not None: + imm_ref = composer([cst(0,2),i.operands[-1],i.address[28:32]]) + s[-1] = (Token.Address, "%s" % (imm_ref)) + return s + + +format_default = (mnemo, opers) + +PPC_full_formats = { + "ppc_loadstore": (mnemo, opers_mem), + "ppc_jump_abs": (mnemo, opers), + "ppc_jump_rel": (mnemo, opers_rel), + "ppc_branch": (mnemo, opers_adr), +} + +PPC_full = Formatter(PPC_full_formats) +PPC_full.default = format_default + + diff --git a/amoco/arch/ppc32/spec_booke.py b/amoco/arch/ppc32/spec_booke.py new file mode 100644 index 0000000..46a0fdc --- /dev/null +++ b/amoco/arch/ppc32/spec_booke.py @@ -0,0 +1,662 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2022 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +from . import env + +from amoco.arch.core import * + +# ------------------------------------------------------- +# instruction PPC Book E decoders +# ref: +# ST RM0004 Reference manual +# Programmer's reference manual for Book E processors +# May 2015, DocID13694 Rev 2, 1025p. +# ------------------------------------------------------- + +ISPECS = [] + +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 100001010 .Rc ]", mnemonic="add") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 000001010 .Rc ]", mnemonic="addc") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 010001010 .Rc ]", mnemonic="adde") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 110001010 .Rc ]", mnemonic="adde64") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 111101011 .Rc ]", mnemonic="divw") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 111001011 .Rc ]", mnemonic="divwu") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 011101011 .Rc ]", mnemonic="mullw") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 000101000 .Rc ]", mnemonic="subf") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 000001000 .Rc ]", mnemonic="subfc") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 010001000 .Rc ]", mnemonic="subfe") +@ispec("32<[ 011111 rD(5) rA(5) rB(5) .OE 110001000 .Rc ]", mnemonic="subfe64") +def ppc_OE_Rc(obj,rD,rA,rB): + if obj.mnemonic.endswith("64") and obj.Rc==1: + raise InstructionError(obj) + obj.operands = [env.GPR[rD],env.GPR[rA],env.GPR[rB]] + obj.type = type_data_processing + +@ispec("32<[ 011111 rT(5) rA(5) rB(5) .OE 111101001 - ]", mnemonic="divd") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) .OE 111001001 - ]", mnemonic="divdu") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) .OE 011101001 - ]", mnemonic="mulld") +def ppc_OE(obj,rT,rA,rB): + obj.operands = [env.GPR[rT],env.GPR[rA],env.GPR[rB]] + obj.type = type_data_processing + +@ispec("32<[ 011111 rT(5) rA(5) rB(5) - 001001001 - ]", mnemonic="mulhd") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) - 000001001 - ]", mnemonic="mulhdu") +def ppc_mulhd(obj,rT,rA,rB): + obj.operands = [env.GPR[rT],env.GPR[rA],env.GPR[rB]] + obj.type = type_data_processing + +@ispec("32<[ 011111 rT(5) rA(5) rB(5) - 001001011 .Rc ]", mnemonic="mulhw") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) - 000001011 .Rc ]", mnemonic="mulhwu") +def ppc_Rc(obj,rT,rA,rB): + obj.operands = [env.GPR[rT],env.GPR[rA],env.GPR[rB]] + obj.type = type_data_processing + +@ispec("32<[ 100010=U rT(5) rA(5) D(16) ]", mnemonic="lbz") +@ispec("32<[ 100011=U rT(5) rA(5) D(16) ]", mnemonic="lbzu") +@ispec("32<[ 100010=U rT(5) rA(5) D(16) ]", mnemonic="lha") +@ispec("32<[ 100011=U rT(5) rA(5) D(16) ]", mnemonic="lhau") +@ispec("32<[ 101000=U rT(5) rA(5) D(16) ]", mnemonic="lhz") +@ispec("32<[ 101001=U rT(5) rA(5) D(16) ]", mnemonic="lhzu") +@ispec("32<[ 101110=U rT(5) rA(5) D(16) ]", mnemonic="lmw") +@ispec("32<[ 100000=U rT(5) rA(5) D(16) ]", mnemonic="lwz") +@ispec("32<[ 100001=U rT(5) rA(5) D(16) ]", mnemonic="lwzu") +@ispec("32<[ 100110=U rT(5) rA(5) D(16) ]", mnemonic="stb") +@ispec("32<[ 100111=U rT(5) rA(5) D(16) ]", mnemonic="stbu") +@ispec("32<[ 101100=U rT(5) rA(5) D(16) ]", mnemonic="sth") +@ispec("32<[ 101101=U rT(5) rA(5) D(16) ]", mnemonic="sthu") +@ispec("32<[ 100100=U rT(5) rA(5) D(16) ]", mnemonic="stw") +@ispec("32<[ 100101=U rT(5) rA(5) D(16) ]", mnemonic="stwu") +def ppc_load_store(obj,U,rT,rA,D): + if rA==0: + addr = env.cst(0,32) + else: + addr = env.GPR[rA] + if U==1: + obj.rA = rA + addr += env.cst(D,16).signextend(32) + obj.operands = [env.GPR[rT],env.ptr(addr)] + obj.type = type_data_processing + +@ispec("32<[ 110010=U rT(5) rA(5) D(16) ]", mnemonic="lfd") +@ispec("32<[ 110011=U rT(5) rA(5) D(16) ]", mnemonic="lfdu") +@ispec("32<[ 110000=U rT(5) rA(5) D(16) ]", mnemonic="lfs") +@ispec("32<[ 110001=U rT(5) rA(5) D(16) ]", mnemonic="lfsu") +@ispec("32<[ 110110=U rT(5) rA(5) D(16) ]", mnemonic="stfd") +@ispec("32<[ 110111=U rT(5) rA(5) D(16) ]", mnemonic="stfdu") +@ispec("32<[ 110100=U rT(5) rA(5) D(16) ]", mnemonic="stfs") +@ispec("32<[ 110101=U rT(5) rA(5) D(16) ]", mnemonic="stfsu") +@ispec("32<[ 101111=U rT(5) rA(5) D(16) ]", mnemonic="stmw") +def ppc_load_store(obj,U,rT,rA,D): + if rA==0: + addr = env.cst(0,32) + else: + addr = env.GPR[rA] + if U==1: + obj.rA = rA + addr += env.cst(D,16).signextend(32)<<2 + obj.operands = [env.FPR[rT],env.ptr(addr)] + obj.type = type_data_processing + +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 000 0=U ]", mnemonic="lbze") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 000 1=U ]", mnemonic="lbzue") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 010 0=U ]", mnemonic="lhae") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 010 1=U ]", mnemonic="lhaue") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 001 0=U ]", mnemonic="lhze") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 001 1=U ]", mnemonic="lhzue") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 011 0=U ]", mnemonic="lwze") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 011 1=U ]", mnemonic="lwzue") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 100 0=U ]", mnemonic="stbe") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 100 1=U ]", mnemonic="stbue") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 101 0=U ]", mnemonic="sthe") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 101 1=U ]", mnemonic="sthue") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 111 0=U ]", mnemonic="stwe") +@ispec("32<[ 111010 rT(5) rA(5) DE(12) 111 1=U ]", mnemonic="stwue") +def ppc_load_store(obj,rT,rA,DE,U): + if rA==0: + addr = env.cst(0,32) + else: + addr = env.GPR[rA] + if U==1: + obj.rA = rA + addr += env.cst(DE,12).signextend(32) + obj.operands = [env.GPR[rT],env.ptr(addr)] + obj.type = type_data_processing + +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 000 0=U ]", mnemonic="lde") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 000 1=U ]", mnemonic="ldue") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 100 0=U ]", mnemonic="stde") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 100 1=U ]", mnemonic="stdue") +def ppc_load_store(obj,rT,rA,DE,U): + if rA==0: + addr = env.cst(0,32) + else: + addr = env.GPR[rA] + if U==1: + obj.rA = rA + addr += env.cst(DE,12).signextend(32)<<2 + obj.operands = [env.GPR[rT],env.ptr(addr)] + obj.type = type_data_processing + +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 011 0=U ]", mnemonic="lfde") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 011 1=U ]", mnemonic="lfdue") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 010 0=U ]", mnemonic="lfse") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 010 1=U ]", mnemonic="lfsue") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 111 0=U ]", mnemonic="stfde") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 111 1=U ]", mnemonic="stfdue") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 110 0=U ]", mnemonic="stfse") +@ispec("32<[ 111110 rT(5) rA(5) DE(12) 110 1=U ]", mnemonic="stfsue") +def ppc_load_store(obj,rT,rA,DE,U): + if rA==0: + addr = env.cst(0,32) + else: + addr = env.GPR[rA] + if U==1: + obj.rA = rA + addr += env.cst(DE,12).signextend(32)<<2 + obj.operands = [env.FPR[rT],env.ptr(addr)] + obj.type = type_data_processing + +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0001 0=U 1 0 111 - ]", mnemonic="lbzx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0001 1=U 1 0 111 - ]", mnemonic="lbzux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0001 0=U 1 1 111 - ]", mnemonic="lbzxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0001 1=U 1 1 111 - ]", mnemonic="lbzuxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1100 0=U 1 0 111 - ]", mnemonic="ldx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1100 1=U 1 0 111 - ]", mnemonic="ldux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1100 0=U 1 1 111 - ]", mnemonic="ldxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1100 1=U 1 1 111 - ]", mnemonic="lduxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0101 0=U 1 0 111 - ]", mnemonic="lhax") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0101 1=U 1 0 111 - ]", mnemonic="lhaux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0101 0=U 1 1 111 - ]", mnemonic="lhaxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0101 1=U 1 1 111 - ]", mnemonic="lhauxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0100 0=U 1 0 111 - ]", mnemonic="lhzx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0100 1=U 1 0 111 - ]", mnemonic="lhzux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0100 0=U 1 1 111 - ]", mnemonic="lhzxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0100 1=U 1 1 111 - ]", mnemonic="lhzuxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0000 0=U 1 0 111 - ]", mnemonic="lwzx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0000 1=U 1 0 111 - ]", mnemonic="lwzux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0000 0=U 1 1 111 - ]", mnemonic="lwzxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0000 1=U 1 1 111 - ]", mnemonic="lwzuxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0011 0=U 1 0 111 - ]", mnemonic="stbx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0011 1=U 1 0 111 - ]", mnemonic="stbux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0011 0=U 1 1 111 - ]", mnemonic="stbxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0011 1=U 1 1 111 - ]", mnemonic="stbuxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1110 0=U 1 1 111 - ]", mnemonic="stdxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1110 1=U 1 1 111 - ]", mnemonic="stduxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0110 0=U 1 0 111 - ]", mnemonic="sthx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0110 1=U 1 0 111 - ]", mnemonic="sthux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0110 0=U 1 1 111 - ]", mnemonic="sthxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0110 1=U 1 1 111 - ]", mnemonic="sthuxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0010 0=U 1 0 111 - ]", mnemonic="stwx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0010 1=U 1 0 111 - ]", mnemonic="stwux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0010 0=U 1 1 111 - ]", mnemonic="stwxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 0010 1=U 1 1 111 - ]", mnemonic="stwuxe") +def ppc_load_store(obj,rT,rA,rB,U): + if rA==0: + addr = env.cst(0,32) + else: + addr = env.GPR[rA] + if U==1: + obj.rA = rA + addr += env.GPR[rB] + obj.operands = [env.GPR[rT],env.ptr(addr)] + obj.type = type_data_processing + +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1001 0=U 1 0 111 - ]", mnemonic="lfdx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1001 1=U 1 0 111 - ]", mnemonic="lfdux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1001 0=U 1 1 111 - ]", mnemonic="lfdxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1001 1=U 1 1 111 - ]", mnemonic="lfduxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1000 0=U 1 0 111 - ]", mnemonic="lfsx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1000 1=U 1 0 111 - ]", mnemonic="lfsux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1000 0=U 1 1 111 - ]", mnemonic="lfsxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1000 1=U 1 1 111 - ]", mnemonic="lfsuxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1011 0=U 1 0 111 - ]", mnemonic="stfdx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1011 1=U 1 0 111 - ]", mnemonic="stfdux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1011 0=U 1 1 111 - ]", mnemonic="stfdxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1011 1=U 1 1 111 - ]", mnemonic="stfduxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1111 0=U 1 0 111 - ]", mnemonic="stfiwx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1111 0=U 1 1 111 - ]", mnemonic="stfiwxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1010 0=U 1 0 111 - ]", mnemonic="stfsx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1010 1=U 1 0 111 - ]", mnemonic="stfsux") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1010 0=U 1 1 111 - ]", mnemonic="stfsxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 1010 1=U 1 1 111 - ]", mnemonic="stfsuxe") +def ppc_load(obj,rT,rA,rB,U): + if rA==0: + addr = env.cst(0,32) + else: + addr = env.GPR[rA] + if U==1: + obj.rA = rA + addr += env.GPR[rB] + obj.operands = [env.FPR[rT],env.ptr(addr)] + obj.type = type_data_processing + +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 011101 1=E 111 - ]", mnemonic="ldarxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 000001 0=E 100 - ]", mnemonic="lwarx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 000111 1=E 110 - ]", mnemonic="lwarxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 100001 0=E 110 - ]", mnemonic="lwbrx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 100001 1=E 110 - ]", mnemonic="lwbrxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 110001 0=E 110 - ]", mnemonic="lhbrx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 110001 1=E 110 - ]", mnemonic="lhbrxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 011111 1=E 111 1 ]", mnemonic="stdcxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 111001 0=E 110 - ]", mnemonic="sthbrx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 111001 1=E 110 - ]", mnemonic="sthbrxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 101001 0=E 110 - ]", mnemonic="stwbrx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 101001 1=E 110 - ]", mnemonic="stwbrxe") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 001001 0=E 110 - ]", mnemonic="stwcx") +@ispec("32<[ 011111 rT(5) rA(5) rB(5) 001001 1=E 110 - ]", mnemonic="stwcxe") +def ppc_load(obj,rT,rA,rB,E): + if rA==0: + addr = env.GPR[rB] + else: + addr = env.GPR[rA]+env.GPR[rB] + obj.operands = [env.GPR[rT],env.ptr(addr)] + obj.type = type_data_processing + +@ispec("32<[ 011111 rT(5) rA(5) NB(5) 1001010101 - ]", mnemonic="lswi") +@ispec("32<[ 011111 rT(5) rA(5) NB(5) 1000010101 - ]", mnemonic="lswx") +@ispec("32<[ 011111 rT(5) rA(5) NB(5) 1011010101 - ]", mnemonic="stswi") +@ispec("32<[ 011111 rT(5) rA(5) NB(5) 1010010101 - ]", mnemonic="stswx") +def ppc_load(obj,rT,rA,NB): + if rA==0: + addr = env.GPR[rB] + else: + addr = env.GPR[rA]+env.GPR[rB] + if obj.mnemonic=="lswi" and NB==0: + n = 32 + else: + n = NB + obj.operands = [env.GPR[rT],env.ptr(addr),NB] + obj.type = type_data_processing + +@ispec("32<[ 00111 s rT(5) rA(5) SI(16) ]", mnemonic="addi") +@ispec("32<[ 00011 1=s rT(5) rA(5) SI(16) ]", mnemonic="mulli") +@ispec("32<[ 00100 0=s rT(5) rA(5) SI(16) ]", mnemonic="subfic") +def ppc_S(obj,s,rT,rA,SI): + obj.s = s + if s==0: + imm = env.cst(SI,16).signextend(32) + else: + imm = env.cst(SI,32)<<16 + obj.operands = [env.GPR[rT],env.GPR[rA],imm] + obj.type = type_data_processing + +@ispec("32<[ 00110 .Rc rT(5) rA(5) SI(16) ]", mnemonic="addic") +def ppc_Rc(obj,rT,rA,SI): + imm = env.cst(SI,16).signextend(32) + obj.operands = [env.GPR[rT],env.GPR[rA],imm] + obj.type = type_data_processing + +@ispec("32<[ 011111 rT(5) rA(5) ----- .OE 0=E 11101010 .Rc ]", mnemonic="addme") +@ispec("32<[ 011111 rT(5) rA(5) ----- .OE 1=E 11101010 .Rc ]", mnemonic="addme64") +@ispec("32<[ 011111 rT(5) rA(5) ----- .OE 0=E 11001010 .Rc ]", mnemonic="addze") +@ispec("32<[ 011111 rT(5) rA(5) ----- .OE 1=E 11001010 .Rc ]", mnemonic="addze64") +@ispec("32<[ 011111 rT(5) rA(5) ----- .OE 0=E 01101000 .Rc ]", mnemonic="neg") +@ispec("32<[ 011111 rT(5) rA(5) ----- .OE 0=E 11101000 .Rc ]", mnemonic="subfme") +@ispec("32<[ 011111 rT(5) rA(5) ----- .OE 1=E 11101000 .Rc ]", mnemonic="subfme64") +@ispec("32<[ 011111 rT(5) rA(5) ----- .OE 0=E 11001000 .Rc ]", mnemonic="subfze") +@ispec("32<[ 011111 rT(5) rA(5) ----- .OE 1=E 11001000 .Rc ]", mnemonic="subfze64") +def ppc_OE_Rc(obj,rT,rA,E): + if obj.mnemonic.endswith("64") and obj.Rc==1: + raise InstructionError(obj) + obj.E = E + obj.operands = [env.GPR[rT],env.GPR[rA]] + obj.type = type_data_processing + +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0000011100 .Rc ]", mnemonic="and") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0000111100 .Rc ]", mnemonic="andc") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0100011100 .Rc ]", mnemonic="eqv") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0111011100 .Rc ]", mnemonic="nand") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0001111100 .Rc ]", mnemonic="nor") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0110111100 .Rc ]", mnemonic="or") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0110011100 .Rc ]", mnemonic="orc") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0000011000 .Rc ]", mnemonic="slw") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 1100011000 .Rc ]", mnemonic="sraw") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 1000011000 .Rc ]", mnemonic="srw") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0100111100 .Rc ]", mnemonic="xor") +def ppc_Rc(obj,rS,rA,rB): + obj.operands = [env.GPR[rA],env.GPR[rS],env.GPR[rB]] + obj.type = type_data_processing + +@ispec("32<[ 011111 rS(5) rA(5) SH(5) 1100011000 .Rc ]", mnemonic="srawi") +def ppc_Rc(obj,rS,rA,SH): + obj.operands = [env.GPR[rA],env.GPR[rS],env.cst(SH,5)] + obj.type = type_data_processing + +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 0000011011 - ]", mnemonic="sld") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 1100011010 - ]", mnemonic="srad") +@ispec("32<[ 011111 rS(5) rA(5) rB(5) 1000011011 - ]", mnemonic="srd") +def ppc_sld(obj,rS,rA,rB): + obj.operands = [env.GPR[rA],env.GPR[rS],env.GPR[rB]] + obj.type = type_data_processing + +@ispec("32<[ 011110 rS(5) rA(5) rB(5) mb(5) mb0 1000 - ]", mnemonic="rldcl") +@ispec("32<[ 011110 rS(5) rA(5) rB(5) mb(5) mb0 1001 - ]", mnemonic="rldcr") +def ppc_rldc(obj,rS,rA,rB,mb,mb0): + mask = env.cst(mb+(mb0<<5),32) + obj.operands = [env.GPR[rA],env.GPR[rS],env.GPR[rB],mask] + obj.type = type_data_processing + +@ispec("32<[ 010100 rS(5) rA(5) sh(5) mb(5) me(5) .Rc ]", mnemonic="rlwimi") +@ispec("32<[ 010111 rS(5) rA(5) sh(5) mb(5) me(5) .Rc ]", mnemonic="rlwnm") +@ispec("32<[ 010101 rS(5) rA(5) sh(5) mb(5) me(5) .Rc ]", mnemonic="rlwinm") +def ppc_Rc(obj,rS,rA,sh,mb,me): + sh = env.cst(sh,5) + mb = env.cst(mb,5) + me = env.cst(me,5) + obj.operands = [env.GPR[rA],env.GPR[rS],sh,mb,me] + obj.type = type_data_processing + +@ispec("32<[ 011110 rS(5) rA(5) sh(5) mb(5) mb0 000 sh0 - ]", mnemonic="rldicl") +@ispec("32<[ 011110 rS(5) rA(5) sh(5) mb(5) mb0 001 sh0 - ]", mnemonic="rldicr") +@ispec("32<[ 011110 rS(5) rA(5) sh(5) mb(5) mb0 010 sh0 - ]", mnemonic="rldic") +@ispec("32<[ 011110 rS(5) rA(5) sh(5) mb(5) mb0 011 sh0 - ]", mnemonic="rldimi") +def ppc_rld(obj,rS,rA,sh,mb,mb0,sh0): + mask = env.cst(mb+(mb0<<5),32) + obj.operands = [env.GPR[rA],env.GPR[rS],env.cst(sh+(sh0<<5),6),mask] + obj.type = type_data_processing + +@ispec("32<[ 011110 rS(5) rA(5) sh(5) 110011 101 sh0 - ]", mnemonic="sradi") +def ppc_rld(obj,rS,rA,sh,sh0): + mask = env.cst(mb+(mb0<<5),32) + obj.operands = [env.GPR[rA],env.GPR[rS],env.cst(sh+(sh0<<5),6)] + obj.type = type_data_processing + +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 0100001000 .Rc ]", mnemonic="fabs") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 0000001110 .Rc ]", mnemonic="fctiw") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 0000001111 .Rc ]", mnemonic="fctiwz") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 0001001000 .Rc ]", mnemonic="fmr") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 0010001000 .Rc ]", mnemonic="fnabs") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 0000101000 .Rc ]", mnemonic="fneg") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 0000001100 .Rc ]", mnemonic="frsp") +def ppc_Rc(obj,FRT,FRB): + obj.operands = [env.FPR[FRT],env.FPR[FRB]] + obj.type = type_data_processing + +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 1101001110 - ]", mnemonic="fcfid") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 1100101110 - ]", mnemonic="fctid") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) 1100101111 - ]", mnemonic="fctidz") +def ppc_fcfid(obj,FRT,FRB): + obj.operands = [env.FPR[FRT],env.FPR[FRB]] + obj.type = type_data_processing + +@ispec("32<[ 111111 FRT(5) FRA(5) FRB(5) -----10101 .Rc ]", mnemonic="fadd") +@ispec("32<[ 111011 FRT(5) FRA(5) FRB(5) -----10101 .Rc ]", mnemonic="fadds") +@ispec("32<[ 111111 FRT(5) FRA(5) FRB(5) -----10010 .Rc ]", mnemonic="fdiv") +@ispec("32<[ 111011 FRT(5) FRA(5) FRB(5) -----10010 .Rc ]", mnemonic="fdivs") +@ispec("32<[ 111111 FRT(5) FRA(5) FRB(5) -----10100 .Rc ]", mnemonic="fsub") +@ispec("32<[ 111011 FRT(5) FRA(5) FRB(5) -----10100 .Rc ]", mnemonic="fsubs") +def ppc_Rc(obj,FRT,FRA,FRB): + obj.operands = [env.FPR[FRT],env.FPR[FRA],env.FPR[FRB]] + obj.type = type_data_processing + +@ispec("32<[ 111011 FRT(5) ----- FRB(5) ----- 11000 .Rc ]", mnemonic="fres") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) ----- 11010 .Rc ]", mnemonic="frsqrte") +@ispec("32<[ 111111 FRT(5) ----- FRB(5) ----- 10110 .Rc ]", mnemonic="fsqrt") +@ispec("32<[ 111011 FRT(5) ----- FRB(5) ----- 10110 .Rc ]", mnemonic="fsqrts") +def ppc_Rc(obj,FRT,FRB): + obj.operands = [env.FPR[FRT],env.FPR[FRB]] + obj.type = type_data_processing + +@ispec("32<[ 111111 FRT(5) FRA(5) FRB(5) FRC(5) 11101 .Rc ]", mnemonic="fmadd") +@ispec("32<[ 111011 FRT(5) FRA(5) FRB(5) FRC(5) 11101 .Rc ]", mnemonic="fmadds") +@ispec("32<[ 111111 FRT(5) FRA(5) FRB(5) FRC(5) 11100 .Rc ]", mnemonic="fmsub") +@ispec("32<[ 111011 FRT(5) FRA(5) FRB(5) FRC(5) 11100 .Rc ]", mnemonic="fmsubs") +@ispec("32<[ 111111 FRT(5) FRA(5) FRB(5) FRC(5) 11111 .Rc ]", mnemonic="fnmadd") +@ispec("32<[ 111011 FRT(5) FRA(5) FRB(5) FRC(5) 11111 .Rc ]", mnemonic="fnmadds") +@ispec("32<[ 111111 FRT(5) FRA(5) FRB(5) FRC(5) 11110 .Rc ]", mnemonic="fnmsub") +@ispec("32<[ 111011 FRT(5) FRA(5) FRB(5) FRC(5) 11110 .Rc ]", mnemonic="fnmsubs") +@ispec("32<[ 111111 FRT(5) FRA(5) FRB(5) FRC(5) 10111 .Rc ]", mnemonic="fsel") +def ppc_Rc(obj,FRT,FRA,FRB,FRC): + obj.operands = [env.FPR[FRT],env.FPR[FRA],env.FPR[FRC],env.FPR[FRB]] + obj.type = type_data_processing + +@ispec("32<[ 111111 FRT(5) FRA(5) ----- FRC(5) 11101 .Rc ]", mnemonic="fmul") +@ispec("32<[ 111011 FRT(5) FRA(5) ----- FRC(5) 11101 .Rc ]", mnemonic="fmuls") +def ppc_Rc(obj,FRT,FRA,FRC): + obj.operands = [env.FPR[FRT],env.FPR[FRA],env.FPR[FRC],env.FPR[FRB]] + obj.type = type_data_processing + +@ispec("32<[ 111111 FRT(5) ----- ----- 10010 00111 .Rc ]", mnemonic="mffs") +def ppc_Rc(obj,FRT): + obj.operands = [env.FPR[FRT]] + obj.type = type_data_processing + +@ispec("32<[ 111111 - FLM(8) - FRB(5) 1011000111 .Rc ]", mnemonic="mtfsf") +def ppc_Rc(obj,FLM,FRB): + obj.operands = [env.cst(FLM,8),env.FPR[FRB]] + obj.type = type_data_processing + +@ispec("32<[ 111111 BF(3) ------- U(4) - 0010000110 .Rc ]", mnemonic="mtfsfi") +def ppc_Rc(obj,BF,U): + obj.operands = [env.cst(BF,3),env.cst(U,4)] + obj.type = type_data_processing + +@ispec("32<[ 111111 BF(3) -- FRA(5) FRB(5) 0000000000 - ]", mnemonic="fcmpu") +@ispec("32<[ 111111 BF(3) -- FRA(5) FRB(5) 0000100000 - ]", mnemonic="fcmpuo") +def ppc_fcmpu(obj,BF,FRA,FRB): + obj.operands = [BF,env.FPR[FRA],env.FPR[FRB]] + obj.type = type_data_processing + +@ispec("32<[ 01111 0=s rS(5) rA(5) UI(16) ]", mnemonic="andi") +@ispec("32<[ 01111 1=s rS(5) rA(5) UI(16) ]", mnemonic="andis") +@ispec("32<[ 01100 0=s rS(5) rA(5) UI(16) ]", mnemonic="ori") +@ispec("32<[ 01100 1=s rS(5) rA(5) UI(16) ]", mnemonic="oris") +@ispec("32<[ 01101 0=s rS(5) rA(5) UI(16) ]", mnemonic="xori") +@ispec("32<[ 01101 1=s rS(5) rA(5) UI(16) ]", mnemonic="xoris") +def ppc_S(obj,s,rS,rA,UI): + obj.Rc = 1 + obj.s = s + if s==0: + imm = env.cst(UI,16).zeroextend(32) + else: + imm = env.cst(UI,32)<<16 + obj.operands = [env.GPR[rA],env.GPR[rS],imm] + obj.type = type_data_processing + +@ispec("32<[ 010 E 10 LI(24) .AA .LK ]", mnemonic="b") +def ppc_E_AA_LK(obj,E,LI): + obj.E = E + soff = env.cst(LI,24).signextend(32)<<2 + obj.operands = [soff] + obj.type = type_control_flow + +@ispec("32<[ 010000 ~BO(5) ~BI(5) BD(14) .AA .LK ]", mnemonic="bc") +@ispec("32<[ 001001 ~BO(5) ~BI(5) BD(14) .AA .LK ]", mnemonic="bce") +def ppc_AA_LK(obj,BO,BI,BD): + soff = env.cst(BD,14).signextend(32)<<2 + obj.operands = [BO,BI,soff] + obj.type = type_control_flow + +@ispec("32<[ 010011 ~BO(5) ~BI(5) ----- 100001000 .E .LK ]", mnemonic="bcctr") +@ispec("32<[ 010011 ~BO(5) ~BI(5) ----- 000001000 .E .LK ]", mnemonic="bclr") +def ppc_E_LK(obj,BO,BI): + obj.operands = [BO,BI] + obj.type = type_control_flow + +@ispec("32<[ 011111 TO(5) RA(5) RB(5) 0001000100 - ]", mnemonic="td") +@ispec("32<[ 011111 TO(5) RA(5) RB(5) 0000000100 - ]", mnemonic="tw") +def ppc_trap(obj,TO,RA,RB): + obj.operands = [env.cst(TO,5),env.GPR[RA],env.GPR[RB]] + obj.type = type_system + +@ispec("32<[ 011111 ----- RA(5) RB(5) 1100010010=E - ]", mnemonic="tlbivax") +@ispec("32<[ 011111 ----- RA(5) RB(5) 1100010011=E - ]", mnemonic="tlbivaxe") +def ppc_trap(obj,RA,RB,E): + if RA==0: + addr = env.cst(0,32) + else: + addr = env.GPR[RA] + obj.E = E + obj.operands = [addr,env.GPR[RB]] + obj.type = type_system + +@ispec("32<[ 000010 TO(5) RA(5) SI(16) ]", mnemonic="tdi") +@ispec("32<[ 000011 TO(5) RA(5) SI(16) ]", mnemonic="twi") +def ppc_trap(obj,TO,RA,SI): + obj.operands = [env.cst(TO,5),env.GPR[RA],env.cst(SI,16).signextend(32)] + obj.type = type_system + +@ispec("32<[ 011111 BF(3) - L rA(5) rB(5) 0000000000 - ]", mnemonic="cmp") +@ispec("32<[ 011111 BF(3) - L rA(5) rB(5) 0000100000 - ]", mnemonic="cmpl") +def ppc_cmp(obj,BF,L,rA,rB): + obj.operands = [BF,L,env.GPR[rA],env.GPR[rB]] + obj.type = type_data_processing + +@ispec("32<[ 010011 BF(3) -- BFA(3) ------- 0000000000 - ]", mnemonic="mcfr") +@ispec("32<[ 111111 BF(3) -- BFA(3) ------- 0001000000 - ]", mnemonic="mcfrs") +def ppc_mcfr(obj,BF,BFA): + obj.operands = [BF,BFA] + obj.type = type_data_processing + +@ispec("32<[ 011111 BF(3) ----- ------- 1000000000 - ]", mnemonic="mcrxr") +@ispec("32<[ 011111 BF(3) ----- ------- 1000100000 - ]", mnemonic="mcrxr64") +def ppc_mcrx(obj,BF): + obj.operands = [BF] + obj.type = type_data_processing + +@ispec("32<[ 111111 BT(5) ---------- 0001000110 .Rc ]", mnemonic="mtfsb0") +@ispec("32<[ 111111 BT(5) ---------- 0000100110 .Rc ]", mnemonic="mtfsb1") +def ppc_mcrx(obj,BT): + obj.operands = [BT] + obj.type = type_data_processing + +@ispec("32<[ 011111 RT(5) RA(5) ----- 0100010011 - ]", mnemonic="mfapidi") +def ppc_mfapidi(obj,RT,RA): + obj.operands = [env.GPR[RT],env.GPR[RA]] + obj.type = type_data_processing + +@ispec("32<[ 011111 RT(5) ----- ----- 0000010011 - ]", mnemonic="mfcr") +@ispec("32<[ 011111 RT(5) ----- ----- 0001010011 - ]", mnemonic="mfmsr") +@ispec("32<[ 011111 RT(5) ----- ----- 0010010010 - ]", mnemonic="mtmsr") +def ppc_mfcr(obj,RT): + obj.operands = [env.GPR[RT]] + obj.type = type_data_processing + +@ispec("32<[ 011111 RT(5) dcrn2(5) dcrn1(5) 0101000011 - ]", mnemonic="mfdcr") +def ppc_mfdcr(obj,RT,dcrn2,dcrn1): + obj.operands = [env.GPR[RT],env.DCREG(dcrn2+(dcrn1<<5))] + obj.type = type_data_processing + +@ispec("32<[ 011111 RS(5) dcrn2(5) dcrn1(5) 0111000011 - ]", mnemonic="mtdcr") +def ppc_mtdcr(obj,RS,dcrn2,dcrn1): + obj.operands = [env.DCREG(dcrn2+(dcrn1<<5)),env.GPR[RS]] + obj.type = type_data_processing + +@ispec("32<[ 011111 RT(5) sprn2(5) sprn1(5) 0101010011 - ]", mnemonic="mfspr") +def ppc_mfspr(obj,RT,sprn2,sprn1): + obj.operands = [env.GPR[RT],env.SPREG(sprn2+(sprn1<<5))] + obj.type = type_data_processing + +@ispec("32<[ 011111 RS(5) sprn2(5) sprn1(5) 0111010011 - ]", mnemonic="mtspr") +def ppc_mtspr(obj,RS,sprn2,sprn1): + obj.operands = [env.SPREG(sprn2+(sprn1<<5)),env.GPR[RS]] + obj.type = type_data_processing + +@ispec("32<[ 011111 RS(5) - FXM(8) - 0010010000 - ]", mnemonic="mfspr") +def ppc_mtcrf(obj,RS,FXM): + obj.operands = [env.cst(FXM,8),env.GPR[RS]] + obj.type = type_data_processing + +@ispec("32<[ 001011 BF(3) - L rA(5) SI(16) ]", mnemonic="cmpi") +def ppc_cmpi(obj,BF,L,rA,SI): + if obj.mnemonic == "cmpi": + imm = env.cst(SI,16).signextend(32) + else: + imm = env.cst(SI,16).zeroextend(32) + obj.operands = [BF,L,env.GPR[rA],imm] + obj.type = type_data_processing + +@ispec("32<[ 011111 rS(5) rA(5) ----- 0000 0 11010 .Rc ]", mnemonic="cntlzw") +@ispec("32<[ 011111 rS(5) rA(5) ----- 0000 1 11010 .Rc ]", mnemonic="cntlzd") +@ispec("32<[ 011111 rS(5) rA(5) ----- 1110 1 11010 .Rc ]", mnemonic="extsb") +@ispec("32<[ 011111 rS(5) rA(5) ----- 1110 0 11010 .Rc ]", mnemonic="extsh") +@ispec("32<[ 011111 rS(5) rA(5) ----- 1111 0 11010 .Rc ]", mnemonic="extsw") +def ppc_Rc(obj,rS,rA): + if obj.mnemonic=="cntlzd" and obj.Rc==1: + raise InstructionError(obj) + obj.operands = [env.GPR[rA],env.GPR[rS]] + obj.type = type_data_processing + +@ispec("32<[ 010011 BT(5) BA(5) BB(5) 0100000001 - ]", mnemonic="crand") +@ispec("32<[ 010011 BT(5) BA(5) BB(5) 0010000001 - ]", mnemonic="crandc") +@ispec("32<[ 010011 BT(5) BA(5) BB(5) 0100100001 - ]", mnemonic="creqv") +@ispec("32<[ 010011 BT(5) BA(5) BB(5) 0011100001 - ]", mnemonic="crnand") +@ispec("32<[ 010011 BT(5) BA(5) BB(5) 0000100001 - ]", mnemonic="crnor") +@ispec("32<[ 010011 BT(5) BA(5) BB(5) 0111000001 - ]", mnemonic="cror") +@ispec("32<[ 010011 BT(5) BA(5) BB(5) 0110100001 - ]", mnemonic="crorc") +@ispec("32<[ 010011 BT(5) BA(5) BB(5) 0011000001 - ]", mnemonic="crxor") +def ppc_cr(obj,BT,BA,BB): + obj.operands = [env.CR[BT:BT+1], env.CR[BA:BA+1], env.CR[BB:BB+1]] + obj.type = type_data_processing + +@ispec("32<[ 011111 ----- rA(5) rB(5) 101111 0 110 - ]", mnemonic="dcba") +@ispec("32<[ 011111 ----- rA(5) rB(5) 101111 1 110 - ]", mnemonic="dcbae") +@ispec("32<[ 011111 ----- rA(5) rB(5) 000101 0 110 - ]", mnemonic="dcbf") +@ispec("32<[ 011111 ----- rA(5) rB(5) 000101 1 110 - ]", mnemonic="dcbfe") +@ispec("32<[ 011111 ----- rA(5) rB(5) 011101 0 110 - ]", mnemonic="dcbi") +@ispec("32<[ 011111 ----- rA(5) rB(5) 011101 1 110 - ]", mnemonic="dcbie") +@ispec("32<[ 011111 ----- rA(5) rB(5) 000011 0 110 - ]", mnemonic="dcbst") +@ispec("32<[ 011111 ----- rA(5) rB(5) 000011 1 110 - ]", mnemonic="dcbste") +@ispec("32<[ 011111 ----- rA(5) rB(5) 111111 0 110 - ]", mnemonic="dcbz") +@ispec("32<[ 011111 ----- rA(5) rB(5) 111111 1 110 - ]", mnemonic="dcbze") +@ispec("32<[ 011111 ----- rA(5) rB(5) 111101 0 110 - ]", mnemonic="icbi") +@ispec("32<[ 011111 ----- rA(5) rB(5) 111101 1 110 - ]", mnemonic="icbie") +def ppc_dc(obj,rA,rB): + obj.operands = [env.GPR[rA],env.GPR[rB]] + obj.type = type_data_processing + +@ispec("32<[ 011111 CT(5) rA(5) rB(5) 010001 0 110- ]", mnemonic="dcbt") +@ispec("32<[ 011111 CT(5) rA(5) rB(5) 010001 1 110- ]", mnemonic="dcbte") +@ispec("32<[ 011111 CT(5) rA(5) rB(5) 001111 0 110- ]", mnemonic="dcbtst") +@ispec("32<[ 011111 CT(5) rA(5) rB(5) 001111 1 110- ]", mnemonic="dcbtste") +@ispec("32<[ 011111 CT(5) rA(5) rB(5) 000001 0 110- ]", mnemonic="icbt") +@ispec("32<[ 011111 CT(5) rA(5) rB(5) 000001 1 110- ]", mnemonic="icbte") +def ppc_dc2(obj,CT,rA,rB): + obj.operands = [env.cst(CT,5),env.GPR[rA],env.GPR[rB]] + obj.type = type_data_processing + +@ispec("32<[ 010011 --------------- 0010010110 - ]", mnemonic="isync") +@ispec("32<[ 011111 --------------- 1001010110 - ]", mnemonic="msync") +@ispec("32<[ 010011 --------------- 0000110011 - ]", mnemonic="rfci") +@ispec("32<[ 010011 --------------- 0000110010 - ]", mnemonic="rfi") +@ispec("32<[ 010001 --------------- ---------1 - ]", mnemonic="sc") +@ispec("32<[ 011111 --------------- 1000110110 - ]", mnemonic="tlbsync") +def ppc_dc2(obj): + obj.operands = [] + obj.type = type_other + +@ispec("32<[ 011111 data(15) 1110110010 - ]", mnemonic="tlbre") +@ispec("32<[ 011111 data(15) 1111010010 - ]", mnemonic="tlbwe") +def ppc_dc2(obj,data): + obj.tlb_data = env.cst(data,15) + obj.operands = [] + obj.type = type_other + +@ispec("32<[ 011111 ----- rA(5) rB(5) 1110010010 - ]", mnemonic="tlbsx") +@ispec("32<[ 011111 ----- rA(5) rB(5) 1110010011 - ]", mnemonic="tlbsxe") +def ppc_tlb(obj,rA,rB): + if rA==0: + addr = env.cst(0,32) + else: + addr = env.GPR[rA] + obj.operands = [addr,env.GPR[rB]] + obj.type = type_other + +@ispec("32<[ 011111 MO(5) ---------- 1101010110 - ]", mnemonic="mbar") +def ppc_dc2(obj,MO): + obj.operands = [env.cst(MO,5)] + obj.type = type_other + +@ispec("32<[ 011111 RS(5) ----- ----- 0010000011 - ]", mnemonic="wrtee") +def ppc_dc2(obj,RS): + obj.operands = [env.GPR[RS][31:32]] + obj.type = type_system + +@ispec("32<[ 011111 ----- ----- E---- 0010100011 - ]", mnemonic="wrteei") +def ppc_dc2(obj,E): + obj.operands = [env.cst(E,1)] + obj.type = type_system + diff --git a/amoco/arch/ppc32/spec_vle.py b/amoco/arch/ppc32/spec_vle.py new file mode 100644 index 0000000..f7e5901 --- /dev/null +++ b/amoco/arch/ppc32/spec_vle.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2022 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +from . import env + +from amoco.arch.core import * + +ISPECS = [] + +@ispec("16<[ 000001 00 rY(4) rX(4) ]", mnemonic="se_add") +def ppc_addx(obj,rX,rY): + obj.operands = [env.GPR[rX],env.GPR[rY]] + obj.type = type_data_processing diff --git a/amoco/cas/expressions.py b/amoco/cas/expressions.py index 6b6781f..55d858f 100644 --- a/amoco/cas/expressions.py +++ b/amoco/cas/expressions.py @@ -2107,10 +2107,11 @@ def ismask(v): def eqn2_helpers(e, bitslice=False, widening=False): "helpers for simplifying binary expressions" threshold = conf.Cas.complexity - if complexity(e.r) > threshold: - e.r = top(e.r.size) - if complexity(e.l) > threshold: - e.l = top(e.l.size) + if threshold>0: + if complexity(e.r) > threshold: + e.r = top(e.r.size) + if complexity(e.l) > threshold: + e.l = top(e.l.size) if e.r._is_top or e.l._is_top: return top(e.size) # if e := ((a l.op cst) e.op r) @@ -2156,6 +2157,12 @@ def eqn2_helpers(e, bitslice=False, widening=False): # if e:= (l [|&*] 0) then e:= 0 if e.op.symbol in (OP_AND, OP_MUL, OP_MUL2): return cst(0, e.size) + # external == 0 ? => false, we assume ext is defined + if e.op.symbol == OP_EQ and e.l._is_ext: + return bit0 + # external != 0 ? => true, we assume ext is defined + if e.op.symbol == OP_NEQ and e.l._is_ext: + return bit1 # if e:= (l [|*/] 1) then e:= l elif e.r.value == 1 and e.op.symbol in (OP_MUL, OP_MUL2, OP_DIV): return e.l diff --git a/amoco/config.py b/amoco/config.py index 32981c5..02077ef 100644 --- a/amoco/config.py +++ b/amoco/config.py @@ -113,15 +113,14 @@ class Cas(Configurable): Configurable parameters related to the Computer Algebra System (expressions). Attributes: - complexity (int): limit expressions complexity to given value. Defaults - to 10000, a relatively high value that keeps precision - but can lead to very large expressions. + complexity (int): limit expressions complexity to given value if >0. + Defaults to 0, ie. no limit. unicode (Bool): use unicode character for expressions' operators if True. noaliasing (Bool): If True (default), then assume that symbolic memory expressions (pointers) are **never** aliased. memtrace (Bool): keep memory writes in mapper in addition to MemoryMap (default). """ - complexity = Integer(10000, config=True) + complexity = Integer(0, config=True) unicode = Bool(False, config=True) noaliasing = Bool(True, config=True) memtrace = Bool(True, config=True) diff --git a/amoco/sa/ghidra.py b/amoco/sa/ghidra.py index 2c960bc..7e94d15 100644 --- a/amoco/sa/ghidra.py +++ b/amoco/sa/ghidra.py @@ -1,3 +1,8 @@ +from amoco.logger import Log + +logger = Log(__name__) +logger.debug("loading module") + import ghidra_bridge b = ghidra_bridge.GhidraBridge(namespace=locals()) @@ -6,8 +11,13 @@ def select_range(begin,end): AddressSet = ghidra.program.model.address.AddressSet setCurrentSelection(AddressSet(toAddr(begin),toAddr(end))) -def add_memory_block(name,start,size,val=None,access="rw"): +def add_memory_block(name,start,size,val=None,access="rw",dt=None): mmap = currentProgram.memory + segs = mmap.blocks + S = {} + for s in segs: + S[s.getStart().getOffset()] = s + blk = mmap.createInitializedBlock(name, toAddr(start), size, val, monitor, False) if "w" in access: @@ -16,6 +26,17 @@ def add_memory_block(name,start,size,val=None,access="rw"): if "x" in access: blk.setExecute(True) + if isinstance(dt,str): + s_ = getDataTypes(dt) + else: + s_ = [dt] + if len(s_)==1: + s_ = s_[0] + else: + logger.warn("too many structures named %s"%dt) + s_ = None + if s_ is not None: + createData(toAddr(start), s_) return blk def setPointer(address,size=4): @@ -48,7 +69,7 @@ def getFunctions_XRefd_at_Location(address): for r in ghidra.app.util.XReferenceUtils.getAllXrefs(loc): f = getFunctionContaining(r.getFromAddress()) if f is None: - print("no function containing reference %s"%r) + logger.warn("no function containing reference %s"%r) continue F.append(f) return set(F) @@ -101,3 +122,107 @@ def walk(node,L): walk(res.getCCodeMarkup(),nodes) return nodes +def propagateType(addr, dt, level=2, data=None, force=True, create=True, _stack=[]): + """at given address 'addr', apply structure type 'dt' and try to + propagate pointers to sub-structures with given depth level (default is 2.) + All other arguments are used internally for recursion... + """ + if len(_stack)==level: + return 0 + if isinstance(addr,int): + cur = toAddr(addr) + else: + cur = addr + addr = cur.getOffset() + if addr<0x40000000: + return 0 + logger.debug("address: %s"%str(cur)) + if isinstance(dt,str): + S = getDataTypes(dt) + if len(S)>1: + logger.warn("too many types found for '%s'"%dt) + return 1 + elif len(S)==0: + logger.warn("no type named '%s'"%dt) + return 2 + else: + dt = S[0] + logger.debug("type: %s"%dt.getName()) + if create: + if force: + clearListing(cur, toAddr(addr+dt.length-1)) + logger.debug("listing cleared for zone [%08x,%08x]"%(addr,addr+dt.length-1)) + elif (getInstructionAt(cur) is not None) or (getDataAt(cur) is not None): + logger.warn("can't overwrite existing instr/data at 0x%08x"%addr) + return 3 + logger.verbose("creating data...") + createData(cur, dt) + if data is None: + s = getDataAt(cur) + else: + s = data + # start propagating sub-types...using recursion limited to a given level depth: + _stack.append(s) + logger.verbose("propagating fields of %s"%str(s)) + for i in range(s.getNumComponents()): + f = s.getComponent(i) + logger.debug(" [%d] field %s"%(i,f.componentPathName)) + t = f.getDataType() + if f.isPointer() and t.dataType.__class__.__name__=='StructureDB': + logger.debug(" --(pointer)--") + try: + err = propagateType(f.value, t.dataType, level) + except Exception as e: + logger.error("can't propagate %s at %s"%(t.dataType.getName(), str(f.address))) + logger.debug("exception was: %s"%e) + err = 4 + if err!=0: + logger.warn("error during propagation of type %s at 0x08x"%(dt.getName(), addr)) + else: + logger.verbose(" done") + elif f.isStructure(): + logger.debug(" --(structure)--") + try: + err = propagateType(f.address, t, data=f, create=False) + except Exception as e: + logger.error("can't propagate field %s (error is '%s')"%(str(f),str(e))) + _stack.pop() + return 0 + +def propagate_here(level=1): + """at current address which should be on a pointer field within a mapped structure, + find the current (pointer) sub-field's type and apply it at the pointer address + with given depth level (default is 1). + """ + found = False + addr = currentAddress.getOffset() + s = getDataContaining(currentAddress) + logger.verbose("searching for component at address %s"%currentAddress) + while not found: + if s.isStructure() or s.isArray(): + if s.isStructure(): + logger.verbose("component is in structure %s..."%s) + if s.isArray(): + logger.verbose("component is in array %s..."%s) + saddr = s.address.getOffset() + off = addr-saddr + c = s.getComponentContaining(off) + if c is None: + logger.warn("component not found...") + break + if c.isPointer() and c.address.getOffset()==addr: + logger.verbose("found Pointer component field %s"%c.componentPathName) + found = True + else: + s = c + else: + logger.warn("currentAddress is not a structure field ??") + break + if found: + t = c.getDataType().dataType + logger.verbose("propagating type %s at address %s"%(t.getName(),c.value)) + err = propagateType(c.value,t,level) + return c.value + else: + return currentAddress + diff --git a/amoco/sa/utils.py b/amoco/sa/utils.py new file mode 100644 index 0000000..5d0dcaa --- /dev/null +++ b/amoco/sa/utils.py @@ -0,0 +1,16 @@ +import math +from math import log +from collections import Counter + +def entropy(data): + if len(data) <= 1: + return 0 + counts = Counter() + for d in data: + counts[d] += 1 + res = 0 + probs = [float(c) / len(data) for c in counts.values()] + for p in probs: + if p > 0.: + res -= p * log(p, math.exp(1)) + return res diff --git a/amoco/system/coff.py b/amoco/system/coff.py new file mode 100644 index 0000000..a5156b6 --- /dev/null +++ b/amoco/system/coff.py @@ -0,0 +1,526 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2006-2023 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +""" +system/coff.py +============== + +The system coff module implements COFF classes for 32bits executable format. +""" +from amoco.system.core import BinFormat +from amoco.system.structs import Consts, StructDefine, UnionDefine, StructureError +from amoco.system.structs import struct, StructFormatter, token_constant_fmt, token_datetime_fmt + +from amoco.logger import Log + +logger = Log(__name__) +logger.debug("loading module") + +class COFFError(Exception): + """ + COFFError is raised whenever COFF object instance fails + to decode required structures. + """ + def __init__(self, message): + self.message = message + + def __str__(self): + return str(self.message) + +# ------------------------------------------------------------------------------ + +class COFF(BinFormat): + """ + This class takes a DataIO object (ie an opened file of BytesIO instance) + and decodes all COFF structures found in it. + + Attributes: + entrypoints (list of int): list of entrypoint addresses. + filename (str): binary file name. + Fhdr (Fhdr): the COFF header structure. + Shdr (list of Shdr): the list of COFF Section header structures. + basemap (int): base address for this COFF image. + functions (list): a list of function names gathered from internal + definitions (if not stripped) and import names. + variables (list): a list of global variables' names (if found.) + """ + is_COFF = True + + @property + def entrypoints(self): + return [self.OptHdr.entry] + + @property + def filename(self): + return self.__file.name + + @property + def header(self): + return self.Fhdr + + @property + def dataio(self): + return self.__file + + def __init__(self, f): + self.__file = f + self.Fhdr = FILEHDR(f) + offset = self.Fhdr.size() + offmax = f.size()-1 + if self.Fhdr.f_opthdr > 0: + sz = self.Fhdr.f_opthdr + try: + self.OptHdr = AOUTHDR(f,offset) + assert self.OptHdr.size() == (sz:=self.Fhdr.f_opthdr) + except AssertionError: + self.OptHdr = f[offset:offset+sz] + offset += sz + self.Shdr = [] + # read sections headers: + for _ in range(self.Fhdr.f_nscns): + s = SCNHDR(f,offset) + self.Shdr.append(s) + offset += s.size() + if offset>=offmax: + logger.warn("no symbol/string tables found") + return + # read symbol table + self.symbols = [] + for _ in range(self.Fhdr.f_nsyms): + e = SYMENT(f,offset) + self.symbols.append(e) + offset += e.size_with_aux() + if offset>=offmax: + logger.warn("no string table found") + return + # read string table + if offset+4>=offmax: + logger.warn("no string table found") + self.strings = b'' + return + try: + sz = struct.unpack(">4 + for _ in range(6): + dt.append(v&0x3) + v = v>>2 + return dt + + def get_aux(self,data,offset=0): + aux = [] + for _ in range(self._v.n_numaux): + x = AUXENT(data,offset) + aux.append(x) + offset += x.size() + return aux + + def size_with_aux(self): + return self.size() + sum((a.size() for a in self.aux),0) + +with Consts("n_scnum"): + N_DEBUG = -2 + N_ABS = -1 + N_UNDEF = 0 + +with Consts("n_sclass"): + C_EFCN = -1 + C_NULL = 0 + C_AUTO = 1 # for .target, value means stack offset in bytes + C_EXT = 2 # value means relocatable address + C_STAT = 3 # for .text .data and .bss value means relocatable address + C_REG = 4 # value means register number + C_EXTDEF = 5 + C_LABEL = 6 # value means relocatable address + C_ULABEL = 7 + C_MOS = 8 # value means offset in bytes + C_ARG = 9 # value means stack offset in bytes + C_STRARG = 10 + C_MOU = 11 + C_UNTAG = 12 + C_TPDEF = 13 + C_USTATIC = 14 + C_ENTAG = 15 + C_MOE = 16 # value means enumeration value + C_REGPARM = 17 # value means register number + C_FIELD = 18 # value means bit displacement + C_BLOCK = 100 # for .bb and .eb value means relocatable address + C_FCN = 101 # for .bf and .ef value means relocatable address + C_EOS = 102 # for .eos value means size + C_FILE = 103 # for .file + C_LINE = 104 + C_ALIAS = 105 # generated by cprs (compress utility) value means tag index + C_HIDDEN = 106 # not used by any unix system tools value means relocatable address + +# auxiliary entries: + +@StructDefine( +""" +H : x_lnno +H : x_size +""" +) +class lnsz(StructFormatter): + pass + +@UnionDefine( +""" +lnsz : x_lnsz +i : x_fsize +""" +) +class aux_misc(StructFormatter): + pass + +@StructDefine( +""" +i : x_lnnoptr +i : x_endndx +""" +) +class fcn(StructFormatter): + pass + +@StructDefine( +""" +H*4 : x_dimen +""" +) +class ary(StructFormatter): + pass + +@UnionDefine( +""" +fcn : x_fcn +ary : x_ary +""" +) +class u_fcnary(StructFormatter): + pass + +@UnionDefine( + +""" +i : x_tagndx +aux_misc : x_misc +u_fcnary : x_fcnary +H : x_tvndx +""" +) +class aux_sym(StructFormatter): + pass + +@StructDefine( +""" +c*14 : x_fname +""" +) +class aux_file(StructFormatter): + pass + +@StructDefine( +""" +i : x_scnlen +H : x_nreloc +H : x_nlinno +""" +) +class aux_scn(StructFormatter): + pass + +@StructDefine( +""" +i : x_tvfill +H : x_tvlen +H*2 : x_tvran +""" +) +class aux_tv(StructFormatter): + pass + +@StructDefine( +""" +aux_sym : x_sym +aux_file : x_file +aux_scn : x_scn +aux_tv : x_tv +""" +) +class AUXENT(StructFormatter): + pass + diff --git a/amoco/system/core.py b/amoco/system/core.py index 9b6e828..c54baaa 100644 --- a/amoco/system/core.py +++ b/amoco/system/core.py @@ -294,6 +294,7 @@ class BinFormat(object): is_MachO = False is_HEX = False is_SREC = False + is_COFF = False basemap = None symtab = None strtab = None @@ -453,14 +454,15 @@ def softspace(self): # ------------------------------------------------------------------------------ def read_program(filename): """ - Identifies the program header and returns an ELF, PE, Mach-O or DataIO. + Identifies the program header and returns an ELF, PE, Mach-O, COFF + or DataIO. Args: filename (str): the program to read. Returns: an instance of currently supported program format - (ELF, PE, Mach-O, HEX, SREC) + (ELF, PE, Mach-O, HEX, SREC, COFF) """ try: @@ -503,6 +505,17 @@ def read_program(filename): f.seek(0) logger.debug("MachOError raised for %s" % f.name) + try: + from amoco.system import coff + + # open file as a COFF object: + p = coff.COFF(f) + logger.info("COFF format detected") + return p + except (coff.StructureError,coff.COFFError): + f.seek(0) + logger.debug("COFFError raised for %s" % f.name) + try: from amoco.system.structs.HEX import HEX,HEXError @@ -573,7 +586,7 @@ def __call__(self, loader): def load_program(f, cpu=None, loader=None): """ - Detects program format header (ELF/PE/Mach-O/HEX/SREC), or consider + Detects program format header (ELF/PE/Mach-O/HEX/SREC/COFF), or consider the input as a raw "shellcode" if no supported format is recognized, and *maps* the program in abstract memory, loading the associated "system" (linux/win) and "arch" (x86/arm), diff --git a/amoco/system/elf.py b/amoco/system/elf.py index 864963b..5716864 100644 --- a/amoco/system/elf.py +++ b/amoco/system/elf.py @@ -219,17 +219,21 @@ def loadsegment(self, S, pagesize=None): need to be mapped at virtual base address. (Returns None if not a PT_LOAD segment.) """ + ELF_PAGESTART = lambda _v : (_v)&(~(pagesize-1)) + ELF_PAGEOFFSET = lambda _v : (_v)&( (pagesize-1)) + ELF_PAGEALIGN = lambda _v : (_v+pagesize-1) & (~(pagesize-1)) if S.p_type == PT_LOAD: - self.__file.seek(S.p_offset) - if S.p_align > 1 and (S.p_offset != (S.p_vaddr % S.p_align)): + if S.p_align > 1 and ((S.p_offset % S.p_align) != (S.p_vaddr % S.p_align)): logger.verbose( "wrong p_vaddr/p_align [%08x/%0d]" % (S.p_vaddr, S.p_align) ) - base = S.p_vaddr - bytes_ = self.__file.read(S.p_filesz).ljust(S.p_memsz, b"\x00") - if pagesize: - # note: bytes are not truncated, only extended if needed... - bytes_ = bytes_.ljust(pagesize, b"\x00") + size = S.p_filesz + ELF_PAGEOFFSET(S.p_vaddr) + off = S.p_offset - ELF_PAGEOFFSET(S.p_vaddr) + addr = ELF_PAGESTART(S.p_vaddr) + size = ELF_PAGEALIGN(size) + self.__file.seek(off) + base = addr + bytes_ = self.__file.read(size) return {base: bytes_} else: logger.error("segment not a PT_LOAD [%08x/%0d]" % (S.p_vaddr, S.p_align)) diff --git a/amoco/system/gdb_tfile.py b/amoco/system/gdb_tfile.py new file mode 100644 index 0000000..f4949f0 --- /dev/null +++ b/amoco/system/gdb_tfile.py @@ -0,0 +1,297 @@ +# -*- coding: utf-8 -*- + +# This code is part of Amoco +# Copyright (C) 2023 Axel Tillequin (bdcht3@gmail.com) +# published under GPLv2 license + +from amoco.system.core import DataIO + +from amoco.logger import Log +logger = Log(__name__) +logger.debug("loading module") + +from amoco.system.structs import StructDefine, StructFactory, StructureError +from amoco.system.structs import StructFormatter, token_bytes_fmt +import xml.etree.ElementTree as ET + + +# GDB trace file format. +# ------------------------------------------------------------------------------ + +class GDBTrace(object): + def __init__(self, f): + self.dataio = data = DataIO(f) + eof = data.size() + offset = 0 + self.header = GDBTraceHeader(data,offset) + offset += len(self.header) + self.descr = GDBTraceDescr(data,offset) + offset += self.descr.size() + self.frames = [] + if (n:=self.descr.status.traceframe_count()) > 0: + for _ in range(n): + self.frames.append(GDBTraceFrame(data,offset)) + # use len(...) not .size() because a GDBTraceFrame is + # a variable length structure so we actually want the + # effective unpacked length rather than the class-level + # infinite length. + offset += len(self.frames[-1]) + + def get_register_block_struct(self): + conv = {8:'B',16:'H',32:'I',64:'Q',80:'B*10',128:'Q*2'} + regs = [r for r in self.descr.target.iter("reg")] + fmt = ["%s : %s"%(conv[int(r.attrib['bitsize'])],r.attrib['name']) for r in regs] + if len(fmt)>0: + return StructFactory("Registers","\n".join(fmt),packed=True) + else: + return None + + def get_code_ptr(self): + code_ptr = [r for r in self.descr.target.iter("reg") if r.attrib['type']=='code_ptr'] + assert len(code_ptr)==1 + return code_ptr[0] + + def get_tracepoint_frame_by_address(self,address): + code_ptr = self.get_code_ptr() + name = code_ptr.attrib['name'] + struct_R = self.get_register_block_struct() + if struct_R is not None: + for tp in self.descr.tp: + for fr in self.frames: + if fr.frame_type==b'R' and fr.number==tp.number: + if not hasattr(fr,'regs'): + fr.regs = struct_R().unpack(data=fr.raw,offset=1) + if address==fr.regs[name]: + return (tp,fr) + return (None,None) + + def get_collected_registers(self,tpnum,frame=None,*args): + struct_R = self.get_register_block_struct() + if struct_R is not None: + for tp in self.descr.tp: + if tp.number != tpnum: + continue + col = [] + found = frame is None + for fr in self.frames: + if not found and (fr is frame): + found = True + if found and fr.frame_type==b'R' and fr.number==tp.number: + if not hasattr(fr,'regs'): + fr.regs = struct_R().unpack(data=fr.raw,offset=1) + D = {} + for r in args: + D[r] = fr.regs[r] + col.append(D) + return col + return None + + + + +@StructDefine( +""" +B : magic +c*5: kind +c : version +c : eol +""" +) +class GDBTraceHeader(StructFormatter): + def __init__(self, data=None, offset=0): + self.func_formatter(magic=token_bytes_fmt) + if data is not None: + self.unpack(data,offset) + if self.magic != 0x7f or self.kind != b'TRACE': + raise StructureError("not a gdb trace file ?") + +class GDBTraceDescr(object): + def __init__(self, data=None, offset=0): + begpos = offset + done = False + descr = b'' + data.seek(offset) + while not done: + l = data.readline() + if len(l)==1: + descr += l + done = True + else: + descr += l + self._descr = descr + # now make sense of this descr: + self.reg_blk_sz = None + self.status = None + self.tp = [] + self.tsv = [] + tdesc = b'' + for l in self._descr.split(b'\n'): + if l.startswith(b'R '): + if self.reg_blk_sz is None: + self.reg_blk_sz = int(l[2:],16) + else: + logger.warn("gdb trace register block is already defined") + elif l.startswith(b'status '): + if self.status is None: + self.status = qTStatus(l,7) + else: + logger.warn("gdb trace status is already defined") + elif l.startswith(b'tp '): + self.tracepoint(l,3) + elif l.startswith(b'tsv '): + self.tsv.append(TraceStateVariable(l[4:])) + elif l.startswith(b'tdesc '): + tdesc += l[6:] + self.tdesc = tdesc + self.target = ET.fromstring(tdesc) + + def tracepoint(self,l,offset): + l = l[offset:] + if l.startswith(b'T'): + self.tp.append(TracePoint(l)) + else: + self.tp[-1].define(l) + + def __len__(self): + return len(self._descr) + + def size(self): + return len(self) + +class qTStatus(object): + def __init__(self, data=None, offset=0): + self.data = data[offset:] + self.running = self.data[0]==b'1' + opt = {} + for o in self.data[2:].split(b";"): + try: + x = o.index(b":") + v = o[x+1:] + n = o[:x] + if len(v)>0: + v = int(v,16) + opt[n] = v + except ValueError: + continue + self.options = opt + + def option(self,opt): + return self.options.get(opt,None) + + def traceframe_count(self): + return self.option(b"tframes") or -1 + def traceframes_created(self): + return self.option(b"tcreated") or 1 + def buffer_free(self): + return self.option(b"tfree") or -1 + def buffer_size(self): + return self.option(b"tsize") or -1 + def disconnected_tracing(self): + return self.option(b"disconn") + def circular_buffer(self): + return self.option(b"circular") + def start_time(self): + return self.option(b"starttime")/1.e6 + def stop_time(self): + return self.option(b"stoptime")/1.e6 + def user_name(self): + return self.option(b"username") + + +class TraceStateVariable(object): + def __init__(self,l=None,offset=0): + if l is not None: + O = l[offset:].split(b':') + self.number = int(O[0],16) + self.initial_value = O[1] + self.builtin = int(O[2],16) + self.name = O[3] + + +class TracePoint(object): + def __init__(self,l): + if l.startswith(b'T'): + O = l[1:].split(b':') + self.number = int(O[0],16) + self.address = O[1] + self.enabled = O[2]==b'E' + self.step = int(O[3],16) + self.nbpass = int(O[4],16) + self.fast = None + self.cond = None + for o in O[5:]: + if o.startswith(b'F'): + self.fast = int(o[1:],16) + elif o.startswith(b'X'): + self.cond = o[1:].split(b',') + self.act = [] + self.step_act = [] + self.src = [] + self.step_src = [] + self.hit_count = None + self.traceframe_usage = None + + def getO(self,l): + O = l[1:].split(b':') + n = int(O[0],16) + if n!=self.number: + logger.error("unknown tracepoint number ?") + raise ValueError(n) + a = O[1] + if a!=self.address: + logger.error("unknown tracepoint address ?") + raise ValueError(a) + return O + + def define(self,l): + O = self.getO(l) + if l.startswith(b'A'): + self.act.append(O[2]) + elif l.startswith(b'S'): + self.step_act.append(O[2]) + elif l.startswith(b'Z'): + s = self.decode_source(O[2:]) + if len(self.step_act)>0: + self.step_src.append(s) + else: + self.src.append(s) + elif l.startswith(b'V'): + self.hit_count = int(O[2],16) + self.traceframe_usage = O[3] + + def decode_source(self,O): + src_type = O[0] + src_len = int(O[2],16) + to_src = lambda x: bytes.fromhex(x.decode('utf-8')) + src_def = to_src(O[3]) + assert src_len == len(src_def) + return (src_type,src_def) + + def __str__(self): + s = "tracepoint %d at address '%s'"%(self.number,self.address) + if len(self.step_src)>0: + s += ":" + s += "\n" + for l in self.step_src: + if l[0]==b'cmd': + s += " %s\n"%l[1] + return s + + +@StructDefine( +""" +H : number +c*~I : raw +""", packed = True +) +class GDBTraceFrame(StructFormatter): + def __init__(self, data=None, offset=0): + self.frame_type = None + if data: + self.unpack(data,offset) + if len(self.raw)>0: + self.frame_type = self.raw[0:1] + + +# ------------------------------------------------------------------------------ + diff --git a/amoco/system/structs/SREC.py b/amoco/system/structs/SREC.py index 0e84707..9171eb7 100644 --- a/amoco/system/structs/SREC.py +++ b/amoco/system/structs/SREC.py @@ -124,12 +124,7 @@ def set(self, line): # byte count: self.count = int(line[2:4], 16) # address: - l = [4, 4, 6, 8, 0, 4, 6, 8, 6, 4][self.SRECtype] - if self.SRECtype in (Count16, Count24): - r = 2*self.count - if r-2 != l: - l = r-2 - self.size = l + l = self.size self.address = int(line[4 : 4 + l], 16) # data: # c = 4+l+2*self.count @@ -141,16 +136,40 @@ def set(self, line): s = (ord(x) for x in s) cksum = sum(s) & 0xFF self.cksum = cksum ^ 0xFF - assert self.cksum == int(line[-2:], 16) - except (AssertionError, ValueError): + if self.cksum != int(line[-2:], 16): + logger.warn("bad checksum, needed %02x"%(cksum^0xff)) + except (AssertionError,ValueError): raise SRECError(line) - def pack(self): + @property + def size(self): + l = [4, 4, 6, 8, 0, 4, 6, 8, 6, 4][self.SRECtype] + if self.SRECtype in (Count16, Count24): + r = 2*self.count + if r-2 != l: + l = r-2 + return l + + def fixit(self,count=True,cksum=True): + if self.SRECtype: + l = self.size + if count: + self.count = (l/2)+len(self.data)+1 + if cksum: + s = self.pack(False) + s = codecs.decode(s[2:],"hex") + if isinstance(s, str): + s = (ord(x) for x in s) + cksum = sum(s) & 0xFF + self.cksum = cksum^0xFF + + def pack(self,cksum=True): s = b"S%1d%02X" % (self.SRECtype, self.count) fa = b"%%0%dX" % self.size s += fa % self.address s += codecs.encode(self.data, "hex").upper() - s += b"%02X" % self.cksum + if cksum: + s += b"%02X" % self.cksum return s def __str__(self): diff --git a/amoco/system/structs/__init__.py b/amoco/system/structs/__init__.py index e945be8..2151450 100644 --- a/amoco/system/structs/__init__.py +++ b/amoco/system/structs/__init__.py @@ -234,9 +234,25 @@ def __init__(self, fmt, **kargs): f_name = f_name.split('/') f_type = kargs.get(f_type, f_type) f_align = 0 - self.fields.append( - f_cls(f_type, f_count, f_name,f_order, f_align, f_comment) - ) + f = f_cls(f_type, f_count, f_name, f_order, f_align, f_comment) + # if this is a bitfield with only one subfield, we want to + # concatenate it with a previous bitfield if possible: + if f_cls in (BitField,BitFieldEx): + if len(f_count)==len(f_name)==1: + if len(self.fields)>0: + prev = self.fields[-1] + if isinstance(prev,(BitField,BitFieldEx)): + try: + prev.concat(f) + except TypeError: + # concat raises TypeError if prev is + # already "full" or if f would exceed + # its size... + pass + else: + # concat suceeded, so we don't append f + continue + self.fields.append(f) def __call__(self, cls): # Alltypes is a global dict located in structs.core module diff --git a/amoco/system/structs/core.py b/amoco/system/structs/core.py index d074b7c..df42b01 100644 --- a/amoco/system/structs/core.py +++ b/amoco/system/structs/core.py @@ -209,7 +209,8 @@ def offset_of(self, name, psize=0): return 0 o = 0 for f in self.fields: - o = f.align(o,psize) + if not self.packed: + o = f.align(o,psize) if f.name == name: return o o += f.size(psize) @@ -221,7 +222,8 @@ def offsets(self,psize=0): o = 0 offsets = [] for f in self.fields: - o = f.align(o,psize) + if not self.packed: + o = f.align(o,psize) if hasattr(f,'subsizes'): oo = 0 for x in f.subsizes: diff --git a/amoco/system/structs/fields.py b/amoco/system/structs/fields.py index 2c3b9b6..178c734 100644 --- a/amoco/system/structs/fields.py +++ b/amoco/system/structs/fields.py @@ -66,7 +66,7 @@ def type(self): try: cls = Alltypes[self.typename] except (KeyError): - logger.verbose("type %s is not defined"%self.typename) + logger.warning("type %s is not defined"%self.typename) else: self.__type = cls return self.__type @@ -84,10 +84,15 @@ def size(self,psize=0): # we return the actual byte-length of the resulting struct: try: return len(self.instance[self.name]) - except: + except Exception: + pass # otherwise we return the natural size of the field's type, # which may be infinite if the type contains a VarField... + try: sz = self.type.size(psize) + except AttributeError: + return float("Infinity") + else: if self.count > 0: sz = sz * self.count return sz @@ -188,7 +193,7 @@ def __call__(self): def __repr__(self): try: fmt = self.type.format() - except KeyError: + except (AttributeError,KeyError): fmt = "?" r = " 0: @@ -222,7 +227,7 @@ def format(self): def align_value(self,psize=0): tn = self.typename - if psize and tn in ('P','L','l'): + if psize and (tn in ('P','L','l')): tn = {4:'I',8:'Q',32:'I',64:'Q'}.get(psize,tn) sz = struct.calcsize(tn) return sz @@ -276,6 +281,10 @@ class BitField(RawField): A BitField is a 0-count RawField with additional subnames and subsizes to allow unpack the type into several named values each of given bit sizes. + Note that The order of subfields in a BitField **always** goes from LSB to MSB. + Subfields are unpacked once the BitField type has been unpacked according to + its own endianness indicator. + Arguments: - The ftype argument is the one that gets "splitted" into parts of bits. - The fcount argument is a list that defines the size of each splitted part @@ -325,11 +334,23 @@ def copy(self,obj=None): newf.instance = obj return newf + def concat(self,other): + oss = other.subsizes[:] + osn = other.subnames[:] + while oss and (self.size()*8>=sum(self.subsizes,oss[0])): + self.subsizes.append(oss.pop(0)) + if osn: + self.subnames.append(osn.pop(0)) + if oss or osn: + logger.debug("BitField size too small in %s"%self) + raise TypeError + def __repr__(self): fmt = self.typename - r = "" % str(["%s:%s"%(n,s) for n,s in zip(self.subnames, - self.subsizes)]) - return r + pre = " "*7 + f = [pre+"%s:%s"%(n,s) for n,s in zip(self.subnames,self.subsizes)] + s = '\n'.join(f).strip() + return "" % s class BitFieldEx(Field): """ @@ -560,14 +581,20 @@ def unpack(self, data, offset=0, psize=0): self.fcount = self.count # ...before overwritting with actual value: self.count = nb - # now fully unpack the whole field: - res = struct.unpack( - self.order + self.format(psize), data[offset : offset + self.size(psize)] - ) - if self.count == 0 or self.typename == "s": + if self.count == 0: + res = [0,b""] + else: + # now fully unpack the whole field: + res = struct.unpack( + self.order + self.format(psize), + data[offset : offset + self.size(psize)] + ) + if self.typename == "s": return res[1] if self.typename == "c": return b"".join(res[1:]) + if self.count == 0: + return None return res[1:] def pack(self, value, psize=0): diff --git a/amoco/system/structs/formatters.py b/amoco/system/structs/formatters.py index 822bdac..4f4bc50 100644 --- a/amoco/system/structs/formatters.py +++ b/amoco/system/structs/formatters.py @@ -106,6 +106,25 @@ def token_address_fmt(k, x, cls=None, fmt=None): """ return highlight([(Token.Address, hex(x))],fmt) +def token_version_fmt(k, x, cls=None, fmt=None): + """The address formatter prints value 'x' of attribute 'k' + as an hexadecimal string token value + """ + l = [] + while x: + l.append("%d"%(x&0xff)) + x = x>>8 + return highlight([(Token.String, '.'.join(l))],fmt) + +def token_bytes_fmt(k, x, cls=None, fmt=None): + """The address formatter prints value 'x' of attribute 'k' + as an hexadecimal string token value + """ + l = [] + while x: + l.append("%02X"%(x&0xff)) + x = x>>8 + return highlight([(Token.String, ' '.join(l))],fmt) def token_constant_fmt(k, x, cls=None, fmt=None): """The constant formatter prints value 'x' of attribute 'k' @@ -211,6 +230,8 @@ def strkey(self, k, cname, ksz=20, formatter=None): val = getattr(self._v, k) if isinstance(val,StructFormatter): val = val.pp__(formatter) + elif isinstance(val,list): + val = "\n".join((e.pp__(formatter) for e in val)) result = self.fkeys[k](k, val, cls=cname,fmt=formatter) else: result = "None" @@ -221,12 +242,15 @@ def pp__(self,fmt=None): ksz = max((len(f.name) for f in self.fields)) s = [] for f in self.fields: - if f.name: + if f.name and f.name!='_': fs = self.strkey(f.name, cname, ksz, fmt) if fs.count("\n") > 0: fs = fs.replace("\n", "\n " + " " * ksz) elif hasattr(f,'subnames'): - fs = "\n".join([self.strkey(n,cname,ksz,fmt) for n in f.subnames]) + subn = filter(lambda n:n!='_', f.subnames) + fs = "\n".join((self.strkey(n,cname,ksz,fmt) for n in subn)) + else: + continue s.append(fs) s = "\n".join(s) return "[%s]\n%s" % (self.__class__.__name__, s) diff --git a/amoco/ui/app.py b/amoco/ui/app.py index ab08912..3081a00 100644 --- a/amoco/ui/app.py +++ b/amoco/ui/app.py @@ -60,8 +60,8 @@ def spawn_console(ctx,exec_lines=None): if c.UI.console.lower() == "ipython": try: from IPython import start_ipython - except ImportError: - if conf.VERBOSE: + except (ImportError,ModuleNotFoundError): + if c.Log.level=="VERBOSE": click.echo("ipython not found") c.UI.console = "python" else: @@ -81,7 +81,7 @@ def spawn_console(ctx,exec_lines=None): readline.set_completer(rlcompleter.Completer(cvars).complete) readline.parse_and_bind("Tab: complete") del readline, rlcompleter - except ImportError: + except (ImportError,ModuleNotFoundError): click.echo("readline not found") ic = InteractiveConsole(cvars) ic.push("print(amoco.conf.BANNER)") @@ -172,7 +172,7 @@ def load_program(ctx, filename, gui): @cli.command("bin_info") @click.argument("filename", nargs=1, type=click.Path(exists=True, dir_okay=False)) -@click.option("--header", is_flag=True, default=False, help="show ELF/PE/Mach-O header") +@click.option("--header", is_flag=True, default=False, help="show executable format (ELF/PE/Mach-O/...) header") @click.pass_context def bin_info(ctx, filename, header): p = amoco.load_program(filename) diff --git a/amoco/ui/graphics/qt_/taskwin.py b/amoco/ui/graphics/qt_/taskwin.py index 96623c7..8740587 100644 --- a/amoco/ui/graphics/qt_/taskwin.py +++ b/amoco/ui/graphics/qt_/taskwin.py @@ -47,9 +47,9 @@ def createDockBin(self,task): # TreeView for ELF/PE/Mach-O structure dock = QDockWidget("[task].view.obj.binfmt", self) #dock.setAllowedAreas(Qt.TopDockWidgetArea) - dock.setFeatures(dock.DockWidgetClosable|\ - dock.DockWidgetMovable|\ - dock.DockWidgetFloatable) + dock.setFeatures(dock.DockWidgetFeature.DockWidgetClosable|\ + dock.DockWidgetFeature.DockWidgetMovable|\ + dock.DockWidgetFeature.DockWidgetFloatable) dock.setMinimumWidth(364) self.addDockWidget(Qt.LeftDockWidgetArea, dock) a = dock.toggleViewAction() @@ -66,9 +66,9 @@ def createDockBin(self,task): def createDockInfo(self,task): dock = QDockWidget("[task].view.obj.info", self) - dock.setFeatures(dock.DockWidgetClosable|\ - dock.DockWidgetMovable|\ - dock.DockWidgetFloatable) + dock.setFeatures(dock.DockWidgetFeature.DockWidgetClosable|\ + dock.DockWidgetFeature.DockWidgetMovable|\ + dock.DockWidgetFeature.DockWidgetFloatable) self.addDockWidget(Qt.RightDockWidgetArea, dock) a = dock.toggleViewAction() self.viewMenu.addAction(a) diff --git a/doc/conf.py b/doc/conf.py index bf45b79..78c064d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -276,8 +276,8 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'python': ('https://docs.python.org/', None), - 'grandalf':('https://grandalf.readthedocs.io/',None), - 'ccrawl':('https://ccrawl.readthedocs.io/',None), + 'grandalf':('https://grandalf.readthedocs.io/en/latest/',None), + 'ccrawl':('https://ccrawl.readthedocs.io/en/latest/',None), } autodoc_member_order = 'bysource' diff --git a/setup.py b/setup.py index a8f4b3a..a580274 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ setup( name = 'amoco', - version = '2.9.8', + version = '2.9.9', description = 'yet another binary analysis framework', long_description = long_descr, # Metadata @@ -52,7 +52,7 @@ url = 'https://github.com/bdcht/amoco', setup_requires=['pytest-runner',], tests_require=['pytest',], - install_requires = ['grandalf>=0.7', + install_requires = ['grandalf>=0.8', 'crysp>=1.2', 'pyparsing', 'traitlets', @@ -67,7 +67,7 @@ 'pygments', 'z3-solver', 'tqdm', - 'ccrawl>=1.6', + 'ccrawl>=1.9', 'PySide6', 'IPython', 'prompt_toolkit>=3.0.28'], diff --git a/tests/test_arch_ppc.py b/tests/test_arch_ppc.py new file mode 100644 index 0000000..8aee3fd --- /dev/null +++ b/tests/test_arch_ppc.py @@ -0,0 +1,29 @@ +import pytest + +from amoco.config import conf +conf.UI.formatter = 'Null' +conf.Cas.unicode = False +conf.UI.unicode = False + +from amoco.arch.ppc32 import cpu + +def test_decoder_START(): + c = b'\x91\x00\x00\xf8' + i = cpu.disassemble(c) + assert i.mnemonic=='stw' + assert i.operands[0] is cpu.R[8] + assert i.operands[1]._is_ptr + assert str(i.operands[1]) == '(0xf8)' + c = b'\xd9\xff\x14\x02' + i = cpu.disassemble(c) + assert i.mnemonic=="stfd" + assert i.operands[0] is cpu.FPR[15] + assert i.operands[1].base == cpu.R[31] + assert i.operands[1].disp == 20488 + c = b'\x7b\xd0\x38\xf1' + i = cpu.disassemble(c) + assert i.mnemonic=="rldcl" + assert i.operands[0] is cpu.R[16] + assert i.operands[1] is cpu.R[30] + assert i.operands[2] is cpu.R[7] + assert i.operands[3] == 0x23 diff --git a/tests/test_system_structs.py b/tests/test_system_structs.py index b5a1f04..1348b66 100644 --- a/tests/test_system_structs.py +++ b/tests/test_system_structs.py @@ -191,8 +191,7 @@ def test_bitfield1(): f = BitField('B',fcount=[2,4,1,1],fname=['a','b','c','d']) assert f.format()=='B' assert f.size()==1 - assert f.name is None - assert str(f)=="" + assert f.name is '' v = f.unpack(b"\x93") # values are splitted from low to high order bits... assert v['a'] == 3 @@ -204,8 +203,7 @@ def test_bitfield2(): f = BitField('H',fcount=[2,4,1,1],fname=['a','b','c','d']) assert f.format()=='H' assert f.size()==2 - assert f.name is None - assert str(f)=="" + assert f.name is '' v = f.unpack(b"\x40\x93") # values are splitted from low to high order bits... assert v['a'] == 0