From f0a9427e4fe819c5a0b86e221e85a2ffaedf183f Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Tue, 2 Jul 2019 15:26:54 -0400 Subject: [PATCH] Extended decompiler support for optimized integer division forms --- .../Decompiler/src/decompile/cpp/rangeutil.cc | 9 +- .../src/decompile/cpp/ruleaction.cc | 234 +++++++++++------- .../src/decompile/cpp/ruleaction.hh | 3 +- .../app/decompiler/DecompileCallback.java | 11 +- 4 files changed, 167 insertions(+), 90 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc index 48948afcfd..e5bfe0ac0b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc @@ -1223,7 +1223,8 @@ bool CircleRange::pushForwardBinary(OpCode opc,const CircleRange &in1,const Circ } int4 wholeSize = 8*sizeof(uintb) - count_leading_zeros(mask); if (in1.getMaxInfo() + in2.getMaxInfo() > wholeSize) { - right = left; // Covered everything + left = in1.left; // Covered everything + right = in1.left; normalize(); return true; } @@ -1630,8 +1631,10 @@ bool ValueSet::iterate(Widener &widener) if (0 != res.circleUnion(range)) { // Union with the previous iteration's set res.minimalContainer(range,MAX_STEP); } - leftIsStable = range.getMin() == res.getMin(); - rightIsStable = range.getEnd() == res.getEnd(); + if (!range.isEmpty() && !res.isEmpty()) { + leftIsStable = range.getMin() == res.getMin(); + rightIsStable = range.getEnd() == res.getEnd(); + } } else if (numParams == 1) { ValueSet *inSet1 = op->getIn(0)->getValueSet(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index fbac25d4b0..594d894668 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -6189,7 +6189,7 @@ int4 RuleDivTermAdd::applyOp(PcodeOp *op,Funcdata &data) { int4 n; OpCode shiftopc; - PcodeOp *subop = RuleDivOpt::findSubshift(op,n,shiftopc); + PcodeOp *subop = findSubshift(op,n,shiftopc); if (subop == (PcodeOp *)0) return 0; // TODO: Cannot currently support 128-bit arithmetic, except in special case of 2^64 if (n > 64) return 0; @@ -6274,6 +6274,44 @@ int4 RuleDivTermAdd::applyOp(PcodeOp *op,Funcdata &data) return 0; } +/// \brief Check for shift form of expression +/// +/// Look for the two forms: +/// - `sub(V,c)` or +/// - `sub(V,c) >> n` +/// +/// Pass back whether a shift was involved and the total truncation in bits: `n+c*8` +/// \param op is the root of the expression +/// \param n is the reference that will hold the total truncation +/// \param shiftopc will hold the shift OpCode if used, CPUI_MAX otherwise +/// \return the SUBPIECE op if present or NULL otherwise +PcodeOp *RuleDivTermAdd::findSubshift(PcodeOp *op,int4 &n,OpCode &shiftopc) + +{ // SUB( .,#c) or SUB(.,#c)>>n return baseop and n+c*8 + // make SUB is high + PcodeOp *subop; + shiftopc = op->code(); + if (shiftopc != CPUI_SUBPIECE) { // Must be right shift + Varnode *vn = op->getIn(0); + if (!vn->isWritten()) return (PcodeOp *)0; + subop = vn->getDef(); + if (subop->code() != CPUI_SUBPIECE) return (PcodeOp *)0; + if (!op->getIn(1)->isConstant()) return (PcodeOp *)0; + n = op->getIn(1)->getOffset(); + } + else { + shiftopc = CPUI_MAX; // Indicate there was no shift + subop = op; + n = 0; + } + int4 c = subop->getIn(1)->getOffset(); + if (subop->getOut()->getSize() + c != subop->getIn(0)->getSize()) + return (PcodeOp *)0; // SUB is not high + n += 8*c; + + return subop; +} + /// \class RuleDivTermAdd2 /// \brief Simplify another expression associated with optimized division /// @@ -6364,42 +6402,92 @@ int4 RuleDivTermAdd2::applyOp(PcodeOp *op,Funcdata &data) return 0; } -/// \brief Check for shift form of expression +/// \brief Check for INT_(S)RIGHT and/or SUBPIECE followed by INT_MULT /// -/// Look for the two forms: -/// - `sub(V,c)` or -/// - `sub(V,c) >> n` +/// Look for the forms: +/// - `sub(ext(X) * #y,#c)` or +/// - `sub(ext(X) * #y,#c) >> n` or +/// - `(ext(X) * #y) >> n` /// -/// Pass back whether a shift was involved and the total truncation in bits: `n+c*8` +/// Looks for truncation/multiplication consistent with an optimized division. The +/// truncation can come as either a SUBPIECE operation and/or right shifts. +/// The numerand and the amount it has been extended is discovered. The extension +/// can be, but doesn't have to be, an explicit INT_ZEXT or INT_SEXT. If the form +/// doesn't match NULL is returned. If the Varnode holding the extended numerand +/// matches the final operand size, it is returned, otherwise the unextended numerand +/// is returned. The total truncation, the multiplicative constant, the numerand +/// size, and the extension type are all passed back. /// \param op is the root of the expression -/// \param n is the reference that will hold the total truncation -/// \param shiftopc will hold the shift OpCode if used, CPUI_MAX otherwise -/// \return the SUBPIECE op if present or NULL otherwise -PcodeOp *RuleDivOpt::findSubshift(PcodeOp *op,int4 &n,OpCode &shiftopc) +/// \param n is the reference that will hold the total number of bits of truncation +/// \param y will hold the multiplicative constant +/// \param xsize will hold the number of (non-zero) bits in the numerand +/// \param extopc holds whether the extension is INT_ZEXT or INT_SEXT +/// \return the extended numerand if possible, or the unextended numerand, or NULL +Varnode *RuleDivOpt::findForm(PcodeOp *op,int4 &n,uintb &y,int4 &xsize,OpCode &extopc) -{ // SUB( .,#c) or SUB(.,#c)>>n return baseop and n+c*8 - // make SUB is high - PcodeOp *subop; - shiftopc = op->code(); - if (shiftopc != CPUI_SUBPIECE) { // Must be right shift - Varnode *vn = op->getIn(0); - if (!vn->isWritten()) return (PcodeOp *)0; - subop = vn->getDef(); - if (subop->code() != CPUI_SUBPIECE) return (PcodeOp *)0; - if (!op->getIn(1)->isConstant()) return (PcodeOp *)0; - n = op->getIn(1)->getOffset(); +{ + PcodeOp *curOp = op; + OpCode shiftopc = curOp->code(); + if (shiftopc == CPUI_INT_RIGHT || shiftopc == CPUI_INT_SRIGHT) { + Varnode *vn = curOp->getIn(0); + if (!vn->isWritten()) return (Varnode *)0; + Varnode *cvn = curOp->getIn(1); + if (!cvn->isConstant()) return (Varnode *)0; + n = cvn->getOffset(); + curOp = vn->getDef(); } else { - shiftopc = CPUI_MAX; // Indicate there was no shift - subop = op; - n = 0; + n = 0; // No initial shift + if (shiftopc != CPUI_SUBPIECE) return (Varnode *)0; // In this case SUBPIECE is not optional + shiftopc = CPUI_MAX; } - int4 c = subop->getIn(1)->getOffset(); - if (subop->getOut()->getSize() + c != subop->getIn(0)->getSize()) - return (PcodeOp *)0; // SUB is not high - n += 8*c; + if (curOp->code() == CPUI_SUBPIECE) { // Optional SUBPIECE + int4 c = curOp->getIn(1)->getOffset(); + Varnode *inVn = curOp->getIn(0); + if (!inVn->isWritten()) return (Varnode *)0; + if (curOp->getOut()->getSize() + c != inVn->getSize()) + return (Varnode *)0; // Must keep high bits + n += 8*c; + curOp = inVn->getDef(); + } + if (curOp->code() != CPUI_INT_MULT) return (Varnode *)0; // There MUST be an INT_MULT + Varnode *inVn = curOp->getIn(0); + if (!inVn->isWritten()) return (Varnode *)0; + if (curOp->getIn(1)->isConstantExtended(y) < 0) return (Varnode *)0; // There MUST be a constant - return subop; + Varnode *resVn; + PcodeOp *extOp = inVn->getDef(); + extopc = extOp->code(); + if (extopc != CPUI_INT_SEXT) { + uintb nzMask = inVn->getNZMask(); + xsize = 8*sizeof(uintb) - count_leading_zeros(nzMask); + if (xsize == 0) return (Varnode *)0; + if (xsize > 4*inVn->getSize()) return (Varnode *)0; + } + else + xsize = extOp->getIn(0)->getSize() * 8; + + if (extopc == CPUI_INT_ZEXT || extopc == CPUI_INT_SEXT) { + Varnode *extVn = extOp->getIn(0); + if (extVn->isFree()) return (Varnode *)0; + if (inVn->getSize() == op->getOut()->getSize()) + resVn = inVn; + else + resVn = extVn; + } + else { + extopc = CPUI_INT_ZEXT; // Treat as unsigned extension + resVn = inVn; + } + // Check for signed mismatch + if (((extopc == CPUI_INT_ZEXT)&&(shiftopc==CPUI_INT_SRIGHT))|| + ((extopc == CPUI_INT_SEXT)&&(shiftopc==CPUI_INT_RIGHT))) { + if (8*op->getOut()->getSize() - n != xsize) + return (Varnode *)0; + // op's signedness does not matter because all the extension + // bits are truncated + } + return resVn; } /// Given the multiplicative encoding \b y and the \b n, the power of 2, @@ -6468,75 +6556,53 @@ void RuleDivOpt::getOpList(vector &oplist) const int4 RuleDivOpt::applyOp(PcodeOp *op,Funcdata &data) { - PcodeOp *subop,*extop,*multop; - Varnode *subin,*xvn; - - int4 nint; - OpCode opc; - subop = findSubshift(op,nint,opc); - if (subop == (PcodeOp *)0) return 0; - if (!subop->getIn(0)->isWritten()) return 0; - multop = subop->getIn(0)->getDef(); - if (multop->code() != CPUI_INT_MULT) return 0; + int4 n,xsize; uintb y; - if (multop->getIn(1)->isConstantExtended(y) < 0) return 0; - if (!multop->getIn(0)->isWritten()) return 0; - uintb n = nint; - extop = multop->getIn(0)->getDef(); - OpCode extopc = extop->code(); - if ((extopc != CPUI_INT_ZEXT)&&(extopc != CPUI_INT_SEXT)) - return 0; - xvn = extop->getIn(0); - if (xvn->isFree()) return 0; - int4 xsize = xvn->getSize(); - subin = op->getOut(); - bool needInputExtension = false; - if (xsize != subin->getSize()) { - if (xsize > subin->getSize()) // Allow input to be smaller than eventual result - return 0; - needInputExtension = true; - xsize = subin->getSize(); - } - // Check for signed mismatch - if (((extopc == CPUI_INT_ZEXT)&&(opc==CPUI_INT_SRIGHT))|| - ((extopc == CPUI_INT_SEXT)&&(opc==CPUI_INT_RIGHT))) { - if (8*subin->getSize() - n != xsize*8) - return 0; - // op's signedness does not matter because all the extension - // bits are truncated - } - - int4 maxx = xsize * 8; - if (extopc == CPUI_INT_SEXT) - maxx -= 1; // one less bit for signed, because of signbit - uintb divisor = calcDivisor(n,y,maxx); + OpCode extOpc; + Varnode *inVn = findForm(op,n,y,xsize,extOpc); + if (inVn == (Varnode *)0) return 0; + if (extOpc == CPUI_INT_SEXT) + xsize -= 1; // one less bit for signed, because of signbit + uintb divisor = calcDivisor(n,y,xsize); if (divisor == 0) return 0; - if (needInputExtension) { // Do we need an extension to get to final size + int4 outSize = op->getOut()->getSize(); + + if (inVn->getSize() < outSize) { // Do we need an extension to get to final size PcodeOp *inExt = data.newOp(1,op->getAddr()); - data.opSetOpcode(inExt,extopc); - Varnode *extOut = data.newUniqueOut(xsize,inExt); - data.opSetInput(inExt,xvn,0); - xvn = extOut; + data.opSetOpcode(inExt,extOpc); + Varnode *extOut = data.newUniqueOut(outSize,inExt); + data.opSetInput(inExt,inVn,0); + inVn = extOut; data.opInsertBefore(inExt,op); } - if (extopc == CPUI_INT_ZEXT) { // Unsigned division - data.opSetInput(op,xvn,0); - data.opSetInput(op,data.newConstant(xsize,divisor),1); + else if (inVn->getSize() > outSize) { // Do we need a truncation to get to final size + PcodeOp *newop = data.newOp(2,op->getAddr()); // Create new op to hold the INT_DIV or INT_SDIV:INT_ADD + data.opSetOpcode(newop, CPUI_INT_ADD); // This gets changed immediately, but need it for opInsert + Varnode *resVn = data.newUniqueOut(inVn->getSize(), newop); + data.opInsertBefore(newop, op); + data.opSetOpcode(op, CPUI_SUBPIECE); // Original op becomes a truncation + data.opSetInput(op,resVn,0); + data.opSetInput(op,data.newConstant(4, 0),1); + op = newop; // Main transform now changes newop + } + if (extOpc == CPUI_INT_ZEXT) { // Unsigned division + data.opSetInput(op,inVn,0); + data.opSetInput(op,data.newConstant(outSize,divisor),1); data.opSetOpcode(op,CPUI_INT_DIV); } else { // Sign division PcodeOp *divop = data.newOp(2,op->getAddr()); data.opSetOpcode(divop,CPUI_INT_SDIV); - Varnode *newout = data.newUniqueOut(xsize,divop); - data.opSetInput(divop,xvn,0); - data.opSetInput(divop,data.newConstant(xsize,divisor),1); + Varnode *newout = data.newUniqueOut(outSize,divop); + data.opSetInput(divop,inVn,0); + data.opSetInput(divop,data.newConstant(outSize,divisor),1); data.opInsertBefore(divop,op); // Build the sign value correction PcodeOp *sgnop = data.newOp(2,op->getAddr()); data.opSetOpcode(sgnop,CPUI_INT_SRIGHT); - Varnode *sgnvn = data.newUniqueOut(xvn->getSize(),sgnop); - data.opSetInput(sgnop,xvn,0); - data.opSetInput(sgnop,data.newConstant(xvn->getSize(),xsize*8-1),1); + Varnode *sgnvn = data.newUniqueOut(inVn->getSize(),sgnop); + data.opSetInput(sgnop,inVn,0); + data.opSetInput(sgnop,data.newConstant(inVn->getSize(),outSize*8-1),1); data.opInsertBefore(sgnop,op); // Add the correction into the division op data.opSetInput(op,newout,0); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh index cfa31f3dbb..f75ace6b16 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh @@ -1083,6 +1083,7 @@ public: } virtual void getOpList(vector &oplist) const; virtual int4 applyOp(PcodeOp *op,Funcdata &data); + static PcodeOp *findSubshift(PcodeOp *op,int4 &n,OpCode &shiftopc); }; class RuleDivTermAdd2 : public Rule { @@ -1106,7 +1107,7 @@ public: } virtual void getOpList(vector &oplist) const; virtual int4 applyOp(PcodeOp *op,Funcdata &data); - static PcodeOp *findSubshift(PcodeOp *op,int4 &n,OpCode &shiftopc); + static Varnode *findForm(PcodeOp *op,int4 &n,uintb &y,int4 &xsize,OpCode &extopc); }; class RuleSignDiv2 : public Rule { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java index 01fdc302f6..498f7727fe 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java @@ -183,9 +183,16 @@ public class DecompileCallback { return null; } byte[] resbytes = new byte[size]; - program.getMemory().getBytes(addr, resbytes, 0, size); + int bytesRead = program.getMemory().getBytes(addr, resbytes, 0, size); if (debug != null) { - debug.getBytes(addr, resbytes); + if (bytesRead != size) { + byte[] debugBytes = new byte[bytesRead]; + System.arraycopy(resbytes, 0, debugBytes, 0, bytesRead); + debug.getBytes(addr, debugBytes); + } + else { + debug.getBytes(addr, resbytes); + } } return resbytes; }