fold/third_party/ghidra-decompiler/cpp/expression.cc
trvon 5152d0c9f7
feat(binary): vendor Ghidra 12.1 decompiler + validate P-Code lifting (Phase 0-1)
Foundation for replacing fold's dead/abstract binary encoder with a real
SSA P-Code frontend. Verified root-cause gap: serializeBinaryIR() emits only
data-discovery nodes (sections/strings/imports/exports); the control-flow
BinaryExprKind values (BasicBlock/CallSite/...) are never constructed, so the
Z3-BB encoder's encodable filter is always empty and encodeBinaryBBs never
runs — hence 0 binary Z3 findings. The P-Code pipeline supersedes that path.

Phase 0 — docs/internal/ghidra-pcode-integration-research.md: P-Code opcode
core, the decompiler C++ embedding API (LoadImage/SleighArchitecture/Funcdata),
the CORE+DECCORE+SLEIGH source sets, no-bison finding, and pypcode as build
reference.

Phase 1 — vendored Ghidra decompiler at pinned commit (Ghidra_12.1_build,
7e89d94e34...) under third_party/ghidra-decompiler/ (cpp + AArch64 specs +
Apache-2.0 LICENSE + VENDOR.md + .gitignore for build artifacts). Validated by
hand on this clang-21/macOS toolchain:
  - full SSA engine (CORE+DECCORE+SLEIGH, 80 files incl. heritage/funcdata/
    action) compiles clean, -std=c++11 -O2, NO BFD, NO bison (generated
    parsers are committed upstream; ruleparse isn't in the lib set);
  - sleigh compiler builds; AArch64 .slaspec -> .sla compiles;
  - tools/pcode_dump.cpp (custom LoadImage, no BFD) lifts the real arm64
    sym._div_by_zero_direct to correct P-Code: `mov w9,#0 -> COPY(const 0)`,
    `sdiv -> INT_SDIV(w8,w9)` — divisor zero-ness explicit in the data flow,
    so the Phase-3 encoder proves div-by-zero trivially, multi-arch.

Not yet meson-wired (next step) and no fold-side IR/encoder yet (Phase 2-3);
this commit is the validated vendor + smoke tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 17:32:47 -04:00

804 lines
26 KiB
C++

/* ###
* 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.
*/
#include "expression.hh"
#include "database.hh"
namespace ghidra {
/// \brief Return \b true if the alternate path looks more valid than the main path.
///
/// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN.
/// Evaluate which path most likely represents actual parameter/return value passing,
/// based on traversal information about each path.
/// \param vn is the Varnode terminating the \e alternate path
/// \param flags indicates traversals for both paths
/// \return \b true if the alternate path is preferred
bool TraverseNode::isAlternatePathValid(const Varnode *vn,uint4 flags)
{
if ((flags & (indirect | indirectalt)) == indirect)
// If main path traversed an INDIRECT but the alternate did not
return true; // Main path traversed INDIRECT, alternate did not
if ((flags & (indirect | indirectalt)) == indirectalt)
return false; // Alternate path traversed INDIRECT, main did not
if ((flags & actionalt) != 0)
return true; // Alternate path traversed a dedicated COPY
if (vn->loneDescend() == (PcodeOp*)0) return false;
const PcodeOp *op = vn->getDef();
if (op == (PcodeOp*)0) return true;
while(op->isIncidentalCopy() && op->code() == CPUI_COPY) { // Skip any incidental COPY
vn = op->getIn(0);
if (vn->loneDescend() == (PcodeOp *)0) return false;
op = vn->getDef();
if (op == (PcodeOp *)0) return true;
}
return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values
}
/// \brief Test if two operations with same opcode produce complementary boolean values
///
/// This only tests for cases where the opcode is INT_LESS or INT_SLESS and one of the
/// inputs is constant.
/// \param bin1op is the first p-code op to compare
/// \param bin2op is the second p-code op to compare
/// \return \b true if the two operations always produce complementary values
bool BooleanMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op)
{
OpCode opcode = bin1op->code();
if ((opcode == CPUI_INT_SLESS)||(opcode==CPUI_INT_LESS)) {
// Basically we test for the scenario like: x < 9 8 < x
int4 constslot = 0;
if (bin1op->getIn(1)->isConstant())
constslot = 1;
if (!bin1op->getIn(constslot)->isConstant()) return false;
if (!bin2op->getIn(1-constslot)->isConstant()) return false;
if (!varnodeSame(bin1op->getIn(1-constslot),bin2op->getIn(constslot))) return false;
uintb val1 = bin1op->getIn(constslot)->getOffset();
uintb val2 = bin2op->getIn(1-constslot)->getOffset();
if (constslot!=0) {
uintb tmp = val2;
val2 = val1;
val1 = tmp;
}
if (val1 + 1 != val2) return false;
if ((val2 == 0)&&(opcode==CPUI_INT_LESS)) return false; // Corner case for unsigned
if (opcode==CPUI_INT_SLESS) { // Corner case for signed
int4 sz = bin1op->getIn(constslot)->getSize();
if (signbit_negative(val2,sz) && (!signbit_negative(val1,sz)))
return false;
}
return true;
}
return false;
}
/// \brief Do the given Varnodes hold the same value, possibly as constants
///
/// \param a is the first Varnode to compare
/// \param b is the second Varnode
/// \return \b true if the Varnodes (always) hold the same value
bool BooleanMatch::varnodeSame(Varnode *a,Varnode *b)
{
if (a == b) return true;
if (a->isConstant() && b->isConstant())
return (a->getOffset() == b->getOffset());
return false;
}
/// \brief Determine if two boolean Varnodes hold related values
///
/// The values may be the \e same, or opposite of each other (\e complementary).
/// Otherwise the values are \e uncorrelated. The trees constructing each Varnode
/// are examined up to a maximum \b depth. If this is exceeded \e uncorrelated is returned.
/// \param vn1 is the first boolean Varnode
/// \param vn2 is the second boolean Varnode
/// \param depth is the maximum depth to traverse in the evaluation
/// \return the correlation class
int4 BooleanMatch::evaluate(Varnode *vn1,Varnode *vn2,int4 depth)
{
if (vn1 == vn2) return same;
PcodeOp *op1,*op2;
OpCode opc1,opc2;
if (vn1->isWritten()) {
op1 = vn1->getDef();
opc1 = op1->code();
if (opc1 == CPUI_BOOL_NEGATE) {
int res = evaluate(op1->getIn(0),vn2,depth);
if (res == same) // Flip same <-> complementary result
res = complementary;
else if (res == complementary)
res = same;
return res;
}
}
else {
op1 = (PcodeOp *)0; // Don't give up before checking if op2 is BOOL_NEGATE
opc1 = CPUI_MAX;
}
if (vn2->isWritten()) {
op2 = vn2->getDef();
opc2 = op2->code();
if (opc2 == CPUI_BOOL_NEGATE) {
int4 res = evaluate(vn1,op2->getIn(0),depth);
if (res == same) // Flip same <-> complementary result
res = complementary;
else if (res == complementary)
res = same;
return res;
}
}
else
return uncorrelated;
if (op1 == (PcodeOp *)0)
return uncorrelated;
if (!op1->isBoolOutput() || !op2->isBoolOutput())
return uncorrelated;
if (depth != 0 && (opc1 == CPUI_BOOL_AND || opc1 == CPUI_BOOL_OR || opc1 == CPUI_BOOL_XOR)) {
if (opc2 == CPUI_BOOL_AND || opc2 == CPUI_BOOL_OR || opc2 == CPUI_BOOL_XOR) {
if (opc1 == opc2 || (opc1 == CPUI_BOOL_AND && opc2 == CPUI_BOOL_OR) || (opc1 == CPUI_BOOL_OR && opc2 == CPUI_BOOL_AND)) {
int4 pair1 = evaluate(op1->getIn(0),op2->getIn(0),depth-1);
int4 pair2;
if (pair1 == uncorrelated) {
pair1 = evaluate(op1->getIn(0),op2->getIn(1),depth-1); // Try other possible pairing (commutative op)
if (pair1 == uncorrelated)
return uncorrelated;
pair2 = evaluate(op1->getIn(1),op2->getIn(0),depth-1);
}
else {
pair2 = evaluate(op1->getIn(1),op2->getIn(1),depth-1);
}
if (pair2 == uncorrelated)
return uncorrelated;
if (opc1 == opc2) {
if (pair1 == same && pair2 == same)
return same;
else if (opc1 == CPUI_BOOL_XOR) {
if (pair1 == complementary && pair2 == complementary)
return same;
return complementary;
}
}
else { // Must be CPUI_BOOL_AND and CPUI_BOOL_OR
if (pair1 == complementary && pair2 == complementary)
return complementary; // De Morgan's Law
}
}
}
}
else {
// Two boolean output ops, compare them directly
if (opc1 == opc2) {
bool sameOp = true;
int numInputs = op1->numInput();
for (int i = 0; i < numInputs; ++i){
if (!varnodeSame(op1->getIn(i),op2->getIn(i))){
sameOp = false;
break;
}
}
if (sameOp){
return same;
}
if (sameOpComplement(op1,op2)) {
return complementary;
}
return uncorrelated;
}
// Check if the binary ops are complements of one another
int4 slot1 = 0;
int4 slot2 = 0;
bool reorder;
if (opc1 != get_booleanflip(opc2,reorder))
return uncorrelated;
if (reorder) slot2 = 1;
if (!varnodeSame(op1->getIn(slot1),op2->getIn(slot2)))
return uncorrelated;
if (!varnodeSame(op1->getIn(1-slot1),op2->getIn(1-slot2)))
return uncorrelated;
return complementary;
}
return uncorrelated;
}
const int4 BooleanExpressionMatch::maxDepth = 1;
bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop)
{
int4 res = BooleanMatch::evaluate(op->getIn(1), iop->getIn(1), maxDepth);
if (res == BooleanMatch::uncorrelated)
return false;
matchflip = (res == BooleanMatch::complementary);
if (op->isBooleanFlip())
matchflip = !matchflip;
if (iop->isBooleanFlip())
matchflip = !matchflip;
return true;
}
/// Assuming root->getOut() is the root of an expression formed with the
/// CPUI_INT_ADD op, collect all the Varnode \e terms of the expression.
void TermOrder::collect(void)
{
Varnode *curvn;
PcodeOp *curop;
PcodeOp *subop,*multop;
vector<PcodeOp *> opstack; // Depth first traversal path
vector<PcodeOp *> multstack;
opstack.push_back(root);
multstack.push_back((PcodeOp *)0);
while(!opstack.empty()) {
curop = opstack.back();
multop = multstack.back();
opstack.pop_back();
multstack.pop_back();
for(int4 i=0;i<curop->numInput();++i) {
curvn = curop->getIn(i); // curvn is a node of the subtree IF
if (!curvn->isWritten()) { // curvn is not defined by another operation
terms.emplace_back(curop,i,multop);
continue;
}
if (curvn->loneDescend() == (PcodeOp *)0) { // curvn has more then one use
terms.emplace_back(curop,i,multop);
continue;
}
subop = curvn->getDef();
if (subop->code() != CPUI_INT_ADD) { // or if curvn is defined with some other type of op
if ((subop->code()==CPUI_INT_MULT)&&(subop->getIn(1)->isConstant())) {
PcodeOp *addop = subop->getIn(0)->getDef();
if ((addop!=(PcodeOp *)0)&&(addop->code()==CPUI_INT_ADD)) {
if (addop->getOut()->loneDescend()!=(PcodeOp *)0) {
opstack.push_back(addop);
multstack.push_back(subop);
continue;
}
}
}
terms.emplace_back(curop,i,multop);
continue;
}
opstack.push_back(subop);
multstack.push_back(multop);
}
}
}
void TermOrder::sortTerms(void)
{
sorter.reserve(terms.size());
for(vector<AdditiveEdge>::iterator iter=terms.begin();iter!=terms.end();++iter)
sorter.push_back( &(*iter) );
sort(sorter.begin(),sorter.end(),additiveCompare);
}
/// The value \b true is returned if it can be proven that two terms add the same value to their
/// respective expressions.
/// \param op2 is the other term to compare with \b this
/// \return \b true if the terms are equivalent
bool AddExpression::Term::isEquivalent(const Term &op2) const
{
if (coeff != op2.coeff) return false;
return functionalEquality(vn,op2.vn);
}
/// The value \b true is returned if it can be proven that the expressions always produce the same value.
/// \param op2 is the other expression to compare with \b this
/// \return \b true if the expressions are equivalent
bool AddExpression::isEquivalent(const AddExpression &op2) const
{
if (constval != op2.constval)
return false;
if (numTerms != op2.numTerms) return false;
if (numTerms == 1) {
if (terms[0].isEquivalent(op2.terms[0]))
return true;
}
else if (numTerms == 2) {
if (terms[0].isEquivalent(op2.terms[0]) && terms[1].isEquivalent(op2.terms[1]))
return true;
if (terms[0].isEquivalent(op2.terms[1]) && terms[1].isEquivalent(op2.terms[0]))
return true;
}
return false;
}
/// Recursively collect terms, up to the given depth. INT_ADD either contributes to the constant sum, or
/// it is recursively walked. Term coefficients are collected from INT_MULT with a constant.
/// \param vn is the root of the (sub)expression
/// \param coeff is the current multiplicative coefficient of the subexpression
/// \param depth is the current depth
void AddExpression::gather(Varnode *vn,uintb coeff,int4 depth)
{
if (vn->isConstant()) {
constval = constval + coeff * vn->getOffset();
constval &= calc_mask(vn->getSize());
return;
}
if (vn->isWritten()) {
PcodeOp *op = vn->getDef();
if (op->code() == CPUI_INT_ADD) {
if (!op->getIn(1)->isConstant())
depth -= 1;
if (depth >= 0) {
gather(op->getIn(0),coeff,depth);
gather(op->getIn(1),coeff,depth);
return;
}
}
else if (op->code() == CPUI_INT_MULT) {
if (op->getIn(1)->isConstant()) {
coeff = coeff * op->getIn(1)->getOffset();
coeff &= calc_mask(vn->getSize());
gather(op->getIn(0),coeff,depth);
return;
}
}
}
add(vn,coeff);
return;
}
/// Gather up to two non-constant additive terms, given two root Varnodes that are being subtracted.
/// \param a is the first root
/// \param b is the second root being subtracted from the first
void AddExpression::gatherTwoTermsSubtract(Varnode *a,Varnode *b)
{
int4 depth = (a->isConstant() || b->isConstant()) ? 1 : 0;
gather(a,(uintb)1,depth);
gather(b,calc_mask(b->getSize()),depth);
}
/// Gather up to two non-constant additive terms, given two root Varnodes being added
/// \param a is the first root
/// \param b is the second root being added to the first
void AddExpression::gatherTwoTermsAdd(Varnode *a,Varnode *b)
{
int4 depth = (a->isConstant() || b->isConstant()) ? 1 : 0;
gather(a,(uintb)1,depth);
gather(b,(uintb)1,depth);
}
/// Gather up to two non-constant additive terms in the expression at the given root.
/// \param root is the root Varnode
void AddExpression::gatherTwoTermsRoot(Varnode *root)
{
gather(root,(uintb)1,1);
}
/// Find the structure immediately containing the byte range described by the given data-type.
/// Then, given the least significant bit of the bitfield, find the structure immediately containing the bitfield,
/// which may be a different structure.
/// \param dt is the data-type describing the byte range
/// \param initByteOff is the initial offset of the byte range within the data-type (may be -1)
/// \param leastBitOff is the least significant bit of the bitfield within the byte range
/// \param isBigEndian is \b true if the data-type is stored in big endian memory
void BitFieldExpression::getStructures(Datatype *dt,int4 initByteOff,int4 leastBitOff,bool isBigEndian)
{
TypeStruct *encompassStruct;
theStruct = (TypeStruct *)0;
byteRangeOffset = (initByteOff < 0) ? 0 : initByteOff;
if (dt->getMetatype() == TYPE_PARTIALSTRUCT) {
TypePartialStruct *partial = (TypePartialStruct *)dt;
Datatype *structTmp = partial->getParent();
if (structTmp->getMetatype() != TYPE_STRUCT) return;
byteRangeOffset += partial->getOffset();
encompassStruct = (TypeStruct *)structTmp;
}
else if (dt->getMetatype() == TYPE_STRUCT) {
encompassStruct = (TypeStruct *)dt;
}
else
return;
int8 offset = byteRangeOffset;
leastBitOff /= 8;
if (isBigEndian)
offset += (dt->getSize() - leastBitOff - 1);
else
offset += leastBitOff;
theStruct = encompassStruct;
offsetToBitStruct = 0;
for(;;) {
int8 newoff;
Datatype *tmpDt = theStruct->getSubType(offset, &newoff);
if (tmpDt == (Datatype *)0) break;
if (tmpDt->getMetatype() != TYPE_STRUCT) break;
theStruct = (TypeStruct *)tmpDt;
offsetToBitStruct += (offset-newoff);
offset = newoff;
}
}
const Varnode *BitFieldExpression::recoverStructurePointer(const Varnode *vn,int4 offset)
{
if (offset == 0 && theStruct->getSize() == vn->getSize())
return vn;
else if (vn->isWritten()) {
const PcodeOp *ptrSub = vn->getDef();
if (ptrSub->code() == CPUI_PTRSUB) {
if ((int4)ptrSub->getIn(1)->getOffset() == offset) {
return ptrSub->getIn(0);
}
}
}
if (offset != 0)
return (const Varnode *)0;
return vn;
}
/// \param pull is the given ZPULL or SPULL
/// \return the corresponding bitfield description or null
const TypeBitField *BitFieldExpression::getPullField(const PcodeOp *pull)
{
BitFieldExpression expr;
const Varnode *inVn = pull->getIn(0);
int4 leastBitOff = (int4)pull->getIn(1)->getOffset();
int4 bitSize = (int4)pull->getIn(2)->getOffset();
bool isBig = inVn->getSpace()->isBigEndian();
Datatype *dt = inVn->getTypeReadFacing(pull);
expr.getStructures(dt, 0, leastBitOff, isBig);
if (expr.theStruct == (TypeStruct *)0)
return (const TypeBitField *)0;
BitRange range(expr.byteRangeOffset-expr.offsetToBitStruct,inVn->getSize(),leastBitOff,bitSize,isBig);
return expr.theStruct->findMatchingBitField(range);
}
InsertExpression::InsertExpression(const PcodeOp *insert)
{
insertOp = insert;
bitfield = (const TypeBitField *)0;
const Varnode *value = insertOp->getOut();
symbol = value->getHigh()->getSymbol();
if (symbol == (const Symbol *)0) return;
AddrSpace *spc = value->getSpace();
int4 leastBitOff = (int4)insertOp->getIn(2)->getOffset();
int4 bitSize = (int4)insertOp->getIn(3)->getOffset();
getStructures(symbol->getType(),value->getHigh()->getSymbolOffset(),leastBitOff,spc->isBigEndian());
if (theStruct == (TypeStruct *)0) return;
BitRange range(byteRangeOffset - offsetToBitStruct,value->getSize(),leastBitOff,bitSize,spc->isBigEndian());
bitfield = theStruct->findMatchingBitField(range);
}
/// \param insert is the CPUI_INSERT op
/// \return a mask with a 1 wherever bits are inserted by the op
uintb InsertExpression::getRangeMask(const PcodeOp *insert)
{
int4 leastBitOff = (int4)insert->getIn(2)->getOffset();
int4 bitSize = (int4)insert->getIn(3)->getOffset();
uintb res = 0;
res = ~res;
if (bitSize < 8 * sizeof(uintb))
res = ~(res << bitSize);
res <<= leastBitOff;
return res;
}
/// \param insert is the CPUI_INSERT op
/// \return mask with of the least significant bits
uintb InsertExpression::getLSBMask(const PcodeOp *insert)
{
int4 bitSize = (int4)insert->getIn(3)->getOffset();
uintb res = 0;
res = ~res;
if (bitSize < 8 * sizeof(uintb))
res = ~(res << bitSize);
return res;
}
InsertStoreExpression::InsertStoreExpression(const PcodeOp *store)
{
bitfield = (const TypeBitField *)0;
structPtr = (Varnode *)0;
theStruct = (TypeStruct *)0;
loadOp = (PcodeOp *)0;
const Varnode *value = store->getIn(2);
if (!value->isWritten()) return;
insertOp = value->getDef();
if (insertOp->code() != CPUI_INSERT) return;
const Varnode *dest = insertOp->getIn(0); // dest can either be a constant or LOAD
if (dest->isWritten()) {
loadOp = dest->getDef();
if (loadOp->code() != CPUI_LOAD) return;
}
else if (!dest->isConstant())
return;
AddrSpace *spc = store->getIn(0)->getSpaceFromConst();
int4 leastBitOff = (int4)insertOp->getIn(2)->getOffset();
int4 bitSize = (int4)insertOp->getIn(3)->getOffset();
getStructures(value->getTypeDefFacing(),0,leastBitOff,spc->isBigEndian());
if (theStruct == (TypeStruct *)0) return;
structPtr = recoverStructurePointer(store->getIn(1), byteRangeOffset);
if (structPtr == (const Varnode *)0) return;
BitRange range(byteRangeOffset - offsetToBitStruct,value->getSize(),leastBitOff,bitSize,spc->isBigEndian());
bitfield = theStruct->findMatchingBitField(range);
}
PullExpression::PullExpression(const PcodeOp *pull)
{
pullOp = pull;
bitfield = (const TypeBitField *)0;
theStruct = (TypeStruct *)0;
loadOp = (const PcodeOp *)0;
structPtr = (const Varnode *)0;
const Varnode *inVn = pullOp->getIn(0);
AddrSpace *spc;
Datatype *dt;
int4 offset;
if (inVn->isWritten() && inVn->getDef()->code() == CPUI_LOAD) {
loadOp = inVn->getDef();
spc = loadOp->getIn(0)->getSpaceFromConst();
dt = inVn->getTypeReadFacing(pullOp);
offset = 0;
}
else {
symbol = inVn->getHigh()->getSymbol();
if (symbol == (const Symbol *)0) return;
spc = inVn->getSpace();
dt = symbol->getType();
offset = inVn->getHigh()->getSymbolOffset();
}
int4 leastBitOff = (int4)pullOp->getIn(1)->getOffset();
int4 bitSize = (int4)pullOp->getIn(2)->getOffset();
getStructures(dt,offset,leastBitOff,spc->isBigEndian());
if (theStruct == (TypeStruct *)0) return;
if (loadOp != (const PcodeOp *)0) {
structPtr = recoverStructurePointer(loadOp->getIn(1), byteRangeOffset);
if (structPtr == (const Varnode *)0) return;
}
BitRange range(byteRangeOffset-offsetToBitStruct,inVn->getSize(),leastBitOff,bitSize,spc->isBigEndian());
bitfield = theStruct->findMatchingBitField(range);
}
/// \brief Perform basic comparison of two given Varnodes
///
/// Return
/// - 0 if \b vn1 and \b vn2 must hold same value
/// - -1 if they definitely don't hold same value
/// - 1 if the same value depends on ops writing to \b vn1 and \b vn2
/// \param vn1 is the first Varnode to compare
/// \param vn2 is the second
/// \return a code -1, 0, or 1
static int4 functionalEqualityLevel0(Varnode *vn1,Varnode *vn2)
{
if (vn1==vn2) return 0;
if (vn1->getSize() != vn2->getSize()) return -1;
if (vn1->isConstant()) {
if (vn2->isConstant()) {
return (vn1->getOffset() == vn2->getOffset()) ? 0 : -1;
}
return -1;
}
if (vn1->isFree() || vn2->isFree()) return -1;
return 1;
}
/// \brief Try to determine if \b vn1 and \b vn2 contain the same value
///
/// Return:
/// - -1, if they do \b not, or if it can't be immediately verified
/// - 0, if they \b do hold the same value
/// - >0, if the result is contingent on additional varnode pairs having the same value
/// In the last case, the varnode pairs are returned as (res1[i],res2[i]),
/// where the return value is the number of pairs.
/// \param vn1 is the first Varnode to compare
/// \param vn2 is the second Varnode
/// \param res1 is a reference to the first returned Varnode
/// \param res2 is a reference to the second returned Varnode
/// \return the result of the comparison
int4 functionalEqualityLevel(Varnode *vn1,Varnode *vn2,Varnode **res1,Varnode **res2)
{
int4 testval = functionalEqualityLevel0(vn1,vn2);
if (testval != 1)
return testval;
if (!vn1->isWritten() || !vn2->isWritten()) {
return -1; // Did not find at least one level of match
}
PcodeOp *op1 = vn1->getDef();
PcodeOp *op2 = vn2->getDef();
OpCode opc = op1->code();
if (opc != op2->code()) return -1;
int4 num = op1->numInput();
if (num != op2->numInput()) return -1;
if (op1->isMarker()) return -1;
if (op2->isCall()) return -1;
if (opc == CPUI_LOAD) {
// FIXME: We assume two loads produce the same
// result if the address is the same and the loads
// occur in the same instruction
if (op1->getAddr() != op2->getAddr()) return -1;
}
if (num >= 3) {
if (opc != CPUI_PTRADD) return -1; // If this is a PTRADD
if (op1->getIn(2)->getOffset() != op2->getIn(2)->getOffset()) return -1; // Make sure the elsize constant is equal
num = 2; // Otherwise treat as having 2 inputs
}
for(int4 i=0;i<num;++i) {
res1[i] = op1->getIn(i);
res2[i] = op2->getIn(i);
}
testval = functionalEqualityLevel0(res1[0],res2[0]);
if (testval == 0) { // A match locks in this comparison ordering
if (num==1) return 0;
testval = functionalEqualityLevel0(res1[1],res2[1]);
if (testval==0) return 0;
if (testval < 0) return -1;
res1[0] = res1[1]; // Match is contingent on second pair
res2[0] = res2[1];
return 1;
}
if (num == 1) return testval;
int4 testval2 = functionalEqualityLevel0(res1[1],res2[1]);
if (testval2 == 0) { // A match locks in this comparison ordering
return testval;
}
int4 unmatchsize;
if ((testval==1)&&(testval2==1))
unmatchsize = 2;
else
unmatchsize = -1;
if (!op1->isCommutative()) return unmatchsize;
// unmatchsize must be 2 or -1 here on a commutative operator,
// try flipping
int4 comm1 = functionalEqualityLevel0(res1[0],res2[1]);
int4 comm2 = functionalEqualityLevel0(res1[1],res2[0]);
if ((comm1==0) && (comm2==0))
return 0;
if ((comm1<0)||(comm2<0))
return unmatchsize;
if (comm1==0) { // AND (comm2==1)
res1[0] = res1[1]; // Left over unmatch is res1[1] and res2[0]
return 1;
}
if (comm2==0) { // AND (comm1==1)
res2[0] = res2[1]; // Left over unmatch is res1[0] and res2[1]
return 1;
}
// If we reach here (comm1==1) AND (comm2==1)
if (unmatchsize == 2) // If the original ordering wasn't impossible
return 2; // Prefer the original ordering
Varnode *tmpvn = res2[0]; // Otherwise swap the ordering
res2[0] = res2[1];
res2[1] = tmpvn;
return 2;
}
/// \brief Determine if two Varnodes hold the same value
///
/// Only return \b true if it can be immediately determined they are equivalent
/// \param vn1 is the first Varnode
/// \param vn2 is the second Varnode
/// \return true if they are provably equal
bool functionalEquality(Varnode *vn1,Varnode *vn2)
{
Varnode *buf1[2];
Varnode *buf2[2];
return (functionalEqualityLevel(vn1,vn2,buf1,buf2)==0);
}
/// \brief Return true if vn1 and vn2 are verifiably different values
///
/// This is actually a rather speculative test
/// \param vn1 is the first Varnode to compare
/// \param vn2 is the second Varnode
/// \param depth is the maximum level to recurse while testing
/// \return \b true if they are different
bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth)
{
PcodeOp *op1,*op2;
int4 i,num;
if (vn1 == vn2) return false;
if ((!vn1->isWritten())||(!vn2->isWritten())) {
if (vn1->isConstant() && vn2->isConstant())
return !(vn1->getAddr()==vn2->getAddr());
if (vn1->isInput()&&vn2->isInput()) return false; // Might be the same
if (vn1->isFree()||vn2->isFree()) return false; // Might be the same
return true;
}
op1 = vn1->getDef();
op2 = vn2->getDef();
if (op1->code() != op2->code()) return true;
num = op1->numInput();
if (num != op2->numInput()) return true;
if (depth==0) return true; // Different as far as we can tell
depth -= 1;
for(i=0;i<num;++i)
if (functionalDifference(op1->getIn(i),op2->getIn(i),depth))
return true;
return false;
}
/// \brief Back-track as far as possible from a pointer Varnode thru PTRSUB, INT_ADD, and COPY collecting offsets
///
/// The pointer that is reached by back-tracking is returned, and any accumulated offset is passed back.
/// \param vn is the pointer Varnode
/// \param offset passes back the accumulated offset
/// \return the reached Varnode pointer
Varnode *rootPointer(Varnode *vn,uintb &offset)
{
offset = 0;
for(;;) {
if (!vn->isWritten()) break;
PcodeOp *op = vn->getDef();
OpCode opc = op->code();
if (opc == CPUI_PTRSUB) {
offset += op->getIn(1)->getOffset();
vn = op->getIn(0);
}
else if (opc == CPUI_INT_ADD) {
Varnode *cvn = op->getIn(1);
if (!cvn->isConstant()) break;
offset += cvn->getOffset();
vn = op->getIn(0);
}
else if (opc == CPUI_COPY) {
vn = op->getIn(0);
}
else
break;
}
return vn;
}
/// \brief Determine if two pointer Varnodes always hold the same value
///
/// \param vn1 is the first pointer to compare
/// \param vn2 is the second pointer to compare
bool pointerEquality(Varnode *vn1,Varnode *vn2)
{
uintb off1,off2;
if (vn1 == vn2) return true;
vn1 = rootPointer(vn1,off1);
vn2 = rootPointer(vn2,off2);
if (off1 != off2) return false;
return (vn1 == vn2);
}
} // End namespace ghidra