Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions pypcode/sleigh/sleigh.cc
Original file line number Diff line number Diff line change
Expand Up @@ -365,13 +365,37 @@ void SleighBuilder::appendBuild(OpTpl *bld,int4 secnum)
walker->popOperand();
}

/// \brief Restore a SleighBuilder's walker and unique offset when the enclosing scope exits
///
/// A delay slot or a CROSSBUILD is built through a ParserWalker that lives on the stack frame of
/// the builder method, so the builder must point back at its original walker before that frame
/// goes away, including when the nested build throws. Otherwise Sleigh::oneInstruction reports
/// the failure through a walker that no longer exists.
class WalkerScope {
ParserWalker *&curwalker; ///< The builder's current walker
uintb &curuniqueoffset; ///< The builder's current unique offset
ParserWalker *oldwalker; ///< The walker in use when this scope was entered
uintb olduniqueoffset; ///< The unique offset in use when this scope was entered
public:
/// Record the walker and unique offset to put back
WalkerScope(ParserWalker *&w,uintb &off) : curwalker(w), curuniqueoffset(off) {
oldwalker = w;
olduniqueoffset = off;
}
/// Put back the recorded walker and unique offset
~WalkerScope(void) {
curwalker = oldwalker;
curuniqueoffset = olduniqueoffset;
}
};

void SleighBuilder::delaySlot(OpTpl *op)

{
// Append pcode for an entire instruction (delay slot)
// in the middle of the current instruction
ParserWalker *tmp = walker;
uintb olduniqueoffset = uniqueoffset;
WalkerScope scope(walker,uniqueoffset); // Restore original context on every exit

Address baseaddr = tmp->getAddr();
int4 fallOffset = tmp->getLength();
Expand All @@ -392,8 +416,6 @@ void SleighBuilder::delaySlot(OpTpl *op)
fallOffset += len;
bytecount += len;
} while(bytecount < delaySlotByteCnt);
walker = tmp; // Restore original context
uniqueoffset = olduniqueoffset;
}

void SleighBuilder::setLabel(OpTpl *op)
Expand All @@ -416,7 +438,7 @@ void SleighBuilder::appendCrossBuild(OpTpl *bld,int4 secnum)
uintb addr = spc->wrapOffset( vn->getOffset().fix(*walker) );

ParserWalker *tmp = walker;
uintb olduniqueoffset = uniqueoffset;
WalkerScope scope(walker,uniqueoffset); // Restore original context on every exit

Address newaddr(spc,addr);
setUniqueOffset(newaddr);
Expand All @@ -434,8 +456,6 @@ void SleighBuilder::appendCrossBuild(OpTpl *bld,int4 secnum)
buildEmpty(ct,secnum);
else
build(construct,secnum);
walker = tmp;
uniqueoffset = olduniqueoffset;
}

/// \param min is the minimum number of allocations before a reuse is expected
Expand Down
24 changes: 24 additions & 0 deletions tests/test_pypcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,30 @@ def test_partial_unimpl_failure(self):
tx = ctx.translate(b"\xd0\x00\xa8\x00") # and r0, r0; unimpl
assert len(get_imarks(tx)) == 1

def test_delay_slot_unimpl_failure(self):
# The delay slot is built through a walker owned by SleighBuilder::delaySlot, and the
# error is reported through the builder's walker once that frame is gone.
for lang, insns in [
("Toy:BE:32:default", b"\xf5\x00\xa8\x00"), # callds 0x0; unimpl
("sparc:BE:32:default", b"\x63\x74\x85\x96\xa7\xb8\xc9\xda"), # call; unimpl
]:
with self.subTest(lang=lang):
ctx = Context(lang)
with self.assertRaises(UnimplError) as ctxmgr:
ctx.translate(insns)
# The branch is reported, not the instruction in its delay slot.
assert "0x00000000" in str(ctxmgr.exception)

def test_partial_delay_slot_unimpl_failure(self):
for lang, insns in [
("Toy:BE:32:default", b"\xd0\x00\xf5\x00\xa8\x00"), # and r0, r0; callds 0x0; unimpl
("sparc:BE:32:default", b"\x01\x00\x00\x00\x63\x74\x85\x96\xa7\xb8\xc9\xda"), # nop; call; unimpl
]:
with self.subTest(lang=lang):
ctx = Context(lang)
tx = ctx.translate(insns)
assert len(get_imarks(tx)) == 1

def test_not_cached(self):
ctx = Context("x86:LE:64:default")
tx = ctx.translate(b"\xeb\xfe", 5)
Expand Down
Loading