GP-2480 Add sleigh compiler support for inst_next2

This commit is contained in:
ghidra1 2022-08-18 17:27:39 -04:00
parent 39baf3a691
commit 8d4a6c213e
84 changed files with 3623 additions and 2807 deletions

View file

@ -105,14 +105,14 @@ public class DebuggerStateEditingPluginIntegrationTest extends AbstractGhidraHea
assertTrue(
helper.patchInstructionAction.isAddToPopup(listingProvider.getActionContext(null)));
Instruction ins =
helper.patchInstructionAt(tb.addr(0x00400123), "imm r0,#0x0", "imm r0,#1234");
helper.patchInstructionAt(tb.addr(0x00400123), "imm r0,#0x0", "imm r0,#0x3d2");
assertEquals(2, ins.getLength());
long snap = traceManager.getCurrent().getViewSnap();
assertTrue(DBTraceUtils.isScratch(snap));
byte[] bytes = new byte[2];
view.getMemory().getBytes(tb.addr(0x00400123), bytes);
assertArrayEquals(tb.arr(0x40, 1234), bytes);
assertArrayEquals(tb.arr(0x30, 0xd2), bytes);
}
@Test

View file

@ -86,8 +86,8 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
iit = asm.assemble(start,
"imm r0, #1234",
"imm r1, #2045"); // 11 bits unsigned
"imm r0, #0x3d2",
"imm r1, #911"); // 10 bits unsigned
}
imm1234 = iit.next();

View file

@ -309,7 +309,7 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes
"pc = 0x00400000;",
"sp = 0x00110000;"),
List.of(
"imm r0, #1234")); // decimal
"imm r0, #911")); // decimal
TracePcodeEmulator emu = new TracePcodeEmulator(tb.trace, 0);
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
@ -324,7 +324,7 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes
TraceSleighUtils.evaluate("sp", tb.trace, 1, thread, 0));
assertEquals(BigInteger.valueOf(0x00400002),
TraceSleighUtils.evaluate("pc", tb.trace, 1, thread, 0));
assertEquals(BigInteger.valueOf(1234),
assertEquals(BigInteger.valueOf(911),
TraceSleighUtils.evaluate("r0", tb.trace, 1, thread, 0));
}
}
@ -341,9 +341,9 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes
"sp = 0x00110000;"),
List.of(
"brds 0x00400006",
"imm r0, #1234", // decimal
"imm r0, #2020",
"imm r1, #2021"));
"imm r0, #911", // decimal
"imm r0, #860",
"imm r1, #861"));
TracePcodeEmulator emu = new TracePcodeEmulator(tb.trace, 0);
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
@ -357,9 +357,9 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes
assertEquals(BigInteger.valueOf(0x00400008),
TraceSleighUtils.evaluate("pc", tb.trace, 1, thread, 0));
assertEquals(BigInteger.valueOf(1234),
assertEquals(BigInteger.valueOf(911),
TraceSleighUtils.evaluate("r0", tb.trace, 1, thread, 0));
assertEquals(BigInteger.valueOf(2021),
assertEquals(BigInteger.valueOf(861),
TraceSleighUtils.evaluate("r1", tb.trace, 1, thread, 0));
}
}

View file

@ -176,6 +176,9 @@ public abstract class AbstractPcodeFormatter<T, A extends Appender<T>>
else if (offset.getType() == ConstTpl.J_NEXT) {
appender.appendLabel("inst_next");
}
else if (offset.getType() == ConstTpl.J_NEXT2) {
appender.appendLabel("inst_next2");
}
else {
formatAddress(appender, null, offset, size);
}

View file

@ -494,6 +494,27 @@ public class ToyProgramBuilder extends ProgramBuilder {
addInstructionWords(address, (short) (0xe000 | (relDest << 4))); // breq rel
}
/**
* Add conditional skip (consumes 2-bytes)
* @param offset instruction address offset
* @throws MemoryAccessException
*/
public void addBytesSkipConditional(long offset)
throws MemoryAccessException {
addBytesSkipConditional(toHex(offset));
}
/**
* Add conditional skip (consumes 2-bytes)
* @param addr instruction address
* @throws MemoryAccessException
*/
public void addBytesSkipConditional(String addr)
throws MemoryAccessException {
Address address = addr(addr);
addInstructionWords(address, (short) (0x8000)); // skeq
}
/**
* Add branch w/ delay slot (consumes 4-bytes)
* @param offset instruction address offset

View file

@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.assembler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import org.junit.*;
@ -90,8 +90,8 @@ public class AssemblerPluginTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testActionPatchInstructionNoExisting() throws Exception {
Address address = space.getAddress(0x00400000);
Instruction ins = helper.patchInstructionAt(address, "", "imm r0, #1234");
assertEquals("imm r0,#0x4d2", ins.toString());
Instruction ins = helper.patchInstructionAt(address, "", "imm r0, #911");
assertEquals("imm r0,#0x38f", ins.toString());
}
@Test
@ -99,11 +99,11 @@ public class AssemblerPluginTest extends AbstractGhidraHeadedIntegrationTest {
Address address = space.getAddress(0x00400000);
Assembler asm = Assemblers.getAssembler(program);
try (ProgramTransaction trans = ProgramTransaction.open(program, "Assemble pre-existing")) {
asm.assemble(address, "imm r0,#0x4d2");
asm.assemble(address, "imm r0,#0x3d2");
trans.commit();
}
Instruction ins = helper.patchInstructionAt(address, "imm r0,#0x4d2", "imm r0, #123");
Instruction ins = helper.patchInstructionAt(address, "imm r0,#0x3d2", "imm r0, #123");
assertEquals("imm r0,#0x7b", ins.toString());
}

View file

@ -106,6 +106,7 @@ public:
void applyCommits(void);
const Address &getAddr(void) const { return addr; }
const Address &getNaddr(void) const { return naddr; }
const Address &getN2addr(void) const { return naddr; /* inst_next2 not supported */ }
const Address &getDestAddr(void) const { return calladdr; }
const Address &getRefAddr(void) const { return calladdr; }
AddrSpace *getCurSpace(void) const { return addr.getSpace(); }
@ -147,6 +148,7 @@ public:
AddrSpace *getConstSpace(void) const { return const_context->getConstSpace(); }
const Address &getAddr(void) const { if (cross_context != (const ParserContext *)0) { return cross_context->getAddr(); } return const_context->getAddr(); }
const Address &getNaddr(void) const { if (cross_context != (const ParserContext *)0) { return cross_context->getNaddr();} return const_context->getNaddr(); }
const Address &getN2addr(void) const { if (cross_context != (const ParserContext *)0) { return cross_context->getN2addr();} return const_context->getN2addr(); }
const Address &getRefAddr(void) const { if (cross_context != (const ParserContext *)0) { return cross_context->getRefAddr();} return const_context->getRefAddr(); }
const Address &getDestAddr(void) const { if (cross_context != (const ParserContext *)0) { return cross_context->getDestAddr();} return const_context->getDestAddr(); }
int4 getLength(void) const { return const_context->getLength(); }

View file

@ -791,7 +791,7 @@ void Funcdata::doLiveInject(InjectPayload *payload,const Address &addr,BlockBasi
emitter.setFuncdata(this);
context.clear();
context.baseaddr = addr; // Shouldn't be using inst_next and inst_start here
context.baseaddr = addr; // Shouldn't be using inst_next, inst_next2 or inst_start here
context.nextaddr = addr;
list<PcodeOp *>::const_iterator deaditer = obank.endDead();

View file

@ -69,6 +69,7 @@ public:
/// concrete Varnodes. This class contains the context dependent data to resolve:
/// - inst_start -- the address where the injection occurs
/// - inst_next -- the address of the instruction following (the instruction being injected)
/// - inst_next2 -- the address of the instruction after the next instruction (Not Supported)
/// - inst_dest -- Original destination of CALL being injected
/// - inst_ref -- Target of reference on injected instruction
/// - \<input> -- Input Varnode of the injection referenced by name

File diff suppressed because it is too large Load diff

View file

@ -38,6 +38,7 @@
LabelSymbol *labelsym;
StartSymbol *startsym;
EndSymbol *endsym;
Next2Symbol *next2sym;
OperandSymbol *operandsym;
VarnodeSymbol *varsym;
SpecificSymbol *specsym;
@ -76,6 +77,7 @@
%token <operandsym> OPERANDSYM
%token <startsym> STARTSYM
%token <endsym> ENDSYM
%token <next2sym> NEXT2SYM
%token <labelsym> LABELSYM
%type <param> paramlist
@ -195,6 +197,7 @@ sizedstar: '*' '[' SPACESYM ']' ':' INTEGER { $$ = new StarQuality; $$->size = *
;
jumpdest: STARTSYM { VarnodeTpl *sym = $1->getVarnode(); $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),sym->getOffset(),ConstTpl(ConstTpl::j_curspace_size)); delete sym; }
| ENDSYM { VarnodeTpl *sym = $1->getVarnode(); $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),sym->getOffset(),ConstTpl(ConstTpl::j_curspace_size)); delete sym; }
| NEXT2SYM { VarnodeTpl *sym = $1->getVarnode(); $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),sym->getOffset(),ConstTpl(ConstTpl::j_curspace_size)); delete sym; }
| INTEGER { $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),ConstTpl(ConstTpl::real,*$1),ConstTpl(ConstTpl::j_curspace_size)); delete $1; }
| BADINTEGER { $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),ConstTpl(ConstTpl::real,0),ConstTpl(ConstTpl::j_curspace_size)); yyerror("Parsed integer is too big (overflow)"); }
| INTEGER '[' SPACESYM ']' { AddrSpace *spc = $3->getSpace(); $$ = new VarnodeTpl(ConstTpl(spc),ConstTpl(ConstTpl::real,*$1),ConstTpl(ConstTpl::real,spc->getAddrSize())); delete $1; }
@ -221,6 +224,7 @@ specificsymbol: VARSYM { $$ = $1; }
| OPERANDSYM { $$ = $1; }
| STARTSYM { $$ = $1; }
| ENDSYM { $$ = $1; }
| NEXT2SYM { $$ = $1; }
;
paramlist: /* EMPTY */ { $$ = new vector<ExprTree *>; }
| expr { $$ = new vector<ExprTree *>; $$->push_back($1); }
@ -749,6 +753,9 @@ int4 PcodeSnippet::lex(void)
case SleighSymbol::end_symbol:
yylval.endsym = (EndSymbol *)sym;
return ENDSYM;
case SleighSymbol::next2_symbol:
yylval.next2sym = (Next2Symbol *)sym;
return NEXT2SYM;
case SleighSymbol::label_symbol:
yylval.labelsym = (LabelSymbol *)sym;
return LABELSYM;

View file

@ -121,6 +121,8 @@ uintb ConstTpl::fix(const ParserWalker &walker) const
return walker.getAddr().getOffset(); // Fill in starting address placeholder with real address
case j_next:
return walker.getNaddr().getOffset(); // Fill in next address placeholder with real address
case j_next2:
return walker.getN2addr().getOffset(); // Fill in next2 address placeholder with real address
case j_flowref:
return walker.getRefAddr().getOffset();
case j_flowref_size:
@ -349,6 +351,9 @@ void ConstTpl::saveXml(ostream &s) const
case j_next:
s << "next\"/>";
break;
case j_next2:
s << "next2\"/>";
break;
case j_curspace:
s << "curspace\"/>";
break;
@ -404,6 +409,9 @@ void ConstTpl::restoreXml(const Element *el,const AddrSpaceManager *manage)
else if (typestring=="next") {
type = j_next;
}
else if (typestring=="next2") {
type = j_next2;
}
else if (typestring=="curspace") {
type = j_curspace;
}

View file

@ -30,9 +30,9 @@ class Translate; // Forward declaration
class HandleTpl; // Forward declaration
class ConstTpl {
public:
enum const_type { real=0, handle=1, j_start=2, j_next=3, j_curspace=4,
j_curspace_size=5, spaceid=6, j_relative=7,
j_flowref=8, j_flowref_size=9, j_flowdest=10, j_flowdest_size=11 };
enum const_type { real=0, handle=1, j_start=2, j_next=3, j_next2=4, j_curspace=5,
j_curspace_size=6, spaceid=7, j_relative=8,
j_flowref=9, j_flowref_size=10, j_flowdest=11, j_flowdest_size=12 };
enum v_field { v_space=0, v_offset=1, v_size=2, v_offset_plus=3 };
private:
const_type type;

View file

@ -1789,7 +1789,7 @@ SleighCompile::SleighCompile(void)
}
/// Create the address spaces: \b const, \b unique, and \b other.
/// Define the special symbols: \b inst_start, \b inst_next, \b epsilon.
/// Define the special symbols: \b inst_start, \b inst_next, \b inst_next2, \b epsilon.
/// Define the root subtable symbol: \b instruction
void SleighCompile::predefinedSymbols(void)
@ -1813,6 +1813,8 @@ void SleighCompile::predefinedSymbols(void)
symtab.addSymbol(startsym);
EndSymbol *endsym = new EndSymbol("inst_next",getConstantSpace());
symtab.addSymbol(endsym);
Next2Symbol *next2sym = new Next2Symbol("inst_next2",getConstantSpace());
symtab.addSymbol(next2sym);
EpsilonSymbol *epsilon = new EpsilonSymbol("epsilon",getConstantSpace());
symtab.addSymbol(epsilon);
pcode.setConstantSpace(getConstantSpace());
@ -2907,20 +2909,23 @@ ConstructTpl *SleighCompile::setResultStarVarnode(ConstructTpl *ct,StarQuality *
/// The new change operation is added to the current list.
/// When executed, the change operation will assign a new value to the given context variable
/// using the specified expression. The change only applies within the parsing of a single instruction.
/// Because we are in the middle of parsing, the \b inst_next value has not been computed yet
/// So we check to make sure the value expression doesn't use this symbol.
/// Because we are in the middle of parsing, the \b inst_next and \b inst_next2 values have not
/// been computed yet. So we check to make sure the value expression doesn't use this symbol.
/// \param vec is the current list of change operations
/// \param sym is the given context variable affected by the operation
/// \param pe is the specified expression
/// \return \b true if the expression does not use the \b inst_next symbol
/// \return \b true if the expression does not use the \b inst_next or \b inst_next2 symbol
bool SleighCompile::contextMod(vector<ContextChange *> *vec,ContextSymbol *sym,PatternExpression *pe)
{
vector<const PatternValue *> vallist;
pe->listValues(vallist);
for(uint4 i=0;i<vallist.size();++i)
for(uint4 i=0;i<vallist.size();++i) {
if (dynamic_cast<const EndInstructionValue *>(vallist[i]) != (const EndInstructionValue *)0)
return false;
if (dynamic_cast<const Next2InstructionValue *>(vallist[i]) != (const Next2InstructionValue *)0)
return false;
}
// Otherwise we generate a "temporary" change to context instruction (ContextOp)
ContextField *field = (ContextField *)sym->getPatternValue();
ContextOp *op = new ContextOp(field->getStartBit(),field->getEndBit(),pe);

File diff suppressed because it is too large Load diff

View file

@ -45,8 +45,8 @@
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
#ifndef YY_YY_SRC_DECOMPILE_CPP_SLGHPARSE_HH_INCLUDED
# define YY_YY_SRC_DECOMPILE_CPP_SLGHPARSE_HH_INCLUDED
#ifndef YY_YY_SLGHPARSE_HH_INCLUDED
# define YY_YY_SLGHPARSE_HH_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 0
@ -168,9 +168,10 @@ extern int yydebug;
OPERANDSYM = 363,
STARTSYM = 364,
ENDSYM = 365,
MACROSYM = 366,
LABELSYM = 367,
SUBTABLESYM = 368
NEXT2SYM = 366,
MACROSYM = 367,
LABELSYM = 368,
SUBTABLESYM = 369
};
#endif
@ -179,7 +180,7 @@ extern int yydebug;
union YYSTYPE
{
#line 29 "src/decompile/cpp/slghparse.y" /* yacc.c:1909 */
#line 29 "slghparse.y" /* yacc.c:1909 */
char ch;
uintb *i;
@ -212,6 +213,7 @@ union YYSTYPE
SubtableSymbol *subtablesym;
StartSymbol *startsym;
EndSymbol *endsym;
Next2Symbol *next2sym;
OperandSymbol *operandsym;
VarnodeListSymbol *varlistsym;
VarnodeSymbol *varsym;
@ -223,7 +225,7 @@ union YYSTYPE
FamilySymbol *famsym;
SpecificSymbol *specsym;
#line 212 "src/decompile/cpp/slghparse.hh" /* yacc.c:1909 */
#line 214 "slghparse.hh" /* yacc.c:1909 */
};
typedef union YYSTYPE YYSTYPE;
@ -236,4 +238,4 @@ extern YYSTYPE yylval;
int yyparse (void);
#endif /* !YY_YY_SRC_DECOMPILE_CPP_SLGHPARSE_HH_INCLUDED */
#endif /* !YY_YY_SLGHPARSE_HH_INCLUDED */

View file

@ -58,6 +58,7 @@
SubtableSymbol *subtablesym;
StartSymbol *startsym;
EndSymbol *endsym;
Next2Symbol *next2sym;
OperandSymbol *operandsym;
VarnodeListSymbol *varlistsym;
VarnodeSymbol *varsym;
@ -121,6 +122,7 @@
%token <operandsym> OPERANDSYM
%token <startsym> STARTSYM
%token <endsym> ENDSYM
%token <next2sym> NEXT2SYM
%token <macrosym> MACROSYM
%token <labelsym> LABELSYM
%token <subtablesym> SUBTABLESYM
@ -331,7 +333,7 @@ contextblock: { $$ = (vector<ContextChange *> *)0; }
| '[' contextlist ']' { $$ = $2; }
;
contextlist: { $$ = new vector<ContextChange *>; }
| contextlist CONTEXTSYM '=' pexpression ';' { $$ = $1; if (!slgh->contextMod($1,$2,$4)) { string errmsg="Cannot use 'inst_next' to set context variable: "+$2->getName(); yyerror(errmsg.c_str()); YYERROR; } }
| contextlist CONTEXTSYM '=' pexpression ';' { $$ = $1; if (!slgh->contextMod($1,$2,$4)) { string errmsg="Cannot use 'inst_next' or 'inst_next2' to set context variable: "+$2->getName(); yyerror(errmsg.c_str()); YYERROR; } }
| contextlist GLOBALSET_KEY '(' familysymbol ',' CONTEXTSYM ')' ';' { $$ = $1; slgh->contextSet($1,$4,$6); }
| contextlist GLOBALSET_KEY '(' specificsymbol ',' CONTEXTSYM ')' ';' { $$ = $1; slgh->contextSet($1,$4,$6); }
| contextlist OPERANDSYM '=' pexpression ';' { $$ = $1; slgh->defineOperand($2,$4); }
@ -456,6 +458,7 @@ sizedstar: '*' '[' SPACESYM ']' ':' INTEGER { $$ = new StarQuality; $$->size = *
;
jumpdest: STARTSYM { VarnodeTpl *sym = $1->getVarnode(); $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),sym->getOffset(),ConstTpl(ConstTpl::j_curspace_size)); delete sym; }
| ENDSYM { VarnodeTpl *sym = $1->getVarnode(); $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),sym->getOffset(),ConstTpl(ConstTpl::j_curspace_size)); delete sym; }
| NEXT2SYM { VarnodeTpl *sym = $1->getVarnode(); $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),sym->getOffset(),ConstTpl(ConstTpl::j_curspace_size)); delete sym; }
| INTEGER { $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),ConstTpl(ConstTpl::real,*$1),ConstTpl(ConstTpl::j_curspace_size)); delete $1; }
| BADINTEGER { $$ = new VarnodeTpl(ConstTpl(ConstTpl::j_curspace),ConstTpl(ConstTpl::real,0),ConstTpl(ConstTpl::j_curspace_size)); yyerror("Parsed integer is too big (overflow)"); }
| OPERANDSYM { $$ = $1->getVarnode(); $1->setCodeAddress(); }
@ -499,6 +502,7 @@ specificsymbol: VARSYM { $$ = $1; }
| OPERANDSYM { $$ = $1; }
| STARTSYM { $$ = $1; }
| ENDSYM { $$ = $1; }
| NEXT2SYM { $$ = $1; }
;
charstring: CHAR { $$ = new string; (*$$) += $1; }
| charstring CHAR { $$ = $1; (*$$) += $2; }
@ -573,6 +577,7 @@ anysymbol: SPACESYM { $$ = $1; }
| OPERANDSYM { $$ = $1; }
| STARTSYM { $$ = $1; }
| ENDSYM { $$ = $1; }
| NEXT2SYM { $$ = $1; }
| BITSYM { $$ = $1; }
;
%%

View file

@ -165,6 +165,19 @@ public:
virtual void restoreXml(const Element *el,Translate *trans) {}
};
class Next2InstructionValue : public PatternValue {
public:
Next2InstructionValue(void) {}
virtual intb getValue(ParserWalker &walker) const {
return (intb)AddrSpace::byteToAddress(walker.getN2addr().getOffset(),walker.getN2addr().getSpace()->getWordSize()); }
virtual TokenPattern genMinPattern(const vector<TokenPattern> &ops) const { return TokenPattern(); }
virtual TokenPattern genPattern(intb val) const { return TokenPattern(); }
virtual intb minValue(void) const { return (intb)0; }
virtual intb maxValue(void) const { return (intb)0; }
virtual void saveXml(ostream &s) const { s << "<next2_exp/>"; }
virtual void restoreXml(const Element *el,Translate *trans) {}
};
class Constructor; // Forward declaration
class OperandSymbol;
class OperandValue : public PatternValue {

File diff suppressed because it is too large Load diff

View file

@ -429,6 +429,9 @@ int4 find_symbol(void) {
case SleighSymbol::end_symbol:
yylval.endsym = (EndSymbol *)sym;
return ENDSYM;
case SleighSymbol::next2_symbol:
yylval.next2sym = (Next2Symbol *)sym;
return NEXT2SYM;
case SleighSymbol::subtable_symbol:
yylval.subtablesym = (SubtableSymbol *)sym;
return SUBTABLESYM;

View file

@ -252,6 +252,8 @@ void SymbolTable::restoreSymbolHeader(const Element *el)
sym = new StartSymbol();
else if (el->getName() == "end_sym_head")
sym = new EndSymbol();
else if (el->getName() == "next2_sym_head")
sym = new Next2Symbol();
else if (el->getName() == "subtable_sym_head")
sym = new SubtableSymbol();
else if (el->getName() == "flowdest_sym_head")
@ -1254,6 +1256,70 @@ void EndSymbol::restoreXml(const Element *el,SleighBase *trans)
patexp->layClaim();
}
Next2Symbol::Next2Symbol(const string &nm,AddrSpace *cspc) : SpecificSymbol(nm)
{
const_space = cspc;
patexp = new Next2InstructionValue();
patexp->layClaim();
}
Next2Symbol::~Next2Symbol(void)
{
if (patexp != (PatternExpression *)0)
PatternExpression::release(patexp);
}
VarnodeTpl *Next2Symbol::getVarnode(void) const
{ // Return instruction offset after next instruction offset as a constant
ConstTpl spc(const_space);
ConstTpl off(ConstTpl::j_next2);
ConstTpl sz_zero;
return new VarnodeTpl(spc,off,sz_zero);
}
void Next2Symbol::getFixedHandle(FixedHandle &hand,ParserWalker &walker) const
{
hand.space = walker.getCurSpace();
hand.offset_space = (AddrSpace *)0;
hand.offset_offset = walker.getN2addr().getOffset(); // Get instruction address after next instruction
hand.size = hand.space->getAddrSize();
}
void Next2Symbol::print(ostream &s,ParserWalker &walker) const
{
intb val = (intb) walker.getN2addr().getOffset();
s << "0x" << hex << val;
}
void Next2Symbol::saveXml(ostream &s) const
{
s << "<next2_sym";
SleighSymbol::saveXmlHeader(s);
s << "/>\n";
}
void Next2Symbol::saveXmlHeader(ostream &s) const
{
s << "<next2_sym_head";
SleighSymbol::saveXmlHeader(s);
s << "/>\n";
}
void Next2Symbol::restoreXml(const Element *el,SleighBase *trans)
{
const_space = trans->getConstantSpace();
patexp = new Next2InstructionValue();
patexp->layClaim();
}
FlowDestSymbol::FlowDestSymbol(const string &nm,AddrSpace *cspc) : SpecificSymbol(nm)
{

View file

@ -25,7 +25,7 @@ class SleighSymbol {
public:
enum symbol_type { space_symbol, token_symbol, userop_symbol, value_symbol, valuemap_symbol,
name_symbol, varnode_symbol, varnodelist_symbol, operand_symbol,
start_symbol, end_symbol, subtable_symbol, macro_symbol, section_symbol,
start_symbol, end_symbol, next2_symbol, subtable_symbol, macro_symbol, section_symbol,
bitrange_symbol, context_symbol, epsilon_symbol, label_symbol,
dummy_symbol };
private:
@ -391,6 +391,23 @@ public:
virtual void restoreXml(const Element *el,SleighBase *trans);
};
class Next2Symbol : public SpecificSymbol {
AddrSpace *const_space;
PatternExpression *patexp;
public:
Next2Symbol(void) { patexp = (PatternExpression *)0; } // For use with restoreXml
Next2Symbol(const string &nm,AddrSpace *cspc);
virtual ~Next2Symbol(void);
virtual VarnodeTpl *getVarnode(void) const;
virtual PatternExpression *getPatternExpression(void) const { return patexp; }
virtual void getFixedHandle(FixedHandle &hand,ParserWalker &walker) const;
virtual void print(ostream &s,ParserWalker &walker) const;
virtual symbol_type getType(void) const { return next2_symbol; }
virtual void saveXml(ostream &s) const;
virtual void saveXmlHeader(ostream &s) const;
virtual void restoreXml(const Element *el,SleighBase *trans);
};
class FlowDestSymbol : public SpecificSymbol {
AddrSpace *const_space;
public:

View file

@ -506,8 +506,8 @@ parameters in the actual p-code operation, or an exception will be thrown during
As with a <code>&lt;callfixup&gt;</code>, the <code>&lt;body&gt;</code> tag is fed straight to the SLEIGH semantic
parser. It can refer to registers via their symbolic name defined in SLEIGH, it can refer to the operator parameters
via their <code>&lt;input&gt;</code> or <code>&lt;output&gt;</code> names, and it can also refer to
<code>inst_start</code> and <code>inst_next</code> as addresses describing the instruction containing the
<code>CALLOTHER</code>.
<code>inst_start</code>, <code>inst_next</code> and <code>inst_next2</code> as addresses describing the instruction
containing the <code>CALLOTHER</code>.
</para>
<example>
<programlisting>

View file

@ -1006,6 +1006,10 @@ We list all of the symbols that are predefined by SLEIGH.
<td><code>inst_next</code></td>
<td>Offset of the address of the next instruction.</td>
</tr>
<tr>
<td><code>inst_next2</code></td>
<td>Offset of the address of the instruction after the next instruction.</td>
</tr>
<tr>
<td><code>epsilon</code></td>
<td>A special identifier indicating an empty bit pattern.</td>
@ -1019,7 +1023,8 @@ and <emphasis>inst_next</emphasis>. These are family symbols which map
in the context of particular instruction to the integer offset of
either the address of the instruction or the address of the next
instruction respectively. These are used in any relative branching
situation. The other symbols are rarely
situation. The <emphasis>inst_next2</emphasis> is intended for conditional
skip instruction situations. The remaining symbols are rarely
used. The <emphasis>const</emphasis> and <emphasis>unique</emphasis>
identifiers are address spaces. The <emphasis>epsilon</emphasis>
identifier is inherited from SLED and is a specific symbol equivalent
@ -2868,6 +2873,26 @@ the <emphasis>BRANCH</emphasis>, <emphasis>CBRANCH</emphasis>,
or <emphasis>CALL</emphasis> operation.
</para>
</sect4>
<sect4 id="sleigh_skip_instruction_branching">
<title>Skip Instruction Branching</title>
<para>>
Many processors have a conditional-skip-instruction which must branch over the next instruction
based upon some condition. The <emphasis>inst_next2</emphasis> symbol has been provided for
this purpose.
<informalexample>
<programlisting>
:skip.eq is opcode=10 {
if (zeroflag!=0) goto inst_next2;
}
</programlisting>
</informalexample>
</para>
<para>
In the example above, the branch address will be determined by adding the parsed-length of the next
instruction to the value of <emphasis>inst_next</emphasis> causing a branch over the next
instruction when the condition is satisfied.
</para>
</sect4>
<sect4 id="sleigh_bitrange_assign">
<title>Bit Range Assignments</title>
<para>
@ -3439,7 +3464,8 @@ by the condition.
<para>
Because the <emphasis role="bold">delayslot</emphasis> directive
combines two or more instructions into one, the meaning of the
symbol <emphasis>inst_next</emphasis> becomes ambiguous. It is not
symbol <emphasis>inst_next</emphasis> and <emphasis>inst_next2</emphasis>
become ambiguous. It is not
clear anymore what exactly the “next instruction” is. SLEIGH uses the
following conventions for interpreting
an <emphasis>inst_next</emphasis> symbol. If it is used in the
@ -3447,7 +3473,11 @@ semantic section, the symbol refers to the address of the instruction
after any instructions in the delay slot. However, if it is used in a
disassembly action, the <emphasis>inst_next</emphasis> symbol refers
to the address of the instruction immediately after the first
instruction, even if there is a delay slot.
instruction, even if there is a delay slot. The use of
the <emphasis>inst_next2</emphasis> symbol may be inappropriate in
conjunction with delayslot use. While the next instruction address
is identified by <emphasis>inst_next</emphasis>, the length of the
next instruction ignores any delayslots it may have.
</para>
</sect2>
</sect1>

View file

@ -349,9 +349,10 @@ specific_symbol[String purpose] returns [SpecificSymbol symbol]
: ^(OP_IDENTIFIER s=.) {
SleighSymbol sym = pcode.findSymbol($s.getText());
if (sym == null) {
unknownSymbolError($s.getText(), find($s), "start, end, operand, epsilon, or varnode", purpose);
unknownSymbolError($s.getText(), find($s), "start, end, next2, operand, epsilon, or varnode", purpose);
} else if(sym.getType() != symbol_type.start_symbol
&& sym.getType() != symbol_type.end_symbol
&& sym.getType() != symbol_type.next2_symbol
&& sym.getType() != symbol_type.operand_symbol
&& sym.getType() != symbol_type.epsilon_symbol
&& sym.getType() != symbol_type.varnode_symbol) {
@ -839,7 +840,7 @@ pattern_symbol[String purpose] returns [PatternExpression expr]
: ^(OP_IDENTIFIER s=.) {
SleighSymbol sym = sc.findSymbol($s.getText());
if (sym == null) {
unknownSymbolError($s.getText(), find($s), "start, end, operand, epsilon, or varnode", purpose);
unknownSymbolError($s.getText(), find($s), "start, end, next2, operand, epsilon, or varnode", purpose);
} else if(sym.getType() == symbol_type.operand_symbol) {
OperandSymbol os = (OperandSymbol) sym;
if (os.getDefiningSymbol() != null && os.getDefiningSymbol().getType() == symbol_type.subtable_symbol) {
@ -848,6 +849,7 @@ pattern_symbol[String purpose] returns [PatternExpression expr]
$expr = os.getPatternExpression();
} else if(sym.getType() == symbol_type.start_symbol
|| sym.getType() == symbol_type.end_symbol
|| sym.getType() == symbol_type.next2_symbol
|| sym.getType() == symbol_type.epsilon_symbol
|| sym.getType() == symbol_type.varnode_symbol) {
SpecificSymbol ss = (SpecificSymbol) sym;
@ -864,7 +866,7 @@ pattern_symbol[String purpose] returns [PatternExpression expr]
reportError(find($s), "Global symbol '" + sym.getName() + "' is not allowed in action expression");
}
} else {
wrongSymbolTypeError(sym, find($s), "start, end, operand, epsilon, or varnode", purpose);
wrongSymbolTypeError(sym, find($s), "start, end, next2, operand, epsilon, or varnode", purpose);
}
}
| t=OP_WILDCARD {
@ -877,9 +879,10 @@ pattern_symbol2[String purpose] returns [PatternExpression expr]
: ^(OP_IDENTIFIER s=.) {
SleighSymbol sym = sc.findSymbol($s.getText());
if (sym == null) {
unknownSymbolError($s.getText(), find($s), "start, end, operand, epsilon, or varnode", purpose);
unknownSymbolError($s.getText(), find($s), "start, end, next2, operand, epsilon, or varnode", purpose);
} else if(sym.getType() == symbol_type.start_symbol
|| sym.getType() == symbol_type.end_symbol
|| sym.getType() == symbol_type.next2_symbol
|| sym.getType() == symbol_type.operand_symbol
|| sym.getType() == symbol_type.epsilon_symbol
|| sym.getType() == symbol_type.varnode_symbol) {
@ -893,7 +896,7 @@ pattern_symbol2[String purpose] returns [PatternExpression expr]
FamilySymbol z = (FamilySymbol) sym;
$expr = z.getPatternValue();
} else {
wrongSymbolTypeError(sym, find($s), "start, end, operand, epsilon, or varnode", purpose);
wrongSymbolTypeError(sym, find($s), "start, end, next2, operand, epsilon, or varnode", purpose);
}
}
| t=OP_WILDCARD {
@ -922,7 +925,7 @@ cstatement[VectorSTL<ContextChange> r]
} else if(sym.getType() == symbol_type.context_symbol) {
ContextSymbol t = (ContextSymbol) sym;
if (!sc.contextMod(r, t, e)) {
reportError(find($id), "Cannot use 'inst_next' to set context variable: '" + t.getName() + "'");
reportError(find($id), "Cannot use 'inst_next' or 'inst_next2' to set context variable: '" + t.getName() + "'");
}
} else if(sym.getType() == symbol_type.operand_symbol) {
OperandSymbol t = (OperandSymbol) sym;
@ -950,6 +953,7 @@ cstatement[VectorSTL<ContextChange> r]
|| sym.getType() == symbol_type.varnodelist_symbol
|| sym.getType() == symbol_type.start_symbol
|| sym.getType() == symbol_type.end_symbol
|| sym.getType() == symbol_type.next2_symbol
|| sym.getType() == symbol_type.operand_symbol
|| sym.getType() == symbol_type.epsilon_symbol
|| sym.getType() == symbol_type.varnode_symbol) {
@ -1176,10 +1180,11 @@ assignment returns [VectorSTL<OpTpl> value]
$value = pcode.newOutput(find(id), false, e, $id.getText());
} else if(sym.getType() != symbol_type.start_symbol
&& sym.getType() != symbol_type.end_symbol
&& sym.getType() != symbol_type.next2_symbol
&& sym.getType() != symbol_type.operand_symbol
&& sym.getType() != symbol_type.epsilon_symbol
&& sym.getType() != symbol_type.varnode_symbol) {
wrongSymbolTypeError(sym, find(id), "start, end, operand, epsilon, or varnode", "assignment");
wrongSymbolTypeError(sym, find(id), "start, end, next2, operand, epsilon, or varnode", "assignment");
} else {
VarnodeTpl v = ((SpecificSymbol) sym).getVarnode();
e.setOutput(find(t), v);
@ -1309,7 +1314,9 @@ jump_symbol[String purpose] returns [VarnodeTpl value]
SleighSymbol sym = pcode.findSymbol($s.getText());
if (sym == null) {
unknownSymbolError($s.getText(), find($s), "start, end, or operand", purpose);
} else if(sym.getType() == symbol_type.start_symbol || sym.getType() == symbol_type.end_symbol) {
} else if (sym.getType() == symbol_type.start_symbol ||
sym.getType() == symbol_type.end_symbol ||
sym.getType() == symbol_type.next2_symbol) {
SpecificSymbol ss = (SpecificSymbol) sym;
$value = new VarnodeTpl(find($s), new ConstTpl(ConstTpl.const_type.j_curspace),
ss.getVarnode().getOffset(),
@ -1489,6 +1496,7 @@ expr_apply returns [Object value]
}
} else if(sym.getType() == symbol_type.start_symbol
|| sym.getType() == symbol_type.end_symbol
|| sym.getType() == symbol_type.next2_symbol
|| sym.getType() == symbol_type.operand_symbol
|| sym.getType() == symbol_type.epsilon_symbol
|| sym.getType() == symbol_type.varnode_symbol) {

View file

@ -573,6 +573,10 @@ public class SleighAssemblerBuilder implements AssemblerBuilder {
else if (sym instanceof EndSymbol) {
// Ignore. We handle inst_next in semantic processing
}
else if (sym instanceof Next2Symbol) {
// Ignore. We handle inst_next2 in semantic processing
}
else if (sym instanceof UseropSymbol) {
// Ignore. We don't do pcode.
}

View file

@ -0,0 +1,77 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.assembler.sleigh.expr;
import java.util.Map;
import java.util.Set;
import ghidra.app.plugin.assembler.sleigh.sem.*;
import ghidra.app.plugin.processors.sleigh.expression.EndInstructionValue;
/**
* "Solves" expressions of {@code inst_next2}
*
* <p>
* Works like the constant solver, but takes the value of {@code inst_next}, which is given by the
* assembly address and the resulting instruction length.
*
* <p>
* <b>NOTE:</b> This solver requires backfill, since the value of {@code inst_next2} is not known
* until possible prefixes have been considered.
*/
public class Next2InstructionValueSolver extends AbstractExpressionSolver<EndInstructionValue> {
public Next2InstructionValueSolver() {
super(EndInstructionValue.class);
}
@Override
public AssemblyResolution solve(EndInstructionValue iv, MaskedLong goal, Map<String, Long> vals,
AssemblyResolvedPatterns cur, Set<SolverHint> hints, String description) {
throw new AssertionError(
"INTERNAL: Should never be asked to solve for " + AssemblyTreeResolver.INST_NEXT2);
}
@Override
public MaskedLong getValue(EndInstructionValue iv, Map<String, Long> vals,
AssemblyResolvedPatterns cur) throws NeedsBackfillException {
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT2);
if (instNext == null) {
throw new NeedsBackfillException(AssemblyTreeResolver.INST_NEXT2);
}
return MaskedLong.fromLong(instNext);
}
@Override
public int getInstructionLength(EndInstructionValue iv) {
return 0;
}
@Override
public MaskedLong valueForResolution(EndInstructionValue exp, Map<String, Long> vals,
AssemblyResolvedPatterns rc) {
Long instNext = vals.get(AssemblyTreeResolver.INST_NEXT2);
if (instNext == null) {
/**
* This method is used in forward state construction, so just leave unknown. This may
* cause unresolvable trees to get generated, but we can't know that until we try to
* resolve them.
*/
return MaskedLong.UNKS;
}
return MaskedLong.fromLong(instNext);
}
}

View file

@ -53,6 +53,7 @@ public class RecursiveDescentSolver {
new ContextFieldSolver().register(this);
new DivExpressionSolver().register(this);
new EndInstructionValueSolver().register(this);
new Next2InstructionValueSolver().register(this);
new LeftShiftExpressionSolver().register(this);
new MinusExpressionSolver().register(this);
new MultExpressionSolver().register(this);

View file

@ -78,6 +78,9 @@ public abstract class AbstractExpressionMatcher<T extends PatternExpression>
if (a instanceof EndInstructionValue) {
return true;
}
if (a instanceof Next2InstructionValue) {
return true;
}
if (a instanceof StartInstructionValue) {
return true;
}

View file

@ -54,6 +54,7 @@ public class AssemblyTreeResolver {
public static final String INST_START = "inst_start";
public static final String INST_NEXT = "inst_next";
public static final String INST_NEXT2 = "inst_next2";
protected final SleighLanguage lang;
protected final Address at;
@ -195,6 +196,8 @@ public class AssemblyTreeResolver {
return rc;
}
vals.put(INST_NEXT, at.add(rc.getInstructionLength()).getAddressableWordOffset());
// inst_next2 use not really supported
vals.put(INST_NEXT2, at.add(rc.getInstructionLength()).getAddressableWordOffset());
DBG.println("Backfilling: " + rc);
AssemblyResolution ar = rc.backfill(SOLVER, vals);
DBG.println("Backfilled final: " + ar);

View file

@ -210,6 +210,12 @@ public class ParserWalker {
return context.getNaddr();
}
public Address getN2addr() {
if (cross_context != null)
return cross_context.getN2addr();
return context.getN2addr();
}
public AddressSpace getCurSpace() {
return context.getCurSpace();
}

View file

@ -246,7 +246,7 @@ public abstract class PcodeEmit {
if (opcode == PcodeOp.BRANCH) {
int offsetType = inputs[0].getOffset().getType();
if (offsetType == ConstTpl.J_RELATIVE || offsetType == ConstTpl.J_START ||
offsetType == ConstTpl.J_NEXT) {
offsetType == ConstTpl.J_NEXT || offsetType == ConstTpl.J_NEXT2) {
return false;
}
OpTpl callopt = new OpTpl(PcodeOp.CALL, null, inputs);
@ -269,7 +269,7 @@ public abstract class PcodeEmit {
else if (opcode == PcodeOp.CBRANCH) {
int offsetType = inputs[0].getOffset().getType();
if (offsetType == ConstTpl.J_RELATIVE || offsetType == ConstTpl.J_START ||
offsetType == ConstTpl.J_NEXT) {
offsetType == ConstTpl.J_NEXT || offsetType == ConstTpl.J_NEXT2) {
return false;
}
@ -326,7 +326,7 @@ public abstract class PcodeEmit {
if (opcode == PcodeOp.BRANCH || opcode == PcodeOp.CALL) {
int offsetType = inputs[0].getOffset().getType();
if (offsetType == ConstTpl.J_RELATIVE || offsetType == ConstTpl.J_START ||
offsetType == ConstTpl.J_NEXT) {
offsetType == ConstTpl.J_NEXT || offsetType == ConstTpl.J_NEXT2) {
return false;
}

View file

@ -89,7 +89,6 @@ public class SleighInstructionPrototype implements InstructionPrototype {
private final static Address[] emptyFlow = new Address[0];
private ContextCache contextCache;
// private InstructionContext instructionContextCache;
private int length;
private ConstructState rootState;
private ConstructState mnemonicState; // state for print mnemonic
@ -236,6 +235,9 @@ public class SleighInstructionPrototype implements InstructionPrototype {
if (destType == ConstTpl.J_NEXT) {
flags = BRANCH_TO_END;
}
else if (destType == ConstTpl.J_NEXT2) {
flags = JUMPOUT;
}
else if ((destType != ConstTpl.J_START) && (destType != ConstTpl.J_RELATIVE)) {
flags = JUMPOUT;
}
@ -674,6 +676,9 @@ public class SleighInstructionPrototype implements InstructionPrototype {
Address addr = getHandleAddr(hand, parsecontext.getAddr().getAddressSpace());
res.add(addr);
}
else if (rec.op.getInput()[0].getOffset().getType() == ConstTpl.J_NEXT2) {
res.add(parsecontext.getN2addr());
}
}
}
}

View file

@ -21,8 +21,7 @@ import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
import ghidra.app.plugin.processors.sleigh.symbol.TripleSymbol;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.*;
/**
*
@ -33,8 +32,9 @@ import ghidra.program.model.mem.MemoryAccessException;
public class SleighParserContext implements ParserContext {
private MemBuffer memBuffer;
private Address addr; // Address of start of instruction
private Address nextInstrAddr; // Address of next instruction
private Address addr; // Address of start of instruction (inst_start)
private Address nextInstrAddr; // Address of next instruction (inst_next)
private Address next2InstAddr; // Address of instruction after next instruction (inst_next2)
private Address refAddr; // corresponds to inst_ref for call-fixup use
private Address destAddr; // corresponds to inst_dest for call-fixup use
private SleighInstructionPrototype prototype;
@ -71,7 +71,8 @@ public class SleighParserContext implements ParserContext {
}
/**
* Constructor for building precompiled templates
* Constructor for building precompiled templates.
* NOTE: This form does not support use of <code>inst_next2</next>.
* @param aAddr = address to which 'inst_start' resolves
* @param nAddr = address to which 'inst_next' resolves
* @param rAddr = special address associated with original call
@ -172,22 +173,99 @@ public class SleighParserContext implements ParserContext {
return handle;
}
/**
* get address of current instruction
* @return address of current instruction
*/
public Address getAddr() {
return addr;
}
/**
* Get address of instruction after current instruction. This may return null if this context
* instance does not support use of {@code inst_next} or next address falls beyond end of
* address space.
* @return address of next instruction or null
*/
public Address getNaddr() {
return nextInstrAddr;
}
/**
* Get address of instruction after the next instruction. This may return {@link #getNaddr()}
* if this context instance does not support use of {@code inst_next2} or parse of next
* instruction fails.
* @return address of instruction after the next instruction or null
*/
public Address getN2addr() {
if (next2InstAddr != null) {
return next2InstAddr;
}
next2InstAddr = computeNext2Address();
if (next2InstAddr == null) {
// unsupported use of inst_next2 or parse failure on next instruction
// returns same as inst_next
next2InstAddr = nextInstrAddr;
}
return next2InstAddr;
}
/**
* Return the address after the next instruction (inst_next2). The length of next instruction
* based on attempted parse of next instruction and does not consider any delayslot use.
* The current instructions context is used during the parse.
* @return address after the next instruction or null if unable/failed to determine
*/
private Address computeNext2Address() {
if (memBuffer == null || nextInstrAddr == null) {
return null; // not supported without memBuffer for parse
}
try {
Address nextAddr = nextInstrAddr;
Language language = prototype.getLanguage();
// limitation: assumes same context as current instruction
ProcessorContextImpl ctx = new ProcessorContextImpl(language);
RegisterValue ctxVal = getContextRegisterValue();
if (ctxVal != null) {
ctx.setRegisterValue(ctxVal);
}
int offset = (int) nextAddr.subtract(addr);
MemBuffer nearbymem = new WrappedMemBuffer(memBuffer, offset);
SleighInstructionPrototype proto =
(SleighInstructionPrototype) language.parse(nearbymem, ctx, true);
return nextAddr.addNoWrap(proto.getLength());
}
catch (Exception e) {
// ignore
}
return null;
}
/**
* Get address space containing current instruction
* @return address space containing current instruction
*/
public AddressSpace getCurSpace() {
return addr.getAddressSpace();
}
/**
* Get constant address space
* @return constant address space
*/
public AddressSpace getConstSpace() {
return constantSpace;
}
/**
* Get memory buffer for current instruction which may also be used to parse next instruction
* or delay slot instructions.
* @return memory buffer for current instruction
*/
public MemBuffer getMemBuffer() {
return memBuffer;
}
@ -251,6 +329,44 @@ public class SleighParserContext implements ParserContext {
return res;
}
/**
* Get the processor context value as a RegisterValue
* @return processor context value
*/
public RegisterValue getContextRegisterValue() {
Register baseContextRegister = prototype.getLanguage().getContextBaseRegister();
if (baseContextRegister == null) {
return null;
}
// convert context int words to byte array for RegisterValue use
int ctxByteLen = baseContextRegister.getMinimumByteSize();
byte[] ctxValueBytes = new byte[ctxByteLen];
for (int i = 0; i < context.length; i++) {
int word = context[i];
for (int n = 3; n >= 0; --n) {
int byteIndex = (i * 4) + n;
setByte(ctxValueBytes, byteIndex, (byte) word);
word >>= 8;
}
}
// append mask to value array for RegisterValue use
byte[] ctxValueMaskBytes = new byte[2 * ctxByteLen];
Arrays.fill(ctxValueMaskBytes, 0, ctxByteLen, (byte) 0xff);
System.arraycopy(ctxValueBytes, 0, ctxValueMaskBytes, ctxByteLen, ctxByteLen);
return new RegisterValue(baseContextRegister, ctxValueMaskBytes);
}
private void setByte(byte[] bytes, int index, byte b) {
if (index < bytes.length) {
bytes[index] = b;
}
}
/**
* Get bytes from context into an int
* @param bytestart is the index of the first byte to fetch

View file

@ -0,0 +1,68 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.processors.sleigh.expression;
import ghidra.app.plugin.processors.sleigh.ParserWalker;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.xml.XmlPullParser;
/**
*
*
* The integer offset of the address following the current instruction
*/
public class Next2InstructionValue extends PatternValue {
private static final int HASH = "[inst_next2]".hashCode();
@Override
public int hashCode() {
return HASH;
}
@Override
public boolean equals(Object obj) {
return obj instanceof Next2InstructionValue;
}
@Override
public long minValue() {
return 0;
}
@Override
public long maxValue() {
return 0;
}
@Override
public long getValue(ParserWalker walker) throws MemoryAccessException {
Address addr = walker.getN2addr();
return addr.getAddressableWordOffset();
}
@Override
public void restoreXml(XmlPullParser parser, SleighLanguage lang) {
parser.discardSubTree("next2_exp");
// Nothing to do
}
@Override
public String toString() {
return "[inst_next2]";
}
}

View file

@ -51,6 +51,8 @@ public abstract class PatternExpression {
res = new StartInstructionValue();
else if (nm.equals("end_exp"))
res = new EndInstructionValue();
else if (nm.equals("next2_exp"))
res = new Next2InstructionValue();
else if (nm.equals("plus_exp"))
res = new PlusExpression();
else if (nm.equals("sub_exp"))

View file

@ -0,0 +1,68 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.processors.sleigh.symbol;
import java.util.ArrayList;
import ghidra.app.plugin.processors.sleigh.*;
import ghidra.app.plugin.processors.sleigh.expression.Next2InstructionValue;
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
*
*
* Symbol with semantic value equal to offset of address immediately
* after the next instruction (inst_next2)
*/
public class Next2Symbol extends SpecificSymbol {
private PatternExpression patexp;
@Override
public PatternExpression getPatternExpression() {
return patexp;
}
@Override
public void getFixedHandle(FixedHandle hand, ParserWalker walker) {
hand.space = walker.getCurSpace();
hand.offset_space = null;
hand.offset_offset = walker.getN2addr().getOffset();
hand.size = hand.space.getPointerSize();
}
@Override
public String print(ParserWalker walker) throws MemoryAccessException {
long val = walker.getN2addr().getOffset();
return "0x"+Long.toHexString(val);
}
@Override
public void printList(ParserWalker walker, ArrayList<Object> list) {
list.add(walker.getParentHandle());
}
@Override
public void restoreXml(XmlPullParser parser, SleighLanguage sleigh) {
XmlElement element = parser.start("next2_sym");
patexp = new Next2InstructionValue();
parser.end(element);
}
}

View file

@ -19,6 +19,8 @@
*/
package ghidra.app.plugin.processors.sleigh.symbol;
import java.util.ArrayList;
import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.program.model.lang.UnknownInstructionException;
@ -26,8 +28,6 @@ import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
import java.util.ArrayList;
/**
*
*
@ -152,6 +152,8 @@ public class SymbolTable {
sym = new StartSymbol();
else if (el.getName().equals("end_sym_head"))
sym = new EndSymbol();
else if (el.getName().equals("next2_sym_head"))
sym = new Next2Symbol();
else if (el.getName().equals("subtable_sym_head"))
sym = new SubtableSymbol();
else

View file

@ -39,14 +39,15 @@ public class ConstTpl {
public static final int HANDLE = 1;
public static final int J_START = 2;
public static final int J_NEXT = 3;
public static final int J_CURSPACE = 4;
public static final int J_CURSPACE_SIZE = 5;
public static final int SPACEID = 6;
public static final int J_RELATIVE = 7;
public static final int J_FLOWREF = 8;
public static final int J_FLOWREF_SIZE = 9;
public static final int J_FLOWDEST = 10;
public static final int J_FLOWDEST_SIZE = 11;
public static final int J_NEXT2 = 4;
public static final int J_CURSPACE = 5;
public static final int J_CURSPACE_SIZE = 6;
public static final int SPACEID = 7;
public static final int J_RELATIVE = 8;
public static final int J_FLOWREF = 9;
public static final int J_FLOWREF_SIZE = 10;
public static final int J_FLOWDEST = 11;
public static final int J_FLOWDEST_SIZE = 12;
public static final int V_SPACE = 0;
public static final int V_OFFSET = 1;
@ -143,6 +144,8 @@ public class ConstTpl {
return walker.getAddr().getOffset();
case J_NEXT:
return walker.getNaddr().getOffset();
case J_NEXT2:
return walker.getN2addr().getOffset();
case J_FLOWREF:
return walker.getFlowRefAddr().getOffset();
case J_FLOWREF_SIZE:
@ -315,6 +318,9 @@ public class ConstTpl {
else if (typestr.equals("next")) {
type = J_NEXT;
}
else if (typestr.equals("next2")) {
type = J_NEXT2;
}
else if (typestr.equals("curspace")) {
type = J_CURSPACE;
}
@ -379,6 +385,8 @@ public class ConstTpl {
return "[flowref_size]";
case J_NEXT:
return "[next]";
case J_NEXT2:
return "[next2]";
case J_START:
return "[start]";
case J_RELATIVE:

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -289,6 +289,8 @@ public class SleighCompile extends SleighBase {
symtab.addSymbol(startsym);
EndSymbol endsym = new EndSymbol(location, "inst_next", getConstantSpace());
symtab.addSymbol(endsym);
Next2Symbol next2sym = new Next2Symbol(location, "inst_next2", getConstantSpace());
symtab.addSymbol(next2sym);
EpsilonSymbol epsilon = new EpsilonSymbol(location, "epsilon", getConstantSpace());
symtab.addSymbol(epsilon);
}
@ -1280,7 +1282,8 @@ public class SleighCompile extends SleighBase {
VectorSTL<PatternValue> vallist = new VectorSTL<>();
pe.listValues(vallist);
for (int i = 0; i < vallist.size(); ++i) {
if (vallist.get(i) instanceof EndInstructionValue) {
PatternValue v = vallist.get(i);
if (v instanceof EndInstructionValue || v instanceof Next2InstructionValue) {
return false;
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,6 +31,7 @@ class Yylval {
OperandSymbol operandsym;
StartSymbol startsym;
EndSymbol endsym;
Next2Symbol next2sym;
SubtableSymbol subtablesym;
MacroSymbol macrosym;
LabelSymbol labelsym;

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -0,0 +1,61 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcodeCPort.slghpatexpress;
import java.io.PrintStream;
import org.jdom.Element;
import generic.stl.VectorSTL;
import ghidra.pcodeCPort.translate.Translate;
import ghidra.sleigh.grammar.Location;
public class Next2InstructionValue extends PatternValue {
public Next2InstructionValue(Location location) {
super(location);
}
@Override
public TokenPattern genMinPattern(VectorSTL<TokenPattern> ops) {
return new TokenPattern(location);
}
@Override
public TokenPattern genPattern(long val) {
return new TokenPattern(location);
}
@Override
public long minValue() {
return 0;
}
@Override
public long maxValue() {
return 0;
}
@Override
public void saveXml(PrintStream s) {
s.append("<next2_exp/>");
}
@Override
public void restoreXml(Element el, Translate trans) {
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -0,0 +1,93 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcodeCPort.slghsymbol;
import java.io.PrintStream;
import org.jdom.Element;
import ghidra.pcodeCPort.semantics.ConstTpl;
import ghidra.pcodeCPort.semantics.VarnodeTpl;
import ghidra.pcodeCPort.sleighbase.SleighBase;
import ghidra.pcodeCPort.slghpatexpress.Next2InstructionValue;
import ghidra.pcodeCPort.slghpatexpress.PatternExpression;
import ghidra.pcodeCPort.space.AddrSpace;
import ghidra.sleigh.grammar.Location;
public class Next2Symbol extends SpecificSymbol {
private AddrSpace const_space;
private PatternExpression patexp;
public Next2Symbol(Location location) {
super(location);
patexp = null;
} // For use with restoreXml
@Override
public PatternExpression getPatternExpression() {
return patexp;
}
@Override
public symbol_type getType() {
return symbol_type.next2_symbol;
}
public Next2Symbol(Location location, String nm, AddrSpace cspc) {
super(location, nm);
const_space = cspc;
patexp = new Next2InstructionValue(location);
patexp.layClaim();
}
@Override
public void dispose() {
if (patexp != null) {
PatternExpression.release(patexp);
}
}
// Return next instruction offset as a constant
@Override
public VarnodeTpl getVarnode() {
ConstTpl spc = new ConstTpl(const_space);
ConstTpl off = new ConstTpl(ConstTpl.const_type.j_next2);
ConstTpl sz_zero = new ConstTpl();
return new VarnodeTpl(location, spc, off, sz_zero);
}
@Override
public void saveXml(PrintStream s) {
s.append("<next2_sym");
saveSleighSymbolXmlHeader(s);
s.println("/>");
}
@Override
public void saveXmlHeader(PrintStream s) {
s.append("<next2_sym_head");
saveSleighSymbolXmlHeader(s);
s.println("/>");
}
@Override
public void restoreXml(Element el, SleighBase trans) {
const_space = trans.getConstantSpace();
patexp = new Next2InstructionValue(null);
patexp.layClaim();
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -15,6 +15,12 @@
*/
package ghidra.pcodeCPort.slghsymbol;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.List;
import org.jdom.Element;
import generic.stl.IteratorSTL;
import generic.stl.VectorSTL;
import ghidra.pcodeCPort.context.SleighError;
@ -22,12 +28,6 @@ import ghidra.pcodeCPort.sleighbase.SleighBase;
import ghidra.pcodeCPort.utils.XmlUtils;
import ghidra.sleigh.grammar.Location;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.List;
import org.jdom.Element;
public class SymbolTable {
private VectorSTL<SleighSymbol> symbollist = new VectorSTL<SleighSymbol>();
@ -300,6 +300,9 @@ public class SymbolTable {
else if (el.getName().equals("end_sym_head")) {
sym = new EndSymbol(location);
}
else if (el.getName().equals("next2_sym_head")) {
sym = new Next2Symbol(location);
}
else if (el.getName().equals("subtable_sym_head")) {
sym = new SubtableSymbol(location);
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -27,6 +27,7 @@ public enum symbol_type {
operand_symbol,
start_symbol, // inst_start, inst_ref, inst_def
end_symbol, // inst_next
next2_symbol, // inst_next2
subtable_symbol,
macro_symbol,
section_symbol,

View file

@ -63,6 +63,7 @@ public interface InstructionContext {
* at the specified address. The returned ParserContext may be cast to the prototype's
* implementation without checking. This method will throw an UnknownContextException
* if a compatible ParserContext is not found at the specified address.
* @param instructionAddress instruction address of requested context
* @return the instruction parser context at the specified instruction address
* @throws UnknownContextException if the instruction at the specified address
* was not previously parsed or attempting to instantiate context resulted in an

View file

@ -85,6 +85,7 @@ public class PcodeParser extends PcodeCompile {
Location internalLoc = Location.INTERNALLY_DEFINED;
symbolMap.put("inst_start", new StartSymbol(internalLoc, "inst_start", getConstantSpace()));
symbolMap.put("inst_next", new EndSymbol(internalLoc, "inst_next", getConstantSpace()));
symbolMap.put("inst_next2", new Next2Symbol(internalLoc, "inst_next2", getConstantSpace()));
symbolMap.put("inst_ref", new FlowRefSymbol(internalLoc, "inst_ref", getConstantSpace()));
symbolMap.put("inst_dest",
new FlowDestSymbol(internalLoc, "inst_dest", getConstantSpace()));

View file

@ -1,8 +1,11 @@
#### immediates
# 0iii dddd iiii iiii # imm rd, i load 11-bit unsigned int into rd
# 10nn dddd nnnn nnnn # simm rd, n load 10-bit signed int into rd
#
#### arithmetic
#### load immediate 0000 000x xxxx xxxx
# 00ii dddd iiii iiii # imm rd, i load 10-bit unsigned int into rd
# 01nn dddd nnnn nnnn # simm rd, n load 10-bit signed int into rd
#
#### misc 100x xxxx xxxx xxxx
# 1000 0000 0000 0ccc # skcc if ccc goto inst_next2 (conditional skip instruction)
#
#### arithmetic 1100 xxxx
# 1100 0000 ssss tttt # add rs, rt rs = rs + rt
# 1100 0001 ssss tttt # sub rs, rt rs = rs - rt
# 1100 0010 ssss tttt # rsub rs, rt rs = rt - rs
@ -40,7 +43,7 @@
# 1101 0110 ssss tttt # load rs, [rt] rs = [rt]
# 1101 0111 ssss tttt # store [rs], rt [rs] = rt
# 1101 1111 ssss tttt # mov rs, rt rs = rt
#
#### flow
# 1110 nnnn nnnn 0ccc # brcc n if ccc goto pc + n
# 1110 nnnn nnnn 1ccc # brdscc n if ccc goto pc + n with delay slot
@ -54,7 +57,7 @@
# 1111 0110 ssss 1ccc # call rs if ccc call rs
# 1111 1nnn nnnn nnnn # call n call n
#
#### user-defined
#### user-defined 1010 xxxx
# 1010 0010 ssss 0000 # user_one rs user_one rs
# 1010 0010 ssss 0000 # user_two rs user_two rs
# 1010 0011 0000 0000 # user_three user_three
@ -63,11 +66,11 @@
# 1010 0110 ssss 0000 # user_six rs user_six rs
# 1010 1000 0000 0000 # unimpl
#
#### RESERVED
# 1101 1001 xxxx xxxx # RESERVED BANK
# 1101 1010 xxxx xxxx # RESERVED BANK
# 1111 0111 xxxx xxxx # RESERVED BANK
#### RESERVED / UNUSED
# 1011 xxxx xxxx xxxx # UNUSED
# 1101 1001 xxxx xxxx # RESERVED BANK (consumed by toy_builder.sinc)
# 1101 1010 xxxx xxxx # RESERVED BANK (consumed by toy_builder.sinc)
# 1111 0111 xxxx xxxx # RESERVED BANK (consumed by toy_builder.sinc)
define token instr(16)
op1515 = (15, 15)
@ -75,13 +78,14 @@ define token instr(16)
op1215 = (12, 15)
op1111 = (11, 11)
op0811 = (8, 11)
op0407 = (4, 7)
op0007 = (0, 7)
op0003 = (0, 3)
op0303 = (3, 3)
rd = (8, 11)
rs = (4, 7)
rt = (0, 3)
imm1214 = (12, 14)
imm1213 = (12, 13)
imm0007 = (0, 7)
imm0003 = (0, 3)
simm1213 = (12, 13) signed
@ -104,7 +108,7 @@ Simm4: "#"^simm0003 is simm0003 { export *[const]:$(SIZE) simm0003; }
Simm10: "#"^computed is simm1213 & imm0007 [ computed = (simm1213 << 8) | imm0007; ] { export *[const]:$(SIZE) computed; }
Imm4: "#"^imm0003 is imm0003 { export *[const]:$(SIZE) imm0003; }
Imm11: "#"^computed is imm1214 & imm0007 [ computed = (imm1214 << 8) | imm0007; ] { export *[const]:$(SIZE) computed; }
Imm10: "#"^computed is imm1213 & imm0007 [ computed = (imm1213 << 8) | imm0007; ] { export *[const]:$(SIZE) computed; }
Rel8: addr is simm0007 [ addr = inst_start + simm0007; ] { export *:$(SIZE) addr; }
Rel82: addr is simm0411 [ addr = inst_start + simm0411; ] { export *:$(SIZE) addr; }
@ -148,7 +152,7 @@ define pcodeop pcodeop_three;
# operations
:imm rd, Imm11 is $(INSTR_PHASE) op1515=0x0 & rd & Imm11 { logicflags(); rd = Imm11; resultflags(rd); }
:imm rd, Imm10 is $(INSTR_PHASE) op1515=0x0 & rd & Imm10 { logicflags(); rd = Imm10; resultflags(rd); }
:simm rd, Simm10 is $(INSTR_PHASE) op1415=0x2 & rd & Simm10 { logicflags(); rd = Simm10; resultflags(rd); }
:add rs, rt is $(INSTR_PHASE) op1215=0xc & op0811=0x0 & rs & rt { addflags(rs, rt); rs = rs + rt; resultflags(rs); }
@ -197,6 +201,8 @@ define pcodeop pcodeop_three;
:store RS, rt is $(INSTR_PHASE) op1215=0xd & op0811=0x7 & RS & rt { RS = rt; logicflags(); resultflags(rt); }
:mov rs, rt is $(INSTR_PHASE) op1215=0xd & op0811=0xf & rs & rt { rs = rt; logicflags(); resultflags(rs); }
:sk^CC is $(INSTR_PHASE) op1215=0x8 & op0811=0x0 & op0407=0x0 & op0303=0x0 & CC & Rel82 { if (CC) goto inst_next2; }
:br^COND Rel82 is $(INSTR_PHASE) op1215=0xe & op0303=0x0 & COND & Rel82 { build COND; goto Rel82; }
:brds^COND Rel82 is $(INSTR_PHASE) op1215=0xe & op0303=0x1 & COND & Rel82 { build COND; delayslot(1); goto Rel82; }
:br^COND rs is $(INSTR_PHASE) op1215=0xf & op0811=0x0 & COND & rs & op0303=0x0 { build COND; goto [rs]; }

View file

@ -1502,6 +1502,35 @@ public class DisassemblerTest extends AbstractGhidraHeadlessIntegrationTest {
}
/**
* 10: skeq
* 12: bral 20 --------+
* 14: ret |
* |
* 20: mov r0, #123 <-+
* 22: ret
*
* Test skip instruction
*
*/
@Test
public void testDisassemblerSkip() throws Exception {
programBuilder.addBytesSkipConditional(10);
programBuilder.addBytesBranch(12, 20);
programBuilder.addBytesReturn(14);
programBuilder.addBytesMoveImmediate(20, (short) 123);
programBuilder.addBytesReturn(22);
AddressSetView disAddrs = disassembler.disassemble(addr(10), null);
assertEquals(addrset(range(10, 15), range(20, 23)), disAddrs);
verifyInstructionPresence();
verifyNoBookmarks();
}
/**
* 10: bral 200 (error on flow to non-existing memory)
*

View file

@ -71,6 +71,26 @@ public class PcodeParserTest extends AbstractGhidraHeadlessIntegrationTest {
return true;
}
public boolean testInstNext2Constant(VarnodeTpl vn, int size) {
assertNotNull(vn);
if (vn.getSpace().getType() != ConstTpl.SPACEID) {
return false;
}
if (!vn.getSpace().getSpaceId().getName().equals(SpaceNames.CONSTANT_SPACE_NAME)) {
return false;
}
if (vn.getOffset().getType() != ConstTpl.J_NEXT2) {
return false;
}
if (vn.getSize().getType() != ConstTpl.REAL) {
return false;
}
if (vn.getSize().getReal() != size) {
return false;
}
return true;
}
public boolean testInstNext(VarnodeTpl vn) {
assertNotNull(vn);
if (vn.getSpace().getType() != ConstTpl.J_CURSPACE) {
@ -85,6 +105,20 @@ public class PcodeParserTest extends AbstractGhidraHeadlessIntegrationTest {
return true;
}
public boolean testInstNext2(VarnodeTpl vn) {
assertNotNull(vn);
if (vn.getSpace().getType() != ConstTpl.J_CURSPACE) {
return false;
}
if (vn.getOffset().getType() != ConstTpl.J_NEXT2) {
return false;
}
if (vn.getSize().getType() != ConstTpl.J_CURSPACE_SIZE) {
return false;
}
return true;
}
public boolean testRelative(VarnodeTpl vn, int labelid, int size) {
assertNotNull(vn);
if (vn.getSpace().getType() != ConstTpl.SPACEID) {
@ -175,6 +209,7 @@ public class PcodeParserTest extends AbstractGhidraHeadlessIntegrationTest {
long uniqueBase = UniqueLayout.INJECT.getOffset(lang);
String pcodeStatements = "tmp:1 = inst_next;\n" + "if (AX == 0) goto inst_next;\n" +
"tmp2:1 = inst_next2;\n" + "if (BX == 0) goto inst_next2;\n" +
"call [ECX];\n" + "if (BX != 1) goto <lab>;\n" + "CX = 0;\n" + "<lab>\n" +
"BX = CX << 2;\n" + "in1 = in2 + 7;";
@ -186,8 +221,9 @@ public class PcodeParserTest extends AbstractGhidraHeadlessIntegrationTest {
assertNull(template.getResult());
assertEquals(template.getNumLabels(), 1);
OpTpl[] vec = template.getOpVec();
assertEquals(vec.length, 10);
assertEquals(vec.length, 13);
// inst_next
assertEquals(vec[0].getOpcode(), PcodeOp.COPY);
assertTrue(testVarnode(vec[0].getOutput(), SpaceNames.UNIQUE_SPACE_NAME, uniqueBase, 1));
assertEquals(vec[0].getInput().length, 1);
@ -207,46 +243,68 @@ public class PcodeParserTest extends AbstractGhidraHeadlessIntegrationTest {
assertTrue(
testVarnode(vec[2].getInput()[1], SpaceNames.UNIQUE_SPACE_NAME, uniqueBase + 0x80, 1));
assertEquals(vec[3].getOpcode(), PcodeOp.CALLIND);
assertNull(vec[3].getOutput());
assertEquals(vec[3].getInput().length, 1);
assertTrue(testVarnode(vec[3].getInput()[0], "register", 4, 4));
assertEquals(vec[4].getOpcode(), PcodeOp.INT_NOTEQUAL);
// inst_next2
assertEquals(vec[3].getOpcode(), PcodeOp.COPY);
assertTrue(
testVarnode(vec[4].getOutput(), SpaceNames.UNIQUE_SPACE_NAME, uniqueBase + 0x100, 1));
testVarnode(vec[3].getOutput(), SpaceNames.UNIQUE_SPACE_NAME, uniqueBase + 0x100, 1));
assertEquals(vec[3].getInput().length, 1);
assertTrue(testInstNext2Constant(vec[3].getInput()[0], 1));
assertEquals(vec[4].getOpcode(), PcodeOp.INT_EQUAL);
assertTrue(
testVarnode(vec[4].getOutput(), SpaceNames.UNIQUE_SPACE_NAME, uniqueBase + 0x180, 1));
assertEquals(vec[4].getInput().length, 2);
assertTrue(testVarnode(vec[4].getInput()[0], "register", 0xc, 2));
assertTrue(testVarnode(vec[4].getInput()[1], SpaceNames.CONSTANT_SPACE_NAME, 1, 2));
assertTrue(testVarnode(vec[4].getInput()[1], SpaceNames.CONSTANT_SPACE_NAME, 0, 2));
assertEquals(vec[5].getOpcode(), PcodeOp.CBRANCH);
assertNull(vec[5].getOutput());
assertEquals(vec[5].getInput().length, 2);
assertTrue(testRelative(vec[5].getInput()[0], 0, 4));
assertTrue(testInstNext2(vec[5].getInput()[0]));
assertTrue(
testVarnode(vec[5].getInput()[1], SpaceNames.UNIQUE_SPACE_NAME, uniqueBase + 0x100, 1));
testVarnode(vec[5].getInput()[1], SpaceNames.UNIQUE_SPACE_NAME, uniqueBase + 0x180, 1));
assertEquals(vec[6].getOpcode(), PcodeOp.COPY);
assertTrue(testVarnode(vec[6].getOutput(), "register", 4, 2));
// call
assertEquals(vec[6].getOpcode(), PcodeOp.CALLIND);
assertNull(vec[6].getOutput());
assertEquals(vec[6].getInput().length, 1);
assertTrue(testVarnode(vec[6].getInput()[0], SpaceNames.CONSTANT_SPACE_NAME, 0, 2));
assertTrue(testVarnode(vec[6].getInput()[0], "register", 4, 4));
assertEquals(vec[7].getOpcode(), PcodeOp.PTRADD); // label
assertNull(vec[7].getOutput());
assertEquals(vec[7].getInput().length, 1);
assertTrue(testVarnode(vec[7].getInput()[0], SpaceNames.CONSTANT_SPACE_NAME, 0, 4));
assertEquals(vec[8].getOpcode(), PcodeOp.INT_LEFT);
assertTrue(testVarnode(vec[8].getOutput(), "register", 0xc, 2));
assertEquals(vec[8].getInput().length, 2);
assertTrue(testVarnode(vec[8].getInput()[0], "register", 0x4, 2));
assertTrue(testVarnode(vec[8].getInput()[1], SpaceNames.CONSTANT_SPACE_NAME, 2, 4));
assertEquals(vec[9].getOpcode(), PcodeOp.INT_ADD);
assertTrue(testParameter(vec[9].getOutput(), 0));
assertEquals(vec[9].getInput().length, 2);
assertTrue(testParameter(vec[9].getInput()[0], 1));
assertEquals(vec[7].getOpcode(), PcodeOp.INT_NOTEQUAL);
assertTrue(
testVarnodeHandleSize(vec[9].getInput()[1], SpaceNames.CONSTANT_SPACE_NAME, 7, 0));
testVarnode(vec[7].getOutput(), SpaceNames.UNIQUE_SPACE_NAME, uniqueBase + 0x200, 1));
assertEquals(vec[7].getInput().length, 2);
assertTrue(testVarnode(vec[7].getInput()[0], "register", 0xc, 2));
assertTrue(testVarnode(vec[7].getInput()[1], SpaceNames.CONSTANT_SPACE_NAME, 1, 2));
assertEquals(vec[8].getOpcode(), PcodeOp.CBRANCH);
assertNull(vec[8].getOutput());
assertEquals(vec[8].getInput().length, 2);
assertTrue(testRelative(vec[8].getInput()[0], 0, 4));
assertTrue(
testVarnode(vec[8].getInput()[1], SpaceNames.UNIQUE_SPACE_NAME, uniqueBase + 0x200, 1));
assertEquals(vec[9].getOpcode(), PcodeOp.COPY);
assertTrue(testVarnode(vec[9].getOutput(), "register", 4, 2));
assertEquals(vec[9].getInput().length, 1);
assertTrue(testVarnode(vec[9].getInput()[0], SpaceNames.CONSTANT_SPACE_NAME, 0, 2));
assertEquals(vec[10].getOpcode(), PcodeOp.PTRADD); // label
assertNull(vec[10].getOutput());
assertEquals(vec[10].getInput().length, 1);
assertTrue(testVarnode(vec[10].getInput()[0], SpaceNames.CONSTANT_SPACE_NAME, 0, 4));
assertEquals(vec[11].getOpcode(), PcodeOp.INT_LEFT);
assertTrue(testVarnode(vec[11].getOutput(), "register", 0xc, 2));
assertEquals(vec[11].getInput().length, 2);
assertTrue(testVarnode(vec[11].getInput()[0], "register", 0x4, 2));
assertTrue(testVarnode(vec[11].getInput()[1], SpaceNames.CONSTANT_SPACE_NAME, 2, 4));
assertEquals(vec[12].getOpcode(), PcodeOp.INT_ADD);
assertTrue(testParameter(vec[12].getOutput(), 0));
assertEquals(vec[12].getInput().length, 2);
assertTrue(testParameter(vec[12].getInput()[0], 1));
assertTrue(
testVarnodeHandleSize(vec[12].getInput()[1], SpaceNames.CONSTANT_SPACE_NAME, 7, 0));
}
}