GP-4859 RuleOrCompare

This commit is contained in:
caheckman 2024-08-20 20:16:26 +00:00
parent 2ef83410be
commit a31c4033a8
8 changed files with 141 additions and 234 deletions

View File

@ -50,6 +50,7 @@ src/decompile/datatests/noforloop_alias.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_globcall.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_iterused.xml||GHIDRA||||END|
src/decompile/datatests/offsetarray.xml||GHIDRA||||END|
src/decompile/datatests/orcompare.xml||GHIDRA||||END|
src/decompile/datatests/packstructaccess.xml||GHIDRA||||END|
src/decompile/datatests/partialmerge.xml||GHIDRA||||END|
src/decompile/datatests/partialsplit.xml||GHIDRA||||END|

View File

@ -5518,7 +5518,6 @@ void ActionDatabase::universalAction(Architecture *conf)
actprop->addRule( new RulePiece2Zext("analysis") );
actprop->addRule( new RulePiece2Sext("analysis") );
actprop->addRule( new RulePopcountBoolXor("analysis") );
actprop->addRule( new RuleOrMultiBool("analysis") );
actprop->addRule( new RuleXorSwap("analysis") );
actprop->addRule( new RuleLzcountShiftBool("analysis") );
actprop->addRule( new RuleFloatSign("analysis") );

View File

@ -4,9 +4,9 @@
* 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.
@ -1124,9 +1124,6 @@ int4 SplitVarnode::applyRuleIn(SplitVarnode &in,Funcdata &data)
break;
case CPUI_INT_OR:
{
Equal2Form equal2form;
if (equal2form.applyRule(in,workop,workishi,data))
return 1;
LogicalForm logicalform;
if (logicalform.applyRule(in,workop,workishi,data))
return 1;
@ -1134,9 +1131,6 @@ int4 SplitVarnode::applyRuleIn(SplitVarnode &in,Funcdata &data)
break;
case CPUI_INT_XOR:
{
Equal2Form equal2form;
if (equal2form.applyRule(in,workop,workishi,data))
return 1;
LogicalForm logicalform;
if (logicalform.applyRule(in,workop,workishi,data))
return 1;
@ -1151,6 +1145,9 @@ int4 SplitVarnode::applyRuleIn(SplitVarnode &in,Funcdata &data)
Equal1Form equal1form;
if (equal1form.applyRule(in,workop,workishi,data))
return 1;
Equal2Form equal2form;
if (equal2form.applyRule(in,workop,workishi,data))
return 1;
}
break;
case CPUI_INT_LESS:
@ -1870,95 +1867,30 @@ bool Equal1Form::applyRule(SplitVarnode &i,PcodeOp *hop,bool workishi,Funcdata &
return false;
}
bool Equal2Form::checkLoForm(void)
{ // Assuming we have equal <- or <- xor <- hi1, verify if we have the full equal form
Varnode *orvnin = orop->getIn(1-orhislot);
if (orvnin == lo1) { // lo2 is an implied 0
loxor = (PcodeOp *)0;
lo2 = (Varnode *)0;
return true;
}
if (!orvnin->isWritten()) return false;
loxor = orvnin->getDef();
if (loxor->code() != CPUI_INT_XOR) return false;
if (loxor->getIn(0) == lo1) {
lo2 = loxor->getIn(1);
return true;
}
else if (loxor->getIn(1) == lo1) {
lo2 = loxor->getIn(0);
return true;
}
return false;
}
bool Equal2Form::fillOutFromOr(Funcdata &data)
{ // We have filled in either or <- xor <- hi1, OR, or <- hi1
// Now try to fill in the rest of the form
Varnode *outvn = orop->getOut();
list<PcodeOp *>::const_iterator iter,enditer;
iter = outvn->beginDescend();
enditer = outvn->endDescend();
while(iter != enditer) {
equalop = *iter;
++iter;
if ((equalop->code() != CPUI_INT_EQUAL)&&(equalop->code() != CPUI_INT_NOTEQUAL)) continue;
if (!equalop->getIn(1)->isConstant()) continue;
if (equalop->getIn(1)->getOffset() != 0) continue;
if (!checkLoForm()) continue;
if (!replace(data)) continue;
return true;
}
return false;
}
bool Equal2Form::replace(Funcdata &data)
{
if ((hi2==(Varnode *)0)&&(lo2==(Varnode *)0)) {
param2.initPartial(in.getSize(),0); // Double precis zero constant
return SplitVarnode::prepareBoolOp(in,param2,equalop);
}
if ((hi2==(Varnode *)0)&&(lo2->isConstant())) {
param2.initPartial(in.getSize(),lo2->getOffset());
return SplitVarnode::prepareBoolOp(in,param2,equalop);
}
if ((lo2==(Varnode *)0)&&(hi2->isConstant())) {
param2.initPartial(in.getSize(),hi2->getOffset() << 8*lo1->getSize());
return SplitVarnode::prepareBoolOp(in,param2,equalop);
}
if (lo2 == (Varnode *)0) {
// Equal to a zero extended and shifted var
return false;
}
if (hi2 == (Varnode *)0) {
// Equal to a zero extended var
return false;
}
if (hi2->isConstant()&&lo2->isConstant()) {
uintb val = hi2->getOffset();
val <<= 8*lo1->getSize();
val |= lo2->getOffset();
param2.initPartial(in.getSize(),val);
return SplitVarnode::prepareBoolOp(in,param2,equalop);
return SplitVarnode::prepareBoolOp(in,param2,boolAndOr);
}
if (hi2->isConstant()||lo2->isConstant()) {
// Some kind of mixed form
return false;
}
param2.initPartial(in.getSize(),lo2,hi2);
return SplitVarnode::prepareBoolOp(in,param2,equalop);
return SplitVarnode::prepareBoolOp(in,param2,boolAndOr);
}
// Given a known double precis input, look for double precision compares of the form
// a == b, a != b
//
// We look for
// res = ((hi1 ^ hi2) | (lo1 ^ lo2) == 0)
// where hi2 or lo2 may be zero, and optimized out
// res = (hi1 == hi2) && (lo1 == lo2) or
// res = (hi1 != hi2) || (lo1 != lo2)
bool Equal2Form::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data)
{
@ -1967,41 +1899,37 @@ bool Equal2Form::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &d
in = i;
hi1 = in.getHi();
lo1 = in.getLo();
if (op->code() == CPUI_INT_OR) {
orop = op;
orhislot = op->getSlot(hi1);
hixor = (PcodeOp *)0;
hi2 = (Varnode *)0;
if (fillOutFromOr(data)) {
if (!param2.exceedsConstPrecision()) {
SplitVarnode::replaceBoolOp(data,equalop,in,param2,equalop->code());
return true;
}
OpCode eqCode = op->code();
int4 hi1slot = op->getSlot(hi1);
hi2 = op->getIn(1-hi1slot);
Varnode *outvn = op->getOut();
list<PcodeOp *>::const_iterator iter,enditer;
iter = outvn->beginDescend();
enditer = outvn->endDescend();
while(iter != enditer) {
boolAndOr = *iter;
++iter;
if (eqCode == CPUI_INT_EQUAL && boolAndOr->code() != CPUI_BOOL_AND) continue;
if (eqCode == CPUI_INT_NOTEQUAL && boolAndOr->code() != CPUI_BOOL_OR) continue;
int4 slot = boolAndOr->getSlot(outvn);
Varnode *othervn = boolAndOr->getIn(1-slot);
if (!othervn->isWritten()) continue;
PcodeOp *equalLo = othervn->getDef();
if (equalLo->code() != eqCode) continue;
if (equalLo->getIn(0) == lo1) {
lo2 = equalLo->getIn(1);
}
}
else { // We see an XOR
hixor = op;
xorhislot = hixor->getSlot(hi1);
hi2 = hixor->getIn(1-xorhislot);
Varnode *vn = op->getOut();
list<PcodeOp *>::const_iterator iter,enditer;
iter = vn->beginDescend();
enditer = vn->endDescend();
while(iter != enditer) {
orop = *iter;
++iter;
if (orop->code() != CPUI_INT_OR) continue;
orhislot = orop->getSlot(vn);
if (fillOutFromOr(data)) {
if (!param2.exceedsConstPrecision()) {
SplitVarnode::replaceBoolOp(data,equalop,in,param2,equalop->code());
return true;
}
}
else if (equalLo->getIn(1) == lo1) {
lo2 = equalLo->getIn(0);
}
else {
continue;
}
if (!replace(data)) continue;
if (param2.exceedsConstPrecision()) continue;
SplitVarnode::replaceBoolOp(data,boolAndOr,in,param2,eqCode);
return true;
}
return false;
}

View File

@ -4,9 +4,9 @@
* 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.
@ -160,12 +160,8 @@ public:
class Equal2Form {
SplitVarnode in;
Varnode *hi1,*hi2,*lo1,*lo2;
PcodeOp *equalop,*orop;
PcodeOp *hixor,*loxor;
int4 orhislot,xorhislot;
PcodeOp *boolAndOr;
SplitVarnode param2;
bool checkLoForm(void);
bool fillOutFromOr(Funcdata &data);
bool replace(Funcdata &data);
public:
bool applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data);

View File

@ -4,9 +4,9 @@
* 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.
@ -672,20 +672,33 @@ uintb PcodeOp::getNZMaskLocal(bool cliploop) const
case CPUI_INT_MULT:
val = getIn(0)->getNZMask();
resmask = getIn(1)->getNZMask();
sz1 = (size > sizeof(uintb)) ? 8*size-1 : mostsigbit_set(val);
if (sz1 == -1)
resmask = 0;
if (size > sizeof(uintb)) {
resmask = fullmask;
}
else {
sz2 = (size > sizeof(uintb)) ? 8*size-1 : mostsigbit_set(resmask);
if (sz2 == -1)
sz1 = mostsigbit_set(val);
sz2 = mostsigbit_set(resmask);
if (sz1 == -1 || sz2 == -1) {
resmask = 0;
}
else {
if (sz1 + sz2 < 8*size-2)
fullmask >>= (8*size-2-sz1-sz2);
sz1 = leastsigbit_set(val);
sz2 = leastsigbit_set(resmask);
resmask = (~((uintb)0))<<(sz1+sz2);
resmask &= fullmask;
int4 l1 = leastsigbit_set(val);
int4 l2 = leastsigbit_set(resmask);
sa = l1 + l2;
if (sa >= 8*size) {
resmask = 0;
}
else {
sz1 = sz1 - l1 + 1;
sz2 = sz2 - l2 + 1;
int4 total = sz1 + sz2;
if (sz1 == 1 || sz2 == 1)
total -= 1;
resmask = fullmask;
if (total < 8 * size)
resmask >>= (8*size - total);
resmask = (resmask << sa) & fullmask;
}
}
}
break;

View File

@ -10101,66 +10101,6 @@ Varnode *RulePopcountBoolXor::getBooleanResult(Varnode *vn,int4 bitPos,int4 &con
}
}
/// \class RuleOrMultiBool
/// \brief Simplify boolean expressions that are combined through INT_OR
///
/// Convert expressions involving boolean values b1 and b2:
/// - `(b1 << 6) | (b2 << 2) != 0 => b1 || b2
void RuleOrMultiBool::getOpList(vector<uint4> &oplist) const
{
oplist.push_back(CPUI_INT_OR);
}
int4 RuleOrMultiBool::applyOp(PcodeOp *op,Funcdata &data)
{
Varnode *outVn = op->getOut();
list<PcodeOp *>::const_iterator iter;
if (popcount(outVn->getNZMask()) != 2) return 0;
for(iter=outVn->beginDescend();iter!=outVn->endDescend();++iter) {
PcodeOp *baseOp = *iter;
OpCode opc = baseOp->code();
// Result of INT_OR must be compared with zero
if (opc != CPUI_INT_EQUAL && opc != CPUI_INT_NOTEQUAL) continue;
Varnode *zerovn = baseOp->getIn(1);
if (!zerovn->isConstant()) continue;
if (zerovn->getOffset() != 0) continue;
int4 pos0 = leastsigbit_set(outVn->getNZMask());
int4 pos1 = mostsigbit_set(outVn->getNZMask());
int4 constRes0,constRes1;
Varnode *b1 = RulePopcountBoolXor::getBooleanResult(outVn, pos0, constRes0);
if (b1 == (Varnode *)0 && constRes0 != 1) continue;
Varnode *b2 = RulePopcountBoolXor::getBooleanResult(outVn, pos1, constRes1);
if (b2 == (Varnode *)0 && constRes1 != 1) continue;
if (b1 == (Varnode *)0 && b2 == (Varnode *)0) continue;
if (b1 == (Varnode *)0)
b1 = data.newConstant(1, 1);
if (b2 == (Varnode *)0)
b2 = data.newConstant(1, 1);
if (opc == CPUI_INT_EQUAL) {
PcodeOp *newOp = data.newOp(2,baseOp->getAddr());
Varnode *notIn = data.newUniqueOut(1, newOp);
data.opSetOpcode(newOp, CPUI_BOOL_OR);
data.opSetInput(newOp, b1, 0);
data.opSetInput(newOp, b2, 1);
data.opInsertBefore(newOp, baseOp);
data.opRemoveInput(baseOp, 1);
data.opSetInput(baseOp, notIn, 0);
data.opSetOpcode(baseOp, CPUI_BOOL_NEGATE);
}
else {
data.opSetOpcode(baseOp, CPUI_BOOL_OR);
data.opSetInput(baseOp, b1, 0);
data.opSetInput(baseOp, b2, 1);
}
return 1;
}
return 0;
}
/// \brief Return \b true if concatenating with a SUBPIECE of the given Varnode is unusual
///
/// \param vn is the given Varnode
@ -10550,53 +10490,65 @@ int4 RuleFloatSignCleanup::applyOp(PcodeOp *op,Funcdata &data)
void RuleOrCompare::getOpList(vector<uint4> &oplist) const
{
oplist.push_back(CPUI_INT_EQUAL);
oplist.push_back(CPUI_INT_NOTEQUAL);
oplist.push_back(CPUI_INT_OR);
}
int4 RuleOrCompare::applyOp(PcodeOp *op,Funcdata &data)
{
// make sure the comparison is against 0
if (! op->getIn(1)->constantMatch(0)) return 0;
Varnode *outvn = op->getOut();
list<PcodeOp *>::const_iterator iter;
bool hasCompares = false;
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
PcodeOp *compOp = *iter;
OpCode opc = compOp->code();
if (opc != CPUI_INT_EQUAL && opc != CPUI_INT_NOTEQUAL)
return 0;
if (!compOp->getIn(1)->constantMatch(0))
return 0;
hasCompares = true;
}
if (!hasCompares)
return 0;
// make sure the other operand is an INT_OR
PcodeOp *or_op = op->getIn(0)->getDef();
if (or_op == (PcodeOp *)0) return 0;
if (or_op->code() != CPUI_INT_OR) return 0;
Varnode* V = or_op->getIn(0);
Varnode* W = or_op->getIn(1);
Varnode* V = op->getIn(0);
Varnode* W = op->getIn(1);
// make sure V and W are in SSA form
if (V->isFree()) return 0;
if (W->isFree()) return 0;
// construct the new segment:
// if the original condition was INT_EQUAL: BOOL_AND(INT_EQUAL(V, 0:|V|), INT_EQUAL(W, 0:|W|))
// if the original condition was INT_NOTEQUAL: BOOL_OR(INT_NOTEQUAL(V, 0:|V|), INT_NOTEQUAL(W, 0:|W|))
Varnode* zero_V = data.newConstant(V->getSize(), 0);
Varnode* zero_W = data.newConstant(W->getSize(), 0);
PcodeOp* eq_V = data.newOp(2, op->getAddr());
data.opSetOpcode(eq_V, op->code());
data.opSetInput(eq_V, V, 0);
data.opSetInput(eq_V, zero_V, 1);
PcodeOp* eq_W = data.newOp(2, op->getAddr());
data.opSetOpcode(eq_W, op->code());
data.opSetInput(eq_W, W, 0);
data.opSetInput(eq_W, zero_W, 1);
iter = outvn->beginDescend();
while(iter!=outvn->endDescend()) {
PcodeOp *equalOp = *iter;
OpCode opc = equalOp->code();
++iter; // Advance iterator immediately as equalOp gets modified
// construct the new segment:
// if the original condition was INT_EQUAL: BOOL_AND(INT_EQUAL(V, 0:|V|), INT_EQUAL(W, 0:|W|))
// if the original condition was INT_NOTEQUAL: BOOL_OR(INT_NOTEQUAL(V, 0:|V|), INT_NOTEQUAL(W, 0:|W|))
Varnode* zero_V = data.newConstant(V->getSize(), 0);
Varnode* zero_W = data.newConstant(W->getSize(), 0);
PcodeOp* eq_V = data.newOp(2, equalOp->getAddr());
data.opSetOpcode(eq_V, opc);
data.opSetInput(eq_V, V, 0);
data.opSetInput(eq_V, zero_V, 1);
PcodeOp* eq_W = data.newOp(2, equalOp->getAddr());
data.opSetOpcode(eq_W, opc);
data.opSetInput(eq_W, W, 0);
data.opSetInput(eq_W, zero_W, 1);
Varnode* eq_V_out = data.newUniqueOut(1, eq_V);
Varnode* eq_W_out = data.newUniqueOut(1, eq_W);
Varnode* eq_V_out = data.newUniqueOut(1, eq_V);
Varnode* eq_W_out = data.newUniqueOut(1, eq_W);
// make sure the comparisons' output is already defined
data.opInsertBefore(eq_V, op);
data.opInsertBefore(eq_W, op);
// make sure the comparisons' output is already defined
data.opInsertBefore(eq_V, equalOp);
data.opInsertBefore(eq_W, equalOp);
// change the original INT_EQUAL into a BOOL_AND, and INT_NOTEQUAL becomes BOOL_OR
data.opSetOpcode(op, op->code() == CPUI_INT_EQUAL ? CPUI_BOOL_AND : CPUI_BOOL_OR);
data.opSetInput(op, eq_V_out, 0);
data.opSetInput(op, eq_W_out, 1);
// change the original INT_EQUAL into a BOOL_AND, and INT_NOTEQUAL becomes BOOL_OR
data.opSetOpcode(equalOp, opc == CPUI_INT_EQUAL ? CPUI_BOOL_AND : CPUI_BOOL_OR);
data.opSetInput(equalOp, eq_V_out, 0);
data.opSetInput(equalOp, eq_W_out, 1);
}
return 1;
}

View File

@ -1598,17 +1598,6 @@ public:
static Varnode *getBooleanResult(Varnode *vn,int4 bitPos,int4 &constRes);
};
class RuleOrMultiBool : public Rule {
public:
RuleOrMultiBool(const string &g) : Rule( g, 0, "ormultibool") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Rule *)0;
return new RuleOrMultiBool(getGroup());
}
virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RulePiecePathology : public Rule {
static bool isPathology(Varnode *vn,Funcdata &data);
static int4 tracePathologyForward(PcodeOp *op,Funcdata &data);

View File

@ -0,0 +1,29 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<bytechunk space="ram" offset="0x100000" readonly="true">
f30f1efa83ff0a0f94c20fb6d201d283
fe140f94c00fb6c0c1e007b901000000
09c2740f833dd50f0000000f94c10fb6
c901c989c8c3f30f1efa81fec8000000
0f94c00fb6c0c1e00283ff640f94c10f
b6c909c881fa2c0100000f94c20fb6d2
c1e20409d07510833d920f000007ba02
0000000f44c2c3b801000000c3
</bytechunk>
<symbol space="ram" offset="0x100000" name="comp2"/>
<symbol space="ram" offset="0x100036" name="comp3"/>
</binaryimage>
<script>
<com>parse line extern int4 comp2(int4 a,int4 b);</com>
<com>parse line extern int4 comp3(int4 x,int4 y,int4 z);</com>
<com>lo fu comp2</com>
<com>dec</com>
<com>print C</com>
<com>lo fu comp3</com>
<com>dec</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Compare INT_OR #1" min="1" max="1">if \(a == 10 \|\| b == 0x14\)</stringmatch>
<stringmatch name="Compare INT_OR #2" min="1" max="1">if \(\(y != 200 &amp;&amp; x != 100\) &amp;&amp; z != 300\)</stringmatch>
</decompilertest>